mirror of
https://github.com/jumpserver/jumpserver.git
synced 2025-07-06 19:38:54 +00:00
[Update] Merge branch dev_cloud_manager (baijiangjie_local) to dev
This commit is contained in:
commit
21c71aba93
@ -2,4 +2,4 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
|
|
||||||
__version__ = "1.4.1"
|
__version__ = "1.4.3"
|
||||||
|
@ -4,3 +4,4 @@ from .label import *
|
|||||||
from .system_user import *
|
from .system_user import *
|
||||||
from .node import *
|
from .node import *
|
||||||
from .domain import *
|
from .domain import *
|
||||||
|
from .cmd_filter import *
|
||||||
|
32
apps/assets/api/cmd_filter.py
Normal file
32
apps/assets/api/cmd_filter.py
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
|
||||||
|
from rest_framework_bulk import BulkModelViewSet
|
||||||
|
from django.shortcuts import get_object_or_404
|
||||||
|
|
||||||
|
from ..hands import IsOrgAdmin
|
||||||
|
from ..models import CommandFilter, CommandFilterRule
|
||||||
|
from .. import serializers
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = ['CommandFilterViewSet', 'CommandFilterRuleViewSet']
|
||||||
|
|
||||||
|
|
||||||
|
class CommandFilterViewSet(BulkModelViewSet):
|
||||||
|
permission_classes = (IsOrgAdmin,)
|
||||||
|
queryset = CommandFilter.objects.all()
|
||||||
|
serializer_class = serializers.CommandFilterSerializer
|
||||||
|
|
||||||
|
|
||||||
|
class CommandFilterRuleViewSet(BulkModelViewSet):
|
||||||
|
permission_classes = (IsOrgAdmin,)
|
||||||
|
serializer_class = serializers.CommandFilterRuleSerializer
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
fpk = self.kwargs.get('filter_pk')
|
||||||
|
if not fpk:
|
||||||
|
return CommandFilterRule.objects.none()
|
||||||
|
group = get_object_or_404(CommandFilter, pk=fpk)
|
||||||
|
return group.rules.all().order_by('priority')
|
||||||
|
|
||||||
|
|
@ -33,7 +33,8 @@ __all__ = [
|
|||||||
'SystemUserViewSet', 'SystemUserAuthInfoApi',
|
'SystemUserViewSet', 'SystemUserAuthInfoApi',
|
||||||
'SystemUserPushApi', 'SystemUserTestConnectiveApi',
|
'SystemUserPushApi', 'SystemUserTestConnectiveApi',
|
||||||
'SystemUserAssetsListView', 'SystemUserPushToAssetApi',
|
'SystemUserAssetsListView', 'SystemUserPushToAssetApi',
|
||||||
'SystemUserTestAssetConnectabilityApi',
|
'SystemUserTestAssetConnectabilityApi', 'SystemUserCommandFilterRuleListApi',
|
||||||
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
@ -127,3 +128,16 @@ class SystemUserTestAssetConnectabilityApi(generics.RetrieveAPIView):
|
|||||||
asset = get_object_or_404(Asset, id=asset_id)
|
asset = get_object_or_404(Asset, id=asset_id)
|
||||||
task = test_system_user_connectability_a_asset.delay(system_user, asset)
|
task = test_system_user_connectability_a_asset.delay(system_user, asset)
|
||||||
return Response({"task": task.id})
|
return Response({"task": task.id})
|
||||||
|
|
||||||
|
|
||||||
|
class SystemUserCommandFilterRuleListApi(generics.ListAPIView):
|
||||||
|
permission_classes = (IsOrgAdminOrAppUser,)
|
||||||
|
|
||||||
|
def get_serializer_class(self):
|
||||||
|
from ..serializers import CommandFilterRuleSerializer
|
||||||
|
return CommandFilterRuleSerializer
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
pk = self.kwargs.get('pk', None)
|
||||||
|
system_user = get_object_or_404(SystemUser, pk=pk)
|
||||||
|
return system_user.cmd_filter_rules
|
||||||
|
@ -4,3 +4,4 @@ from .asset import *
|
|||||||
from .label import *
|
from .label import *
|
||||||
from .user import *
|
from .user import *
|
||||||
from .domain import *
|
from .domain import *
|
||||||
|
from .cmd_filter import *
|
||||||
|
@ -48,7 +48,7 @@ class AssetCreateForm(OrgModelForm):
|
|||||||
'root or other NOPASSWD sudo privilege user existed in asset,'
|
'root or other NOPASSWD sudo privilege user existed in asset,'
|
||||||
'If asset is windows or other set any one, more see admin user left menu'
|
'If asset is windows or other set any one, more see admin user left menu'
|
||||||
),
|
),
|
||||||
# 'platform': _("* required Must set exact system platform, Windows, Linux ..."),
|
'platform': _("Windows 2016 RDP protocol is different, If is window 2016, set it"),
|
||||||
'domain': _("If your have some network not connect with each other, you can set domain")
|
'domain': _("If your have some network not connect with each other, you can set domain")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,7 +88,7 @@ class AssetUpdateForm(OrgModelForm):
|
|||||||
'root or other NOPASSWD sudo privilege user existed in asset,'
|
'root or other NOPASSWD sudo privilege user existed in asset,'
|
||||||
'If asset is windows or other set any one, more see admin user left menu'
|
'If asset is windows or other set any one, more see admin user left menu'
|
||||||
),
|
),
|
||||||
# 'platform': _("* required Must set exact system platform, Windows, Linux ..."),
|
'platform': _("Windows 2016 RDP protocol is different, If is window 2016, set it"),
|
||||||
'domain': _("If your have some network not connect with each other, you can set domain")
|
'domain': _("If your have some network not connect with each other, you can set domain")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
27
apps/assets/forms/cmd_filter.py
Normal file
27
apps/assets/forms/cmd_filter.py
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
from django import forms
|
||||||
|
|
||||||
|
from orgs.mixins import OrgModelForm
|
||||||
|
from ..models import CommandFilter, CommandFilterRule
|
||||||
|
|
||||||
|
__all__ = ['CommandFilterForm', 'CommandFilterRuleForm']
|
||||||
|
|
||||||
|
|
||||||
|
class CommandFilterForm(OrgModelForm):
|
||||||
|
class Meta:
|
||||||
|
model = CommandFilter
|
||||||
|
fields = ['name', 'comment']
|
||||||
|
|
||||||
|
|
||||||
|
class CommandFilterRuleForm(OrgModelForm):
|
||||||
|
class Meta:
|
||||||
|
model = CommandFilterRule
|
||||||
|
fields = [
|
||||||
|
'filter', 'type', 'content', 'priority', 'action', 'comment'
|
||||||
|
]
|
||||||
|
widgets = {
|
||||||
|
'content': forms.Textarea(attrs={
|
||||||
|
'placeholder': 'eg:\r\nreboot\r\nrm -rf'
|
||||||
|
}),
|
||||||
|
}
|
@ -3,8 +3,9 @@
|
|||||||
from django import forms
|
from django import forms
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from ..models import AdminUser, SystemUser
|
|
||||||
from common.utils import validate_ssh_private_key, ssh_pubkey_gen, get_logger
|
from common.utils import validate_ssh_private_key, ssh_pubkey_gen, get_logger
|
||||||
|
from orgs.mixins import OrgModelForm
|
||||||
|
from ..models import AdminUser, SystemUser
|
||||||
|
|
||||||
logger = get_logger(__file__)
|
logger = get_logger(__file__)
|
||||||
__all__ = [
|
__all__ = [
|
||||||
@ -85,7 +86,7 @@ class AdminUserForm(PasswordAndKeyAuthForm):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class SystemUserForm(PasswordAndKeyAuthForm):
|
class SystemUserForm(OrgModelForm, PasswordAndKeyAuthForm):
|
||||||
# Admin user assets define, let user select, save it in form not in view
|
# Admin user assets define, let user select, save it in form not in view
|
||||||
auto_generate_key = forms.BooleanField(initial=True, required=False)
|
auto_generate_key = forms.BooleanField(initial=True, required=False)
|
||||||
|
|
||||||
@ -136,11 +137,14 @@ class SystemUserForm(PasswordAndKeyAuthForm):
|
|||||||
fields = [
|
fields = [
|
||||||
'name', 'username', 'protocol', 'auto_generate_key',
|
'name', 'username', 'protocol', 'auto_generate_key',
|
||||||
'password', 'private_key_file', 'auto_push', 'sudo',
|
'password', 'private_key_file', 'auto_push', 'sudo',
|
||||||
'comment', 'shell', 'priority', 'login_mode',
|
'comment', 'shell', 'priority', 'login_mode', 'cmd_filters',
|
||||||
]
|
]
|
||||||
widgets = {
|
widgets = {
|
||||||
'name': forms.TextInput(attrs={'placeholder': _('Name')}),
|
'name': forms.TextInput(attrs={'placeholder': _('Name')}),
|
||||||
'username': forms.TextInput(attrs={'placeholder': _('Username')}),
|
'username': forms.TextInput(attrs={'placeholder': _('Username')}),
|
||||||
|
'cmd_filters': forms.SelectMultiple(attrs={
|
||||||
|
'class': 'select2', 'data-placeholder': _('Command filter')
|
||||||
|
}),
|
||||||
}
|
}
|
||||||
help_texts = {
|
help_texts = {
|
||||||
'name': '* required',
|
'name': '* required',
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
from .user import AdminUser, SystemUser
|
from .user import *
|
||||||
from .label import Label
|
from .label import Label
|
||||||
from .cluster import *
|
from .cluster import *
|
||||||
from .group import *
|
from .group import *
|
||||||
from .domain import *
|
from .domain import *
|
||||||
from .node import *
|
from .node import *
|
||||||
from .asset import *
|
from .asset import *
|
||||||
|
from .cmd_filter import *
|
||||||
from .utils import *
|
from .utils import *
|
||||||
|
@ -15,7 +15,7 @@ from django.core.cache import cache
|
|||||||
|
|
||||||
from ..const import ASSET_ADMIN_CONN_CACHE_KEY
|
from ..const import ASSET_ADMIN_CONN_CACHE_KEY
|
||||||
from .user import AdminUser, SystemUser
|
from .user import AdminUser, SystemUser
|
||||||
from orgs.mixins import OrgModelMixin,OrgManager
|
from orgs.mixins import OrgModelMixin, OrgManager
|
||||||
|
|
||||||
__all__ = ['Asset']
|
__all__ = ['Asset']
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
60
apps/assets/models/cmd_filter.py
Normal file
60
apps/assets/models/cmd_filter.py
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
from django.db import models
|
||||||
|
from django.core.validators import MinValueValidator, MaxValueValidator
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
from orgs.mixins import OrgModelMixin
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
'CommandFilter', 'CommandFilterRule'
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class CommandFilter(OrgModelMixin):
|
||||||
|
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
|
||||||
|
name = models.CharField(max_length=64, verbose_name=_("Name"))
|
||||||
|
is_active = models.BooleanField(default=True, verbose_name=_('Is active'))
|
||||||
|
comment = models.TextField(blank=True, default='', verbose_name=_("Comment"))
|
||||||
|
date_created = models.DateTimeField(auto_now_add=True)
|
||||||
|
date_updated = models.DateTimeField(auto_now=True)
|
||||||
|
created_by = models.CharField(max_length=128, blank=True, default='', verbose_name=_('Created by'))
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
|
||||||
|
class CommandFilterRule(OrgModelMixin):
|
||||||
|
TYPE_REGEX = 'regex'
|
||||||
|
TYPE_COMMAND = 'command'
|
||||||
|
TYPE_CHOICES = (
|
||||||
|
(TYPE_REGEX, _('Regex')),
|
||||||
|
(TYPE_COMMAND, _('Command')),
|
||||||
|
)
|
||||||
|
|
||||||
|
ACTION_DENY, ACTION_ALLOW = range(2)
|
||||||
|
ACTION_CHOICES = (
|
||||||
|
(ACTION_DENY, _('Deny')),
|
||||||
|
(ACTION_ALLOW, _('Allow')),
|
||||||
|
)
|
||||||
|
|
||||||
|
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
|
||||||
|
filter = models.ForeignKey('CommandFilter', on_delete=models.CASCADE, verbose_name=_("Filter"), related_name='rules')
|
||||||
|
type = models.CharField(max_length=16, default=TYPE_COMMAND, choices=TYPE_CHOICES, verbose_name=_("Type"))
|
||||||
|
priority = models.IntegerField(default=50, verbose_name=_("Priority"), help_text=_("1-100, the lower will be match first"),
|
||||||
|
validators=[MinValueValidator(1), MaxValueValidator(100)])
|
||||||
|
content = models.TextField(max_length=1024, verbose_name=_("Content"), help_text=_("One line one command"))
|
||||||
|
action = models.IntegerField(default=ACTION_DENY, choices=ACTION_CHOICES, verbose_name=_("Action"))
|
||||||
|
comment = models.CharField(max_length=64, blank=True, default='', verbose_name=_("Comment"))
|
||||||
|
date_created = models.DateTimeField(auto_now_add=True)
|
||||||
|
date_updated = models.DateTimeField(auto_now=True)
|
||||||
|
created_by = models.CharField(max_length=128, blank=True, default='', verbose_name=_('Created by'))
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
ordering = ('priority', 'action')
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return '{} % {}'.format(self.type, self.content)
|
@ -40,12 +40,10 @@ class Node(OrgModelMixin):
|
|||||||
return True
|
return True
|
||||||
self_key = [int(k) for k in self.key.split(':')]
|
self_key = [int(k) for k in self.key.split(':')]
|
||||||
other_key = [int(k) for k in other.key.split(':')]
|
other_key = [int(k) for k in other.key.split(':')]
|
||||||
if len(self_key) < len(other_key):
|
return self_key.__lt__(other_key)
|
||||||
return True
|
|
||||||
elif len(self_key) > len(other_key):
|
def __lt__(self, other):
|
||||||
return False
|
return not self.__gt__(other)
|
||||||
else:
|
|
||||||
return self_key[-1] < other_key[-1]
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import uuid
|
|
||||||
|
|
||||||
from django.core.cache import cache
|
from django.core.cache import cache
|
||||||
from django.db import models
|
from django.db import models
|
||||||
@ -118,6 +117,7 @@ class SystemUser(AssetUser):
|
|||||||
sudo = models.TextField(default='/bin/whoami', verbose_name=_('Sudo'))
|
sudo = models.TextField(default='/bin/whoami', verbose_name=_('Sudo'))
|
||||||
shell = models.CharField(max_length=64, default='/bin/bash', verbose_name=_('Shell'))
|
shell = models.CharField(max_length=64, default='/bin/bash', verbose_name=_('Shell'))
|
||||||
login_mode = models.CharField(choices=LOGIN_MODE_CHOICES, default=AUTO_LOGIN, max_length=10, verbose_name=_('Login mode'))
|
login_mode = models.CharField(choices=LOGIN_MODE_CHOICES, default=AUTO_LOGIN, max_length=10, verbose_name=_('Login mode'))
|
||||||
|
cmd_filters = models.ManyToManyField('CommandFilter', related_name='system_users', verbose_name=_("Command filter"), blank=True)
|
||||||
|
|
||||||
cache_key = "__SYSTEM_USER_CACHED_{}"
|
cache_key = "__SYSTEM_USER_CACHED_{}"
|
||||||
|
|
||||||
@ -163,6 +163,14 @@ class SystemUser(AssetUser):
|
|||||||
def expire_cache(self):
|
def expire_cache(self):
|
||||||
cache.delete(self.cache_key.format(self.id))
|
cache.delete(self.cache_key.format(self.id))
|
||||||
|
|
||||||
|
@property
|
||||||
|
def cmd_filter_rules(self):
|
||||||
|
from .cmd_filter import CommandFilterRule
|
||||||
|
rules = CommandFilterRule.objects.filter(
|
||||||
|
filter__in=self.cmd_filters.all()
|
||||||
|
).order_by('priority').distinct()
|
||||||
|
return rules
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_system_user_by_id_or_cached(cls, sid):
|
def get_system_user_by_id_or_cached(cls, sid):
|
||||||
cached = cache.get(cls.cache_key.format(sid))
|
cached = cache.get(cls.cache_key.format(sid))
|
||||||
|
@ -7,3 +7,4 @@ from .label import *
|
|||||||
from .system_user import *
|
from .system_user import *
|
||||||
from .node import *
|
from .node import *
|
||||||
from .domain import *
|
from .domain import *
|
||||||
|
from .cmd_filter import *
|
||||||
|
23
apps/assets/serializers/cmd_filter.py
Normal file
23
apps/assets/serializers/cmd_filter.py
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
from rest_framework import serializers
|
||||||
|
|
||||||
|
from common.fields import ChoiceDisplayField
|
||||||
|
from ..models import CommandFilter, CommandFilterRule, SystemUser
|
||||||
|
|
||||||
|
|
||||||
|
class CommandFilterSerializer(serializers.ModelSerializer):
|
||||||
|
rules = serializers.PrimaryKeyRelatedField(queryset=CommandFilterRule.objects.all(), many=True)
|
||||||
|
system_users = serializers.PrimaryKeyRelatedField(queryset=SystemUser.objects.all(), many=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = CommandFilter
|
||||||
|
fields = '__all__'
|
||||||
|
|
||||||
|
|
||||||
|
class CommandFilterRuleSerializer(serializers.ModelSerializer):
|
||||||
|
serializer_choice_field = ChoiceDisplayField
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = CommandFilterRule
|
||||||
|
fields = '__all__'
|
@ -62,6 +62,10 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
<div id="command-filter-block">
|
||||||
|
<h3>{% trans 'Command filter' %}</h3>
|
||||||
|
{% bootstrap_field form.cmd_filters layout="horizontal" %}
|
||||||
|
</div>
|
||||||
<h3>{% trans 'Other' %}</h3>
|
<h3>{% trans 'Other' %}</h3>
|
||||||
{% bootstrap_field form.sudo layout="horizontal" %}
|
{% bootstrap_field form.sudo layout="horizontal" %}
|
||||||
{% bootstrap_field form.shell layout="horizontal" %}
|
{% bootstrap_field form.shell layout="horizontal" %}
|
||||||
@ -101,6 +105,7 @@ var need_change_field_login_mode = [
|
|||||||
function protocolChange() {
|
function protocolChange() {
|
||||||
if ($(protocol_id + " option:selected").text() === 'rdp') {
|
if ($(protocol_id + " option:selected").text() === 'rdp') {
|
||||||
$('.auth-fields').removeClass('hidden');
|
$('.auth-fields').removeClass('hidden');
|
||||||
|
$('#command-filter-block').addClass('hidden');
|
||||||
$.each(need_change_field, function (index, value) {
|
$.each(need_change_field, function (index, value) {
|
||||||
$(value).closest('.form-group').addClass('hidden')
|
$(value).closest('.form-group').addClass('hidden')
|
||||||
});
|
});
|
||||||
|
@ -90,7 +90,7 @@
|
|||||||
<td colspan="2" class="no-borders">
|
<td colspan="2" class="no-borders">
|
||||||
<select data-placeholder="{% trans 'Select nodes' %}" id="nodes_selected" class="select2" style="width: 100%" multiple="" tabindex="4">
|
<select data-placeholder="{% trans 'Select nodes' %}" id="nodes_selected" class="select2" style="width: 100%" multiple="" tabindex="4">
|
||||||
{% for node in nodes %}
|
{% for node in nodes %}
|
||||||
<option value="{{ node.id }}" id="opt_{{ node.id }}" >{{ node.value }}</option>
|
<option value="{{ node.id }}" id="opt_{{ node.id }}" >{{ node }}</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
</td>
|
</td>
|
||||||
|
20
apps/assets/templates/assets/cmd_filter_create_update.html
Normal file
20
apps/assets/templates/assets/cmd_filter_create_update.html
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
{% extends '_base_create_update.html' %}
|
||||||
|
{% load static %}
|
||||||
|
{% load bootstrap3 %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block form %}
|
||||||
|
<form id="groupForm" method="post" class="form-horizontal">
|
||||||
|
{% csrf_token %}
|
||||||
|
{% bootstrap_field form.name layout="horizontal" %}
|
||||||
|
{% bootstrap_field form.comment 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 %}
|
168
apps/assets/templates/assets/cmd_filter_detail.html
Normal file
168
apps/assets/templates/assets/cmd_filter_detail.html
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
{% extends 'base.html' %}
|
||||||
|
{% load static %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block custom_head_css_js %}
|
||||||
|
<link href='{% static "css/plugins/select2/select2.min.css" %}' rel="stylesheet">
|
||||||
|
<script src='{% static "js/plugins/select2/select2.full.min.js" %}'></script>
|
||||||
|
{% 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="panel-options">
|
||||||
|
<ul class="nav nav-tabs">
|
||||||
|
<li class="active">
|
||||||
|
<a href="{% url 'assets:cmd-filter-detail' pk=object.id %}" class="text-center">
|
||||||
|
<i class="fa fa-laptop"></i> {% trans 'Detail' %}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<li>
|
||||||
|
<a href="{% url 'assets:cmd-filter-rule-list' pk=object.id %}" class="text-center">
|
||||||
|
<i class="fa fa-laptop"></i> {% trans 'Rules' %}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="pull-right">
|
||||||
|
<a class="btn btn-outline btn-default" href="{% url 'assets:cmd-filter-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-del">
|
||||||
|
<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 'Comment' %}:</td>
|
||||||
|
<td><b>{{ object.comment }}</b></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{% trans 'Date created' %}:</td>
|
||||||
|
<td><b>{{ object.date_created }}</b></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{% trans 'Date updated' %}:</td>
|
||||||
|
<td><b>{{ object.date_updated }}</b></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{% trans 'Created by' %}:</td>
|
||||||
|
<td><b>{{ object.created_by }}</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 'System users' %}
|
||||||
|
</div>
|
||||||
|
<div class="panel-body">
|
||||||
|
<table class="table group_edit" id="table-clusters">
|
||||||
|
<tbody>
|
||||||
|
<form>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" class="no-borders">
|
||||||
|
<select data-placeholder="{% trans 'Binding to system user' %}" id="system_users_selected" class="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>
|
||||||
|
<td colspan="2" class="no-borders">
|
||||||
|
<button type="button" class="btn btn-primary btn-sm" id="btn-binding-system-users">{% trans 'Confirm' %}</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</form>
|
||||||
|
{% for system_user in object.system_users.all %}
|
||||||
|
<tr>
|
||||||
|
<td><b class="bdg-system-users" data-gid={{ system_user.id }}>{{ system_user }}</b></td>
|
||||||
|
<td>
|
||||||
|
<button class="btn btn-danger pull-right btn-xs btn-unbound-system-user" data-gid={{ system_user.id }} type="button"><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>
|
||||||
|
function updateCMDFilterSystemUsers(system_users) {
|
||||||
|
var the_url = "{% url 'api-assets:cmd-filter-detail' pk=object.id %}";
|
||||||
|
var body = {
|
||||||
|
system_users: Object.assign([], system_users)
|
||||||
|
};
|
||||||
|
var success = function(data) {
|
||||||
|
location.reload();
|
||||||
|
};
|
||||||
|
APIUpdateAttr({
|
||||||
|
url: the_url,
|
||||||
|
body: JSON.stringify(body),
|
||||||
|
method: 'PATCH',
|
||||||
|
success: success
|
||||||
|
});
|
||||||
|
}
|
||||||
|
$(document).ready(function () {
|
||||||
|
$(".select2").select2();
|
||||||
|
}).on('click', '#btn-binding-system-users', function () {
|
||||||
|
var origin_system_users = $.map($(".bdg-system-users"), function (s) {
|
||||||
|
return $(s).data('gid')
|
||||||
|
});
|
||||||
|
var new_selected_system_users_id = $.map($("#system_users_selected").select2('data'), function (s) {
|
||||||
|
return s.id;
|
||||||
|
});
|
||||||
|
var system_users = origin_system_users.concat(new_selected_system_users_id);
|
||||||
|
updateCMDFilterSystemUsers(system_users)
|
||||||
|
}).on('click', '.btn-unbound-system-user', function () {
|
||||||
|
var unbound_system_user = $(this).data('gid');
|
||||||
|
var origin_system_users = $.map($(".bdg-system-users"), function (s) {
|
||||||
|
return $(s).data('gid')
|
||||||
|
});
|
||||||
|
var system_users = $.grep(origin_system_users, function (n, i) {
|
||||||
|
return n !== unbound_system_user
|
||||||
|
});
|
||||||
|
updateCMDFilterSystemUsers(system_users)
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
89
apps/assets/templates/assets/cmd_filter_list.html
Normal file
89
apps/assets/templates/assets/cmd_filter_list.html
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
{% extends '_base_list.html' %}
|
||||||
|
{% load i18n static %}
|
||||||
|
{% block table_search %}{% endblock %}
|
||||||
|
{% block help_message %}
|
||||||
|
<div class="alert alert-info help-message">
|
||||||
|
{% trans 'System user bound some command filter, each command filter has some rules,'%}
|
||||||
|
{% trans 'When user login asset with this system user, then run a command,' %}
|
||||||
|
{% trans 'The command will be filter by rules, higher priority(lower number) rule run first,' %}
|
||||||
|
{% trans 'When a rule matched, if rule action is allow, then allow command execute,' %}
|
||||||
|
{% trans 'else if action is deny, then command with be deny,' %}
|
||||||
|
{% trans 'else match next rule, if none matched, allowed' %}
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
{% block table_container %}
|
||||||
|
<div class="uc pull-left m-r-5">
|
||||||
|
<a href="{% url 'assets:cmd-filter-create' %}" class="btn btn-sm btn-primary"> {% trans "Create command filter" %} </a>
|
||||||
|
</div>
|
||||||
|
<table class="table table-striped table-bordered table-hover " id="cmd_filter_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 'Rules' %}</th>
|
||||||
|
<th class="text-center">{% trans 'System users' %}</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: $('#cmd_filter_list_table'),
|
||||||
|
columnDefs: [
|
||||||
|
{targets: 1, createdCell: function (td, cellData, rowData) {
|
||||||
|
var detail_btn = '<a href="{% url 'assets:cmd-filter-detail' pk=DEFAULT_PK %}">' + cellData + '</a>';
|
||||||
|
$(td).html(detail_btn.replace('{{ DEFAULT_PK }}', rowData.id));
|
||||||
|
}},
|
||||||
|
{targets: 2, createdCell: function (td, cellData, rowData) {
|
||||||
|
var filters_list_btn = '<a href="{% url "assets:cmd-filter-rule-list" pk=DEFAULT_PK %}">' + cellData.length + '</a>';
|
||||||
|
filters_list_btn = filters_list_btn.replace("{{ DEFAULT_PK }}", rowData.id);
|
||||||
|
$(td).html(filters_list_btn);
|
||||||
|
}},
|
||||||
|
{targets: 3, createdCell: function (td, cellData, rowData) {
|
||||||
|
var system_users_list_btn = '<a href="{% url "assets:cmd-filter-detail" pk=DEFAULT_PK %}">' + cellData.length + '</a>';
|
||||||
|
system_users_list_btn = system_users_list_btn.replace("{{ DEFAULT_PK }}", rowData.id);
|
||||||
|
$(td).html(system_users_list_btn);
|
||||||
|
}},
|
||||||
|
{targets: 5, createdCell: function (td, cellData, rowData) {
|
||||||
|
var update_btn = '<a href="{% url "assets:cmd-filter-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-uid="{{ DEFAULT_PK }}">{% trans "Delete" %}</a>'.replace('{{ DEFAULT_PK }}', cellData);
|
||||||
|
$(td).html(update_btn + del_btn)
|
||||||
|
}}
|
||||||
|
],
|
||||||
|
ajax_url: '{% url "api-assets:cmd-filter-list" %}',
|
||||||
|
columns: [
|
||||||
|
{data: "id"}, {data: "name" }, {data: "rules" },
|
||||||
|
{data: "system_users" }, {data: "comment"}, {data: "id"}
|
||||||
|
],
|
||||||
|
op_html: $('#actions').html()
|
||||||
|
};
|
||||||
|
jumpserver.initDataTable(options);
|
||||||
|
}
|
||||||
|
$(document).ready(function(){
|
||||||
|
initTable();
|
||||||
|
})
|
||||||
|
.on('click', '.btn-delete', function () {
|
||||||
|
var $this = $(this);
|
||||||
|
var $data_table = $('#cmd_filter_list_table').DataTable();
|
||||||
|
var name = $(this).closest("tr").find(":nth-child(2)").children('a').html();
|
||||||
|
var uid = $this.data('uid');
|
||||||
|
var the_url = '{% url "api-assets:cmd-filter-detail" pk=DEFAULT_PK %}'.replace('{{ DEFAULT_PK }}', uid);
|
||||||
|
objectDelete($this, name, the_url);
|
||||||
|
setTimeout( function () {
|
||||||
|
$data_table.ajax.reload();
|
||||||
|
}, 3000);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,74 @@
|
|||||||
|
{% extends 'base.html' %}
|
||||||
|
{% load i18n %}
|
||||||
|
{% load static %}
|
||||||
|
{% load bootstrap3 %}
|
||||||
|
{% block custom_head_css_js %}
|
||||||
|
<link href="{% static 'css/plugins/select2/select2.min.css' %}" rel="stylesheet">
|
||||||
|
<script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script>
|
||||||
|
{% 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 enctype="multipart/form-data" method="post" class="form-horizontal" action="" >
|
||||||
|
{% csrf_token %}
|
||||||
|
{% if form.non_field_errors %}
|
||||||
|
<div class="alert alert-danger">
|
||||||
|
{{ form.non_field_errors }}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% bootstrap_form form 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>
|
||||||
|
var content_origin_placeholder = '';
|
||||||
|
var content_origin_help_text = '';
|
||||||
|
var content_ref = '';
|
||||||
|
var content_help_ref = '';
|
||||||
|
|
||||||
|
$(document).ready(function(){
|
||||||
|
content_ref = $('#id_content');
|
||||||
|
content_help_ref = content_ref.next();
|
||||||
|
content_origin_placeholder = content_ref.attr('placeholder');
|
||||||
|
content_origin_help_text = content_help_ref.html();
|
||||||
|
}).on('change', '#id_type', function () {
|
||||||
|
if ($('#id_type :selected').val() === 'regex') {
|
||||||
|
content_ref.attr('placeholder', 'rm.*|reboot|shutdown');
|
||||||
|
content_help_ref.html("");
|
||||||
|
} else {
|
||||||
|
content_ref.attr('placeholder', content_origin_placeholder);
|
||||||
|
content_help_ref.html(content_origin_help_text);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
115
apps/assets/templates/assets/cmd_filter_rule_list.html
Normal file
115
apps/assets/templates/assets/cmd_filter_rule_list.html
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
{% extends 'base.html' %}
|
||||||
|
{% load static %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block custom_head_css_js %}
|
||||||
|
<link href='{% static "css/plugins/select2/select2.min.css" %}' rel="stylesheet">
|
||||||
|
<script src='{% static "js/plugins/select2/select2.full.min.js" %}'></script>
|
||||||
|
{% 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="panel-options">
|
||||||
|
<ul class="nav nav-tabs">
|
||||||
|
<li>
|
||||||
|
<a href="{% url 'assets:cmd-filter-detail' pk=object.id %}" class="text-center">
|
||||||
|
<i class="fa fa-laptop"></i> {% trans 'Detail' %}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="active">
|
||||||
|
<a href="{% url 'assets:cmd-filter-rule-list' pk=object.id %}" class="text-center"><i class="fa fa-laptop"></i> {% trans 'Rules' %} </a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="tab-content">
|
||||||
|
<div class="col-sm-12" style="padding-left: 0;">
|
||||||
|
<div class="" id="content_start">
|
||||||
|
</div>
|
||||||
|
<div class="ibox float-e-margins">
|
||||||
|
<div class="ibox-title">
|
||||||
|
<span style="float: left"><b>{% trans 'Command filter rule list' %}</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">
|
||||||
|
<div class="uc pull-left m-r-5">
|
||||||
|
<a href="{% url 'assets:cmd-filter-rule-create' filter_pk=object.id %}" class="btn btn-sm btn-primary"> {% trans "Create rule" %} </a>
|
||||||
|
</div>
|
||||||
|
<table class="table table-striped table-bordered table-hover " id="cmd_filter_rule_list_table" >
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="text-center">
|
||||||
|
<input type="checkbox" id="check_all" class="ipt_check_all" >
|
||||||
|
</th>
|
||||||
|
<th class="text-center">{% trans 'Type' %}</th>
|
||||||
|
<th class="text-center">{% trans 'Content' %}</th>
|
||||||
|
<th class="text-center">{% trans 'Priority' %}</th>
|
||||||
|
<th class="text-center">{% trans 'Strategy' %}</th>
|
||||||
|
<th class="text-center">{% trans 'Comment' %}</th>
|
||||||
|
<th class="text-center">{% trans 'Action' %}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
{% block content_bottom_left %}{% endblock %}
|
||||||
|
{% block custom_foot_js %}
|
||||||
|
<script>
|
||||||
|
function initTable() {
|
||||||
|
var options = {
|
||||||
|
ele: $('#cmd_filter_rule_list_table'),
|
||||||
|
columnDefs: [
|
||||||
|
{targets: 6, createdCell: function (td, cellData, rowData) {
|
||||||
|
var update_btn = '<a href="{% url "assets:cmd-filter-rule-update" filter_pk=object.id 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-uid="{{ DEFAULT_PK }}">{% trans "Delete" %}</a>'.replace('{{ DEFAULT_PK }}', cellData);
|
||||||
|
$(td).html(update_btn + del_btn)
|
||||||
|
}}
|
||||||
|
],
|
||||||
|
ajax_url: '{% url "api-assets:cmd-filter-rule-list" filter_pk=object.id %}',
|
||||||
|
columns: [
|
||||||
|
{data: "id"}, {data: "type.display" }, {data: 'content'}, {data: 'priority'},
|
||||||
|
{data: 'action.display'}, {data: "comment" }, {data: "id"}
|
||||||
|
],
|
||||||
|
op_html: $('#actions').html()
|
||||||
|
};
|
||||||
|
jumpserver.initDataTable(options);
|
||||||
|
}
|
||||||
|
$(document).ready(function(){
|
||||||
|
initTable();
|
||||||
|
})
|
||||||
|
.on('click', '.btn-delete', function () {
|
||||||
|
var $this = $(this);
|
||||||
|
var $data_table = $('#cmd_filter_rule_list_table').DataTable();
|
||||||
|
var name = $(this).closest("tr").find(":nth-child(2)").children('a').html();
|
||||||
|
var uid = $this.data('uid');
|
||||||
|
var the_url = '{% url "api-assets:cmd-filter-rule-detail" filter_pk=object.id pk=DEFAULT_PK %}'.replace('{{ DEFAULT_PK }}', uid);
|
||||||
|
objectDelete($this, name, the_url);
|
||||||
|
setTimeout( function () {
|
||||||
|
$data_table.ajax.reload();
|
||||||
|
}, 3000);
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
@ -1,4 +1,5 @@
|
|||||||
{% extends 'base.html' %}
|
{% extends 'base.html' %}
|
||||||
|
{% load common_tags %}
|
||||||
{% load static %}
|
{% load static %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
@ -113,7 +114,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
{% for node in system_user.nodes.all %}
|
{% for node in system_user.nodes.all|sort %}
|
||||||
<tr>
|
<tr>
|
||||||
<td ><b class="bdg_node" data-gid={{ node.id }}>{{ node }}</b></td>
|
<td ><b class="bdg_node" data-gid={{ node.id }}>{{ node }}</b></td>
|
||||||
<td>
|
<td>
|
||||||
|
@ -156,44 +156,47 @@
|
|||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{# <div class="panel panel-info">#}
|
|
||||||
{# <div class="panel-heading">#}
|
|
||||||
{# <i class="fa fa-info-circle"></i> {% trans 'Nodes' %}#}
|
|
||||||
{# </div>#}
|
|
||||||
{# <div class="panel-body">#}
|
|
||||||
{# <table class="table node_edit" id="add-asset2group">#}
|
|
||||||
{# <tbody>#}
|
|
||||||
{# <form>#}
|
|
||||||
{# <tr>#}
|
|
||||||
{# <td colspan="2" class="no-borders">#}
|
|
||||||
{# <select data-placeholder="{% trans 'Add to node' %}" id="node_selected" class="select2" style="width: 100%" multiple="" tabindex="4">#}
|
|
||||||
{# {% for node in nodes_remain %}#}
|
|
||||||
{# <option value="{{ node.id }}" id="opt_{{ node.id }}" >{{ node }}</option>#}
|
|
||||||
{# {% endfor %}#}
|
|
||||||
{# </select>#}
|
|
||||||
{# </td>#}
|
|
||||||
{# </tr>#}
|
|
||||||
{# <tr>#}
|
|
||||||
{# <td colspan="2" class="no-borders">#}
|
|
||||||
{# <button type="button" class="btn btn-info btn-sm" id="btn-add-to-node">{% trans 'Confirm' %}</button>#}
|
|
||||||
{# </td>#}
|
|
||||||
{# </tr>#}
|
|
||||||
{# </form>#}
|
|
||||||
{##}
|
|
||||||
{# {% for node in system_user.nodes.all %}#}
|
|
||||||
{# <tr>#}
|
|
||||||
{# <td ><b class="bdg_node" data-gid={{ node.id }}>{{ node }}</b></td>#}
|
|
||||||
{# <td>#}
|
|
||||||
{# <button class="btn btn-danger pull-right btn-xs btn-remove-from-node" type="button"><i class="fa fa-minus"></i></button>#}
|
|
||||||
{# </td>#}
|
|
||||||
{# </tr>#}
|
|
||||||
{# {% endfor %}#}
|
|
||||||
{# </tbody>#}
|
|
||||||
{# </table>#}
|
|
||||||
{# </div>#}
|
|
||||||
{# </div>#}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{% if system_user.protocol != 'rdp' %}
|
||||||
|
<div class="col-sm-4" style="padding-left: 0; padding-right: 0">
|
||||||
|
<div class="panel panel-info">
|
||||||
|
<div class="panel-heading">
|
||||||
|
<i class="fa fa-info-circle"></i> {% trans 'Command filter' %}
|
||||||
|
</div>
|
||||||
|
<div class="panel-body">
|
||||||
|
<table class="table">
|
||||||
|
<tbody>
|
||||||
|
<form>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" class="no-borders">
|
||||||
|
<select data-placeholder="{% trans 'Binding command filters' %}" id="command_filters_selected" class="select2" style="width: 100%" multiple="" tabindex="4">
|
||||||
|
{% for cf in cmd_filters_remain %}
|
||||||
|
<option value="{{ cf.id }}" id="opt_{{ cf.id }}" >{{ cf }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" class="no-borders">
|
||||||
|
<button type="button" class="btn btn-info btn-sm" id="btn-binding-command-filters">{% trans 'Confirm' %}</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</form>
|
||||||
|
{% for cf in object.cmd_filters.all %}
|
||||||
|
<tr>
|
||||||
|
<td><b class="bdg-command-filters" data-gid={{ cf.id }}>{{ cf }}</b></td>
|
||||||
|
<td>
|
||||||
|
<button class="btn btn-danger pull-right btn-xs btn-unbound-command-filter" data-gid={{ cf.id }} type="button"><i class="fa fa-minus"></i></button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -201,27 +204,13 @@
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block custom_foot_js %}
|
{% block custom_foot_js %}
|
||||||
<script>
|
<script>
|
||||||
function updateSystemUserNode(nodes) {
|
function updateCommandFilters(command_filters) {
|
||||||
var the_url = "{% url 'api-assets:system-user-detail' pk=system_user.id %}";
|
var the_url = "{% url 'api-assets:system-user-detail' pk=system_user.id %}";
|
||||||
var body = {
|
var body = {
|
||||||
nodes: Object.assign([], nodes)
|
cmd_filters: Object.assign([], command_filters)
|
||||||
};
|
};
|
||||||
var success = function(data) {
|
var success = function(data) {
|
||||||
// remove all the selected groups from select > option and rendered ul element;
|
location.reload();
|
||||||
$('.select2-selection__rendered').empty();
|
|
||||||
$('#node_selected').val('');
|
|
||||||
$.map(jumpserver.nodes_selected, function(node_name, index) {
|
|
||||||
$('#opt_' + index).remove();
|
|
||||||
// change tr html of user groups.
|
|
||||||
$('.node_edit tbody').append(
|
|
||||||
'<tr>' +
|
|
||||||
'<td><b class="bdg_node" data-gid="' + index + '">' + node_name + '</b></td>' +
|
|
||||||
'<td><button class="btn btn-danger btn-xs pull-right btn-remove-from-node" type="button"><i class="fa fa-minus"></i></button></td>' +
|
|
||||||
'</tr>'
|
|
||||||
)
|
|
||||||
});
|
|
||||||
// clear jumpserver.groups_selected
|
|
||||||
jumpserver.nodes_selected = {};
|
|
||||||
};
|
};
|
||||||
APIUpdateAttr({
|
APIUpdateAttr({
|
||||||
url: the_url,
|
url: the_url,
|
||||||
@ -229,21 +218,12 @@ function updateSystemUserNode(nodes) {
|
|||||||
success: success
|
success: success
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
jumpserver.nodes_selected = {};
|
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
if($('#id_protocol_type').text() === 'rdp'){
|
if($('#id_protocol_type').text() === 'rdp'){
|
||||||
$('.only-ssh').addClass('hidden')
|
$('.only-ssh').addClass('hidden')
|
||||||
}
|
}
|
||||||
$(".panel-body .table tr:visible:first").addClass('no-borders-tr');
|
$(".panel-body .table tr:visible:first").addClass('no-borders-tr');
|
||||||
$('.select2').select2()
|
$('.select2').select2()
|
||||||
.on('select2:select', function(evt) {
|
|
||||||
var data = evt.params.data;
|
|
||||||
jumpserver.nodes_selected[data.id] = data.text;
|
|
||||||
})
|
|
||||||
.on('select2:unselect', function(evt) {
|
|
||||||
var data = evt.params.data;
|
|
||||||
delete jumpserver.nodes_selected[data.id];
|
|
||||||
});
|
|
||||||
})
|
})
|
||||||
.on('click', '#btn-auto-push', function () {
|
.on('click', '#btn-auto-push', function () {
|
||||||
var checked = $(this).prop('checked');
|
var checked = $(this).prop('checked');
|
||||||
@ -255,33 +235,6 @@ $(document).ready(function () {
|
|||||||
url: the_url,
|
url: the_url,
|
||||||
body: JSON.stringify(body)
|
body: JSON.stringify(body)
|
||||||
});
|
});
|
||||||
})
|
|
||||||
.on('click', '#btn-add-to-node', function() {
|
|
||||||
if (Object.keys(jumpserver.nodes_selected).length === 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
var nodes = $('.bdg_node').map(function() {
|
|
||||||
return $(this).data('gid');
|
|
||||||
}).get();
|
|
||||||
$.map(jumpserver.nodes_selected, function(value, index) {
|
|
||||||
nodes.push(index);
|
|
||||||
});
|
|
||||||
updateSystemUserNode(nodes);
|
|
||||||
})
|
|
||||||
.on('click', '.btn-remove-from-node', function() {
|
|
||||||
var $this = $(this);
|
|
||||||
var $tr = $this.closest('tr');
|
|
||||||
var $badge = $tr.find('.bdg_node');
|
|
||||||
var gid = $badge.data('gid');
|
|
||||||
var node_name = $badge.html() || $badge.text();
|
|
||||||
$('#groups_selected').append(
|
|
||||||
'<option value="' + gid + '" id="opt_' + gid + '">' + node_name + '</option>'
|
|
||||||
);
|
|
||||||
$tr.remove();
|
|
||||||
var nodes = $('.bdg_node').map(function () {
|
|
||||||
return $(this).data('gid');
|
|
||||||
}).get();
|
|
||||||
updateSystemUserNode(nodes);
|
|
||||||
}).on('click', '.btn-del', function () {
|
}).on('click', '.btn-del', function () {
|
||||||
var $this = $(this);
|
var $this = $(this);
|
||||||
var name = "{{ system_user.name}}";
|
var name = "{{ system_user.name}}";
|
||||||
@ -317,25 +270,24 @@ $(document).ready(function () {
|
|||||||
success: success,
|
success: success,
|
||||||
flash_message: false
|
flash_message: false
|
||||||
});
|
});
|
||||||
}).on('click', '.btn-clear-auth', function () {
|
}).on('click', '#btn-binding-command-filters', function () {
|
||||||
var the_url = '{% url "api-assets:system-user-auth-info" pk=system_user.id %}';
|
var new_selected_cmd_filters = $.map($('#command_filters_selected').select2('data'), function (i) {
|
||||||
var name = '{{ system_user.name }}';
|
return i.id;
|
||||||
swal({
|
|
||||||
title: "{% trans 'Are you sure to remove authentication information for the system user ?' %}",
|
|
||||||
text: " [" + name + "] ",
|
|
||||||
type: "warning",
|
|
||||||
showCancelButton: true,
|
|
||||||
cancelButtonText: "{% trans 'Cancel' %}",
|
|
||||||
confirmButtonColor: "#ed5565",
|
|
||||||
confirmButtonText: "{% trans 'Confirm' %}",
|
|
||||||
closeOnConfirm: true
|
|
||||||
}, function () {
|
|
||||||
APIUpdateAttr({
|
|
||||||
url: the_url,
|
|
||||||
method: 'DELETE',
|
|
||||||
success_message: "{% trans 'Clear auth' %}" + " {% trans 'success' %}"
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
var origin_cmd_filters = $.map($(".bdg-command-filters"), function (s) {
|
||||||
|
return $(s).data('gid')
|
||||||
|
});
|
||||||
|
var command_filters = new_selected_cmd_filters.concat(origin_cmd_filters);
|
||||||
|
updateCommandFilters(command_filters)
|
||||||
|
}).on('click', '.btn-unbound-command-filter', function () {
|
||||||
|
var unbound_command_filter = $(this).data('gid');
|
||||||
|
var origin_command_filters = $.map($(".bdg-command-filters"), function (s) {
|
||||||
|
return $(s).data('gid')
|
||||||
|
});
|
||||||
|
var command_filters = $.grep(origin_command_filters, function (n, i) {
|
||||||
|
return n !== unbound_command_filter
|
||||||
|
});
|
||||||
|
updateCommandFilters(command_filters)
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -1,8 +1,11 @@
|
|||||||
# coding:utf-8
|
# coding:utf-8
|
||||||
from django.urls import path
|
from django.urls import path
|
||||||
from .. import api
|
from rest_framework_nested import routers
|
||||||
|
# from rest_framework.routers import DefaultRouter
|
||||||
from rest_framework_bulk.routes import BulkRouter
|
from rest_framework_bulk.routes import BulkRouter
|
||||||
|
|
||||||
|
from .. import api
|
||||||
|
|
||||||
app_name = 'assets'
|
app_name = 'assets'
|
||||||
|
|
||||||
router = BulkRouter()
|
router = BulkRouter()
|
||||||
@ -13,6 +16,11 @@ router.register(r'labels', api.LabelViewSet, 'label')
|
|||||||
router.register(r'nodes', api.NodeViewSet, 'node')
|
router.register(r'nodes', api.NodeViewSet, 'node')
|
||||||
router.register(r'domain', api.DomainViewSet, 'domain')
|
router.register(r'domain', api.DomainViewSet, 'domain')
|
||||||
router.register(r'gateway', api.GatewayViewSet, 'gateway')
|
router.register(r'gateway', api.GatewayViewSet, 'gateway')
|
||||||
|
router.register(r'cmd-filter', api.CommandFilterViewSet, 'cmd-filter')
|
||||||
|
|
||||||
|
cmd_filter_router = routers.NestedDefaultRouter(router, r'cmd-filter', lookup='filter')
|
||||||
|
cmd_filter_router.register(r'rules', api.CommandFilterRuleViewSet, 'cmd-filter-rule')
|
||||||
|
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('assets-bulk/', api.AssetListUpdateApi.as_view(), name='asset-bulk-update'),
|
path('assets-bulk/', api.AssetListUpdateApi.as_view(), name='asset-bulk-update'),
|
||||||
@ -42,6 +50,9 @@ urlpatterns = [
|
|||||||
path('system-user/<uuid:pk>/connective/',
|
path('system-user/<uuid:pk>/connective/',
|
||||||
api.SystemUserTestConnectiveApi.as_view(), name='system-user-connective'),
|
api.SystemUserTestConnectiveApi.as_view(), name='system-user-connective'),
|
||||||
|
|
||||||
|
path('system-user/<uuid:pk>/cmd-filter-rules/',
|
||||||
|
api.SystemUserCommandFilterRuleListApi.as_view(), name='system-user-cmd-filter-rule-list'),
|
||||||
|
|
||||||
path('nodes/<uuid:pk>/children/',
|
path('nodes/<uuid:pk>/children/',
|
||||||
api.NodeChildrenApi.as_view(), name='node-children'),
|
api.NodeChildrenApi.as_view(), name='node-children'),
|
||||||
path('nodes/children/', api.NodeChildrenApi.as_view(), name='node-children-2'),
|
path('nodes/children/', api.NodeChildrenApi.as_view(), name='node-children-2'),
|
||||||
@ -64,5 +75,5 @@ urlpatterns = [
|
|||||||
api.GatewayTestConnectionApi.as_view(), name='test-gateway-connective'),
|
api.GatewayTestConnectionApi.as_view(), name='test-gateway-connective'),
|
||||||
]
|
]
|
||||||
|
|
||||||
urlpatterns += router.urls
|
urlpatterns += router.urls + cmd_filter_router.urls
|
||||||
|
|
||||||
|
@ -49,4 +49,12 @@ urlpatterns = [
|
|||||||
|
|
||||||
path('domain/<uuid:pk>/gateway/create/', views.DomainGatewayCreateView.as_view(), name='domain-gateway-create'),
|
path('domain/<uuid:pk>/gateway/create/', views.DomainGatewayCreateView.as_view(), name='domain-gateway-create'),
|
||||||
path('domain/gateway/<uuid:pk>/update/', views.DomainGatewayUpdateView.as_view(), name='domain-gateway-update'),
|
path('domain/gateway/<uuid:pk>/update/', views.DomainGatewayUpdateView.as_view(), name='domain-gateway-update'),
|
||||||
|
|
||||||
|
path('cmd-filter/', views.CommandFilterListView.as_view(), name='cmd-filter-list'),
|
||||||
|
path('cmd-filter/create/', views.CommandFilterCreateView.as_view(), name='cmd-filter-create'),
|
||||||
|
path('cmd-filter/<uuid:pk>/update/', views.CommandFilterUpdateView.as_view(), name='cmd-filter-update'),
|
||||||
|
path('cmd-filter/<uuid:pk>/', views.CommandFilterDetailView.as_view(), name='cmd-filter-detail'),
|
||||||
|
path('cmd-filter/<uuid:pk>/rule/', views.CommandFilterRuleListView.as_view(), name='cmd-filter-rule-list'),
|
||||||
|
path('cmd-filter/<uuid:filter_pk>/rule/create/', views.CommandFilterRuleCreateView.as_view(), name='cmd-filter-rule-create'),
|
||||||
|
path('cmd-filter/<uuid:filter_pk>/rule/<uuid:pk>/update/', views.CommandFilterRuleUpdateView.as_view(), name='cmd-filter-rule-update'),
|
||||||
]
|
]
|
||||||
|
@ -4,3 +4,4 @@ from .system_user import *
|
|||||||
from .admin_user import *
|
from .admin_user import *
|
||||||
from .label import *
|
from .label import *
|
||||||
from .domain import *
|
from .domain import *
|
||||||
|
from .cmd_filter import *
|
||||||
|
168
apps/assets/views/cmd_filter.py
Normal file
168
apps/assets/views/cmd_filter.py
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
|
||||||
|
from django.views.generic import TemplateView, CreateView, \
|
||||||
|
UpdateView, DeleteView, DetailView
|
||||||
|
from django.views.generic.detail import SingleObjectMixin
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
from django.urls import reverse_lazy
|
||||||
|
from django.shortcuts import get_object_or_404, reverse
|
||||||
|
|
||||||
|
from common.permissions import AdminUserRequiredMixin
|
||||||
|
from common.const import create_success_msg, update_success_msg
|
||||||
|
from ..models import CommandFilter, CommandFilterRule, SystemUser
|
||||||
|
from ..forms import CommandFilterForm, CommandFilterRuleForm
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = (
|
||||||
|
"CommandFilterListView", "CommandFilterCreateView",
|
||||||
|
"CommandFilterUpdateView",
|
||||||
|
"CommandFilterRuleListView", "CommandFilterRuleCreateView",
|
||||||
|
"CommandFilterRuleUpdateView", "CommandFilterDetailView",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class CommandFilterListView(AdminUserRequiredMixin, TemplateView):
|
||||||
|
template_name = 'assets/cmd_filter_list.html'
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = {
|
||||||
|
'app': _('Assets'),
|
||||||
|
'action': _('Command filter list'),
|
||||||
|
}
|
||||||
|
kwargs.update(context)
|
||||||
|
return super().get_context_data(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class CommandFilterCreateView(AdminUserRequiredMixin, CreateView):
|
||||||
|
model = CommandFilter
|
||||||
|
template_name = 'assets/cmd_filter_create_update.html'
|
||||||
|
form_class = CommandFilterForm
|
||||||
|
success_url = reverse_lazy('assets:cmd-filter-list')
|
||||||
|
success_message = create_success_msg
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = {
|
||||||
|
'app': _('Assets'),
|
||||||
|
'action': _('Create command filter'),
|
||||||
|
}
|
||||||
|
kwargs.update(context)
|
||||||
|
return super().get_context_data(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class CommandFilterUpdateView(AdminUserRequiredMixin, UpdateView):
|
||||||
|
model = CommandFilter
|
||||||
|
template_name = 'assets/cmd_filter_create_update.html'
|
||||||
|
form_class = CommandFilterForm
|
||||||
|
success_url = reverse_lazy('assets:cmd-filter-list')
|
||||||
|
success_message = update_success_msg
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = {
|
||||||
|
'app': _('Assets'),
|
||||||
|
'action': _('Update command filter'),
|
||||||
|
}
|
||||||
|
kwargs.update(context)
|
||||||
|
return super().get_context_data(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class CommandFilterDetailView(AdminUserRequiredMixin, DetailView):
|
||||||
|
model = CommandFilter
|
||||||
|
template_name = 'assets/cmd_filter_detail.html'
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
system_users_remain = SystemUser.objects\
|
||||||
|
.exclude(cmd_filters=self.object)\
|
||||||
|
.exclude(protocol='rdp')
|
||||||
|
context = {
|
||||||
|
'app': _('Assets'),
|
||||||
|
'action': _('Command filter detail'),
|
||||||
|
'system_users_remain': system_users_remain
|
||||||
|
}
|
||||||
|
kwargs.update(context)
|
||||||
|
return super().get_context_data(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class CommandFilterRuleListView(AdminUserRequiredMixin, SingleObjectMixin, TemplateView):
|
||||||
|
template_name = 'assets/cmd_filter_rule_list.html'
|
||||||
|
model = CommandFilter
|
||||||
|
object = None
|
||||||
|
|
||||||
|
def get(self, request, *args, **kwargs):
|
||||||
|
self.object = self.get_object(queryset=self.model.objects.all())
|
||||||
|
return super().get(request, *args, **kwargs)
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = {
|
||||||
|
'app': _('Assets'),
|
||||||
|
'action': _('Command filter rule list'),
|
||||||
|
'object': self.get_object()
|
||||||
|
}
|
||||||
|
kwargs.update(context)
|
||||||
|
return super().get_context_data(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class CommandFilterRuleCreateView(AdminUserRequiredMixin, CreateView):
|
||||||
|
template_name = 'assets/cmd_filter_rule_create_update.html'
|
||||||
|
model = CommandFilterRule
|
||||||
|
form_class = CommandFilterRuleForm
|
||||||
|
success_message = create_success_msg
|
||||||
|
cmd_filter = None
|
||||||
|
|
||||||
|
def get_success_url(self):
|
||||||
|
return reverse('assets:cmd-filter-rule-list', kwargs={
|
||||||
|
'pk': self.cmd_filter.id
|
||||||
|
})
|
||||||
|
|
||||||
|
def get_form(self, form_class=None):
|
||||||
|
form = super().get_form(form_class=form_class)
|
||||||
|
form['filter'].initial = self.cmd_filter
|
||||||
|
form['filter'].field.widget.attrs['readonly'] = 1
|
||||||
|
return form
|
||||||
|
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
filter_pk = self.kwargs.get('filter_pk')
|
||||||
|
self.cmd_filter = get_object_or_404(CommandFilter, pk=filter_pk)
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = {
|
||||||
|
'app': _('Assets'),
|
||||||
|
'action': _('Create command filter rule'),
|
||||||
|
'object': self.cmd_filter,
|
||||||
|
}
|
||||||
|
kwargs.update(context)
|
||||||
|
return super().get_context_data(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class CommandFilterRuleUpdateView(AdminUserRequiredMixin, UpdateView):
|
||||||
|
template_name = 'assets/cmd_filter_rule_create_update.html'
|
||||||
|
model = CommandFilterRule
|
||||||
|
form_class = CommandFilterRuleForm
|
||||||
|
success_message = create_success_msg
|
||||||
|
cmd_filter = None
|
||||||
|
|
||||||
|
def get_success_url(self):
|
||||||
|
return reverse('assets:cmd-filter-rule-list', kwargs={
|
||||||
|
'pk': self.cmd_filter.id
|
||||||
|
})
|
||||||
|
|
||||||
|
def get_form(self, form_class=None):
|
||||||
|
form = super().get_form(form_class=form_class)
|
||||||
|
form['filter'].initial = self.cmd_filter
|
||||||
|
form['filter'].field.widget.attrs['readonly'] = 1
|
||||||
|
return form
|
||||||
|
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
filter_pk = self.kwargs.get('filter_pk')
|
||||||
|
self.cmd_filter = get_object_or_404(CommandFilter, pk=filter_pk)
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = {
|
||||||
|
'app': _('Assets'),
|
||||||
|
'action': _('Update command filter rule'),
|
||||||
|
'object': self.cmd_filter,
|
||||||
|
}
|
||||||
|
kwargs.update(context)
|
||||||
|
return super().get_context_data(**kwargs)
|
@ -9,7 +9,7 @@ from django.views.generic.detail import DetailView
|
|||||||
|
|
||||||
from common.const import create_success_msg, update_success_msg
|
from common.const import create_success_msg, update_success_msg
|
||||||
from ..forms import SystemUserForm
|
from ..forms import SystemUserForm
|
||||||
from ..models import SystemUser, Node
|
from ..models import SystemUser, Node, CommandFilter
|
||||||
from common.permissions import AdminUserRequiredMixin
|
from common.permissions import AdminUserRequiredMixin
|
||||||
|
|
||||||
|
|
||||||
@ -73,7 +73,7 @@ class SystemUserDetailView(AdminUserRequiredMixin, DetailView):
|
|||||||
context = {
|
context = {
|
||||||
'app': _('Assets'),
|
'app': _('Assets'),
|
||||||
'action': _('System user detail'),
|
'action': _('System user detail'),
|
||||||
'nodes_remain': Node.objects.exclude(systemuser=self.object)
|
'cmd_filters_remain': CommandFilter.objects.exclude(system_users=self.object)
|
||||||
}
|
}
|
||||||
kwargs.update(context)
|
kwargs.update(context)
|
||||||
return super().get_context_data(**kwargs)
|
return super().get_context_data(**kwargs)
|
||||||
@ -91,10 +91,11 @@ class SystemUserAssetView(AdminUserRequiredMixin, DetailView):
|
|||||||
context_object_name = 'system_user'
|
context_object_name = 'system_user'
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
|
nodes_remain = sorted(Node.objects.exclude(systemuser=self.object), reverse=True)
|
||||||
context = {
|
context = {
|
||||||
'app': _('assets'),
|
'app': _('assets'),
|
||||||
'action': _('System user asset'),
|
'action': _('System user asset'),
|
||||||
'nodes_remain': Node.objects.exclude(systemuser=self.object)
|
'nodes_remain': nodes_remain
|
||||||
}
|
}
|
||||||
kwargs.update(context)
|
kwargs.update(context)
|
||||||
return super().get_context_data(**kwargs)
|
return super().get_context_data(**kwargs)
|
||||||
|
@ -86,3 +86,21 @@ class FormEncryptCharField(FormEncryptMixin, forms.CharField):
|
|||||||
|
|
||||||
class FormEncryptDictField(FormEncryptMixin, FormDictField):
|
class FormEncryptDictField(FormEncryptMixin, FormDictField):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class ChoiceDisplayField(serializers.ChoiceField):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super(ChoiceDisplayField, self).__init__(*args, **kwargs)
|
||||||
|
self.choice_strings_to_display = {
|
||||||
|
six.text_type(key): value for key, value in self.choices.items()
|
||||||
|
}
|
||||||
|
|
||||||
|
def to_representation(self, value):
|
||||||
|
if value is None:
|
||||||
|
return value
|
||||||
|
return {
|
||||||
|
'value': self.choice_strings_to_values.get(six.text_type(value),
|
||||||
|
value),
|
||||||
|
'display': self.choice_strings_to_display.get(six.text_type(value),
|
||||||
|
value),
|
||||||
|
}
|
||||||
|
@ -100,3 +100,9 @@ def is_bool_field(field):
|
|||||||
@register.filter
|
@register.filter
|
||||||
def to_dict(data):
|
def to_dict(data):
|
||||||
return dict(data)
|
return dict(data)
|
||||||
|
|
||||||
|
|
||||||
|
@register.filter
|
||||||
|
def sort(data):
|
||||||
|
print(data)
|
||||||
|
return sorted(data)
|
||||||
|
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@ -14,7 +14,8 @@ class SwitchOrgView(DetailView):
|
|||||||
pk = kwargs.get('pk')
|
pk = kwargs.get('pk')
|
||||||
self.object = Organization.get_instance(pk)
|
self.object = Organization.get_instance(pk)
|
||||||
request.session['oid'] = self.object.id.__str__()
|
request.session['oid'] = self.object.id.__str__()
|
||||||
return redirect('index')
|
referer = request.META.get('HTTP_REFERER', reverse('index'))
|
||||||
|
return redirect(referer)
|
||||||
|
|
||||||
|
|
||||||
class SwitchToAOrgView(View):
|
class SwitchToAOrgView(View):
|
||||||
|
@ -1528,9 +1528,15 @@ a.ui-button:active .ui-icon,
|
|||||||
.std42-dialog .ui-dialog-titlebar .elfinder-titlebar-minimize:hover .ui-icon {
|
.std42-dialog .ui-dialog-titlebar .elfinder-titlebar-minimize:hover .ui-icon {
|
||||||
background-color: #ff9800;
|
background-color: #ff9800;
|
||||||
}
|
}
|
||||||
.elfinder-dialog-title {
|
.elfinder-dialog-title {
|
||||||
color: #f1f1f1;
|
color: #f1f1f1;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ui-dialog-buttonpane {
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
.std42-dialog .ui-dialog-content {
|
.std42-dialog .ui-dialog-content {
|
||||||
background: #fff;
|
background: #fff;
|
||||||
}
|
}
|
||||||
|
@ -375,7 +375,7 @@
|
|||||||
'pass' : '密码', // added 18.04.2012
|
'pass' : '密码', // added 18.04.2012
|
||||||
'confirmUnmount' : '确实要卸载 $1?', // from v2.1 added 30.04.2012
|
'confirmUnmount' : '确实要卸载 $1?', // from v2.1 added 30.04.2012
|
||||||
'dropFilesBrowser': '从浏览器中拖放或粘贴文件', // from v2.1 added 30.05.2012
|
'dropFilesBrowser': '从浏览器中拖放或粘贴文件', // from v2.1 added 30.05.2012
|
||||||
'dropPasteFiles' : '拖放文件,粘贴网址或剪贴板图像', // from v2.1 added 07.04.2014
|
'dropPasteFiles' : '拖放文件,或粘贴剪贴板图像', // from v2.1 added 07.04.2014
|
||||||
'encoding' : '编码', // from v2.1 added 19.12.2014
|
'encoding' : '编码', // from v2.1 added 19.12.2014
|
||||||
'locale' : '语言环境', // from v2.1 added 19.12.2014
|
'locale' : '语言环境', // from v2.1 added 19.12.2014
|
||||||
'searchTarget' : '目标: $1', // from v2.1 added 22.5.2015
|
'searchTarget' : '目标: $1', // from v2.1 added 22.5.2015
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
<div class="footer fixed">
|
<div class="footer fixed">
|
||||||
<div class="pull-right">
|
<div class="pull-right">
|
||||||
Version <strong>1.4.1-{% include '_build.html' %}</strong> GPLv2.
|
Version <strong>1.4.3-{% include '_build.html' %}</strong> GPLv2.
|
||||||
<!--<img style="display: none" src="http://www.jumpserver.org/img/evaluate_avatar1.jpg">-->
|
<!--<img style="display: none" src="http://www.jumpserver.org/img/evaluate_avatar1.jpg">-->
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
<li id="admin-user"><a href="{% url 'assets:admin-user-list' %}">{% trans 'Admin user' %}</a></li>
|
<li id="admin-user"><a href="{% url 'assets:admin-user-list' %}">{% trans 'Admin user' %}</a></li>
|
||||||
<li id="system-user"><a href="{% url 'assets:system-user-list' %}">{% trans 'System user' %}</a></li>
|
<li id="system-user"><a href="{% url 'assets:system-user-list' %}">{% trans 'System user' %}</a></li>
|
||||||
<li id="label"><a href="{% url 'assets:label-list' %}">{% trans 'Labels' %}</a></li>
|
<li id="label"><a href="{% url 'assets:label-list' %}">{% trans 'Labels' %}</a></li>
|
||||||
|
<li id="cmd-filter"><a href="{% url 'assets:cmd-filter-list' %}">{% trans 'Command filters' %}</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li id="perms">
|
<li id="perms">
|
||||||
@ -47,6 +48,11 @@
|
|||||||
<span class="nav-label">{% trans 'Web terminal' %}</span>
|
<span class="nav-label">{% trans 'Web terminal' %}</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="{% url 'terminal:web-sftp' %}" target="_blank">
|
||||||
|
<span class="nav-label">{% trans 'File manager' %}</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
{% if request.user.is_superuser %}
|
{% if request.user.is_superuser %}
|
||||||
<li id="terminal"><a href="{% url 'terminal:terminal-list' %}">{% trans 'Terminal' %}</a></li>
|
<li id="terminal"><a href="{% url 'terminal:terminal-list' %}">{% trans 'Terminal' %}</a></li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@ -91,10 +97,8 @@
|
|||||||
{% for plugin in XPACK_PLUGINS %}
|
{% for plugin in XPACK_PLUGINS %}
|
||||||
<li id="{{ plugin.name }}"><a href="{{ plugin.endpoint }}">{% trans plugin.verbose_name %}</a></li>
|
<li id="{{ plugin.name }}"><a href="{{ plugin.endpoint }}">{% trans plugin.verbose_name %}</a></li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
<li id="id_cloud_account"><a href="{% url 'xpack:cloud:account-list' %}">{% trans 'Account list' %}</a></li>
|
<li id="id_cloud_account"><a href="{% url 'xpack:cloud:account-list' %}">{% trans 'Account list' %}</a></li>
|
||||||
<li id="id_cloud_sync_task"><a href="{% url 'xpack:cloud:sync-task-list' %}">{% trans 'Sync task list' %}</a></li>
|
<li id="id_cloud_sync_task"><a href="{% url 'xpack:cloud:sync-task-list' %}">{% trans 'Sync task list' %}</a></li>
|
||||||
|
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
|
|
||||||
|
from django.core.cache import cache
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
from rest_framework_bulk.serializers import BulkListSerializer
|
from rest_framework_bulk.serializers import BulkListSerializer
|
||||||
@ -28,19 +29,8 @@ class TerminalSerializer(serializers.ModelSerializer):
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_is_alive(obj):
|
def get_is_alive(obj):
|
||||||
try:
|
key = StatusSerializer.CACHE_KEY_PREFIX + str(obj.id)
|
||||||
status = obj.status_set.latest()
|
return cache.get(key)
|
||||||
except Status.DoesNotExist:
|
|
||||||
status = None
|
|
||||||
|
|
||||||
if not status:
|
|
||||||
return False
|
|
||||||
|
|
||||||
delta = timezone.now() - status.date_created
|
|
||||||
if delta < timezone.timedelta(seconds=600):
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
class SessionSerializer(BulkSerializerMixin, serializers.ModelSerializer):
|
class SessionSerializer(BulkSerializerMixin, serializers.ModelSerializer):
|
||||||
@ -57,11 +47,18 @@ class SessionSerializer(BulkSerializerMixin, serializers.ModelSerializer):
|
|||||||
|
|
||||||
|
|
||||||
class StatusSerializer(serializers.ModelSerializer):
|
class StatusSerializer(serializers.ModelSerializer):
|
||||||
|
CACHE_KEY_PREFIX = 'terminal_status_'
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
fields = '__all__'
|
fields = '__all__'
|
||||||
model = Status
|
model = Status
|
||||||
|
|
||||||
|
def create(self, validated_data):
|
||||||
|
terminal_id = str(validated_data['terminal'].id)
|
||||||
|
key = self.CACHE_KEY_PREFIX + terminal_id
|
||||||
|
cache.set(key, 1, 60)
|
||||||
|
return validated_data
|
||||||
|
|
||||||
|
|
||||||
class TaskSerializer(BulkSerializerMixin, serializers.ModelSerializer):
|
class TaskSerializer(BulkSerializerMixin, serializers.ModelSerializer):
|
||||||
|
|
||||||
|
@ -16,6 +16,7 @@ urlpatterns = [
|
|||||||
path('terminal/<uuid:pk>/update/', views.TerminalUpdateView.as_view(), name='terminal-update'),
|
path('terminal/<uuid:pk>/update/', views.TerminalUpdateView.as_view(), name='terminal-update'),
|
||||||
path('<uuid:pk>/accept/', views.TerminalAcceptView.as_view(), name='terminal-accept'),
|
path('<uuid:pk>/accept/', views.TerminalAcceptView.as_view(), name='terminal-accept'),
|
||||||
path('web-terminal/', views.WebTerminalView.as_view(), name='web-terminal'),
|
path('web-terminal/', views.WebTerminalView.as_view(), name='web-terminal'),
|
||||||
|
path('web-sftp/', views.WebSFTPView.as_view(), name='web-sftp'),
|
||||||
|
|
||||||
# Session view
|
# Session view
|
||||||
path('session-online/', views.SessionOnlineListView.as_view(), name='session-online-list'),
|
path('session-online/', views.SessionOnlineListView.as_view(), name='session-online-list'),
|
||||||
|
@ -16,7 +16,7 @@ from common.permissions import SuperUserRequiredMixin
|
|||||||
__all__ = [
|
__all__ = [
|
||||||
"TerminalListView", "TerminalUpdateView", "TerminalDetailView",
|
"TerminalListView", "TerminalUpdateView", "TerminalDetailView",
|
||||||
"TerminalDeleteView", "TerminalConnectView", "TerminalAcceptView",
|
"TerminalDeleteView", "TerminalConnectView", "TerminalAcceptView",
|
||||||
"WebTerminalView",
|
"WebTerminalView", 'WebSFTPView',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
@ -93,6 +93,9 @@ class TerminalAcceptView(SuperUserRequiredMixin, JSONResponseMixin, UpdateView):
|
|||||||
|
|
||||||
|
|
||||||
class TerminalConnectView(LoginRequiredMixin, SuperUserRequiredMixin, DetailView):
|
class TerminalConnectView(LoginRequiredMixin, SuperUserRequiredMixin, DetailView):
|
||||||
|
"""
|
||||||
|
Abandon
|
||||||
|
"""
|
||||||
template_name = 'flash_message_standalone.html'
|
template_name = 'flash_message_standalone.html'
|
||||||
model = Terminal
|
model = Terminal
|
||||||
|
|
||||||
@ -121,3 +124,8 @@ class TerminalConnectView(LoginRequiredMixin, SuperUserRequiredMixin, DetailView
|
|||||||
class WebTerminalView(LoginRequiredMixin, View):
|
class WebTerminalView(LoginRequiredMixin, View):
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
return redirect('/luna/?' + request.GET.urlencode())
|
return redirect('/luna/?' + request.GET.urlencode())
|
||||||
|
|
||||||
|
|
||||||
|
class WebSFTPView(LoginRequiredMixin, View):
|
||||||
|
def get(self, request, *args, **kwargs):
|
||||||
|
return redirect('/coco/elfinder/sftp/?' + request.GET.urlencode())
|
||||||
|
@ -71,3 +71,4 @@ urllib3==1.22
|
|||||||
vine==1.1.4
|
vine==1.1.4
|
||||||
drf-yasg==1.9.1
|
drf-yasg==1.9.1
|
||||||
Werkzeug==0.14.1
|
Werkzeug==0.14.1
|
||||||
|
drf-nested-routers==0.90.2
|
||||||
|
Loading…
Reference in New Issue
Block a user