Unverified Commit c8623c3b authored by 老广's avatar 老广 Committed by GitHub

Merge pull request #2947 from jumpserver/dev

Dev
parents e6037303 9d54baac
...@@ -107,6 +107,21 @@ function hiddenFields(){ ...@@ -107,6 +107,21 @@ function hiddenFields(){
}); });
$('.' + app_type + '-fields').removeClass('hidden'); $('.' + app_type + '-fields').removeClass('hidden');
} }
function constructParams(data) {
var typeList = ['chrome', 'mysql_workbench', 'vmware_client', 'custom'];
var params = {};
for (var type in typeList){
if (data.type === type){
for (var k in data){
if (k.startsWith(data.type)){
params[k] = data[k]
}
}
break
}
}
return params;
}
$(document).ready(function () { $(document).ready(function () {
$('.select2').select2({ $('.select2').select2({
closeOnSelect: true closeOnSelect: true
...@@ -118,6 +133,28 @@ $(document).ready(function () { ...@@ -118,6 +133,28 @@ $(document).ready(function () {
.on('change', app_type_id, function(){ .on('change', app_type_id, function(){
hiddenFields(); hiddenFields();
setDefaultValue(); setDefaultValue();
}); })
.on("submit", "form", function (evt) {
evt.preventDefault();
var the_url = '{% url "api-applications:remote-app-list" %}';
var redirect_to = '{% url "applications:remote-app-list" %}';
var method = "POST";
{% if type == "update" %}
the_url = '{% url "api-applications:remote-app-detail" object.id %}';
method = "PUT";
{% endif %}
var form = $("form");
var data = form.serializeObject();
data["params"] = constructParams(data);
var props = {
url: the_url,
data: data,
method: method,
form: form,
redirect_to: redirect_to
};
formSubmit(props);
})
;
</script> </script>
{% endblock %} {% endblock %}
\ No newline at end of file
...@@ -46,6 +46,7 @@ class RemoteAppCreateView(PermissionsMixin, SuccessMessageMixin, CreateView): ...@@ -46,6 +46,7 @@ class RemoteAppCreateView(PermissionsMixin, SuccessMessageMixin, CreateView):
context = { context = {
'app': _('Applications'), 'app': _('Applications'),
'action': _('Create RemoteApp'), 'action': _('Create RemoteApp'),
'type': 'create'
} }
kwargs.update(context) kwargs.update(context)
return super().get_context_data(**kwargs) return super().get_context_data(**kwargs)
...@@ -68,6 +69,7 @@ class RemoteAppUpdateView(PermissionsMixin, SuccessMessageMixin, UpdateView): ...@@ -68,6 +69,7 @@ class RemoteAppUpdateView(PermissionsMixin, SuccessMessageMixin, UpdateView):
context = { context = {
'app': _('Applications'), 'app': _('Applications'),
'action': _('Update RemoteApp'), 'action': _('Update RemoteApp'),
'type': 'update'
} }
kwargs.update(context) kwargs.update(context)
return super().get_context_data(**kwargs) return super().get_context_data(**kwargs)
......
...@@ -13,11 +13,11 @@ ...@@ -13,11 +13,11 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
from rest_framework_bulk import BulkModelViewSet
from rest_framework.pagination import LimitOffsetPagination from rest_framework.pagination import LimitOffsetPagination
from django.db.models import Count from django.db.models import Count
from common.utils import get_logger from common.utils import get_logger
from orgs.mixins import OrgBulkModelViewSet
from ..hands import IsOrgAdmin from ..hands import IsOrgAdmin
from ..models import Label from ..models import Label
from .. import serializers from .. import serializers
...@@ -27,7 +27,7 @@ logger = get_logger(__file__) ...@@ -27,7 +27,7 @@ logger = get_logger(__file__)
__all__ = ['LabelViewSet'] __all__ = ['LabelViewSet']
class LabelViewSet(BulkModelViewSet): class LabelViewSet(OrgBulkModelViewSet):
filter_fields = ("name", "value") filter_fields = ("name", "value")
search_fields = filter_fields search_fields = filter_fields
permission_classes = (IsOrgAdmin,) permission_classes = (IsOrgAdmin,)
......
...@@ -131,7 +131,7 @@ class NodeChildrenAsTreeApi(generics.ListAPIView): ...@@ -131,7 +131,7 @@ class NodeChildrenAsTreeApi(generics.ListAPIView):
if not include_assets: if not include_assets:
return queryset return queryset
assets = self.node.get_assets().only( assets = self.node.get_assets().only(
"id", "hostname", "ip", 'platform', "os", "org_id", "id", "hostname", "ip", 'platform', "os", "org_id", "protocols",
) )
for asset in assets: for asset in assets:
queryset.append(asset.as_tree_node(self.node)) queryset.append(asset.as_tree_node(self.node))
......
...@@ -29,9 +29,9 @@ class ProtocolForm(forms.Form): ...@@ -29,9 +29,9 @@ class ProtocolForm(forms.Form):
class AssetCreateForm(OrgModelForm): class AssetCreateForm(OrgModelForm):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
if not self.data: nodes_field = self.fields['nodes']
nodes_field = self.fields['nodes'] nodes_field.choices = ((n.id, n.full_value) for n in
nodes_field._queryset = Node.get_queryset() Node.get_queryset())
class Meta: class Meta:
model = Asset model = Asset
......
# Generated by Django 2.1.7 on 2019-07-11 12:18
import common.fields.model
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('assets', '0034_auto_20190705_1348'),
]
operations = [
migrations.AlterField(
model_name='adminuser',
name='private_key',
field=common.fields.model.EncryptTextField(blank=True, null=True, verbose_name='SSH private key'),
),
migrations.AlterField(
model_name='authbook',
name='private_key',
field=common.fields.model.EncryptTextField(blank=True, null=True, verbose_name='SSH private key'),
),
migrations.AlterField(
model_name='gateway',
name='private_key',
field=common.fields.model.EncryptTextField(blank=True, null=True, verbose_name='SSH private key'),
),
migrations.AlterField(
model_name='systemuser',
name='private_key',
field=common.fields.model.EncryptTextField(blank=True, null=True, verbose_name='SSH private key'),
),
]
...@@ -6,7 +6,8 @@ import uuid ...@@ -6,7 +6,8 @@ import uuid
import logging import logging
import random import random
from functools import reduce from functools import reduce
from collections import OrderedDict from collections import OrderedDict, defaultdict
from django.core.cache import cache
from django.db import models from django.db import models
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
...@@ -96,7 +97,53 @@ class ProtocolsMixin: ...@@ -96,7 +97,53 @@ class ProtocolsMixin:
return self.protocols_as_dict.get("ssh", 22) return self.protocols_as_dict.get("ssh", 22)
class Asset(ProtocolsMixin, OrgModelMixin): class NodesRelationMixin:
NODES_CACHE_KEY = 'ASSET_NODES_{}'
ALL_ASSET_NODES_CACHE_KEY = 'ALL_ASSETS_NODES'
CACHE_TIME = 3600 * 24 * 7
id = ""
_all_nodes_keys = None
@classmethod
def get_all_nodes_keys(cls):
"""
:return: {asset.id: [node.key, ]}
"""
from .node import Node
cache_key = cls.ALL_ASSET_NODES_CACHE_KEY
cached = cache.get(cache_key)
if cached:
return cached
assets = Asset.objects.all().only('id').prefetch_related(
models.Prefetch('nodes', queryset=Node.objects.all().only('key'))
)
assets_nodes_keys = {}
for asset in assets:
assets_nodes_keys[asset.id] = [n.key for n in asset.nodes.all()]
cache.set(cache_key, assets_nodes_keys, cls.CACHE_TIME)
return assets_nodes_keys
@classmethod
def expire_all_nodes_keys_cache(cls):
cache_key = cls.ALL_ASSET_NODES_CACHE_KEY
cache.delete(cache_key)
def get_nodes(self):
from .node import Node
nodes = self.nodes.all() or [Node.root()]
return nodes
def get_all_nodes(self, flat=False):
nodes = []
for node in self.get_nodes():
_nodes = node.get_ancestor(with_self=True)
nodes.append(_nodes)
if flat:
nodes = list(reduce(lambda x, y: set(x) | set(y), nodes))
return nodes
class Asset(ProtocolsMixin, NodesRelationMixin, OrgModelMixin):
# Important # Important
PLATFORM_CHOICES = ( PLATFORM_CHOICES = (
('Linux', 'Linux'), ('Linux', 'Linux'),
...@@ -182,20 +229,6 @@ class Asset(ProtocolsMixin, OrgModelMixin): ...@@ -182,20 +229,6 @@ class Asset(ProtocolsMixin, OrgModelMixin):
def is_support_ansible(self): def is_support_ansible(self):
return self.has_protocol('ssh') and self.platform not in ("Other",) return self.has_protocol('ssh') and self.platform not in ("Other",)
def get_nodes(self):
from .node import Node
nodes = self.nodes.all() or [Node.root()]
return nodes
def get_all_nodes(self, flat=False):
nodes = []
for node in self.get_nodes():
_nodes = node.get_ancestor(with_self=True)
nodes.append(_nodes)
if flat:
nodes = list(reduce(lambda x, y: set(x) | set(y), nodes))
return nodes
@property @property
def cpu_info(self): def cpu_info(self):
info = "" info = ""
......
...@@ -28,7 +28,7 @@ class AssetUser(OrgModelMixin): ...@@ -28,7 +28,7 @@ class AssetUser(OrgModelMixin):
name = models.CharField(max_length=128, verbose_name=_('Name')) name = models.CharField(max_length=128, verbose_name=_('Name'))
username = models.CharField(max_length=32, blank=True, verbose_name=_('Username'), validators=[alphanumeric]) username = models.CharField(max_length=32, blank=True, verbose_name=_('Username'), validators=[alphanumeric])
password = fields.EncryptCharField(max_length=256, blank=True, null=True, verbose_name=_('Password')) password = fields.EncryptCharField(max_length=256, blank=True, null=True, verbose_name=_('Password'))
private_key = fields.EncryptTextField(blank=True, null=True, verbose_name=_('SSH private key'), validators=[private_key_validator, ]) private_key = fields.EncryptTextField(blank=True, null=True, verbose_name=_('SSH private key'))
public_key = fields.EncryptTextField(blank=True, null=True, verbose_name=_('SSH public key')) public_key = fields.EncryptTextField(blank=True, null=True, verbose_name=_('SSH public key'))
comment = models.TextField(blank=True, verbose_name=_('Comment')) comment = models.TextField(blank=True, verbose_name=_('Comment'))
date_created = models.DateTimeField(auto_now_add=True, verbose_name=_("Date created")) date_created = models.DateTimeField(auto_now_add=True, verbose_name=_("Date created"))
......
...@@ -212,14 +212,12 @@ class AssetsAmountMixin: ...@@ -212,14 +212,12 @@ class AssetsAmountMixin:
if cached is not None: if cached is not None:
return cached return cached
assets_amount = self.get_all_assets().count() assets_amount = self.get_all_assets().count()
self.assets_amount = assets_amount cache.set(cache_key, assets_amount, self.cache_time)
return assets_amount return assets_amount
@assets_amount.setter @assets_amount.setter
def assets_amount(self, value): def assets_amount(self, value):
self._assets_amount = value self._assets_amount = value
cache_key = self._assets_amount_cache_key.format(self.key)
cache.set(cache_key, value, self.cache_time)
def expire_assets_amount(self): def expire_assets_amount(self):
ancestor_keys = self.get_ancestor_keys(with_self=True) ancestor_keys = self.get_ancestor_keys(with_self=True)
......
...@@ -117,16 +117,6 @@ class SystemUser(AssetUser): ...@@ -117,16 +117,6 @@ class SystemUser(AssetUser):
def __str__(self): def __str__(self):
return '{0.name}({0.username})'.format(self) return '{0.name}({0.username})'.format(self)
def to_json(self):
return {
'id': self.id,
'name': self.name,
'username': self.username,
'protocol': self.protocol,
'priority': self.priority,
'auto_push': self.auto_push,
}
@property @property
def login_mode_display(self): def login_mode_display(self):
return self.get_login_mode_display() return self.get_login_mode_display()
......
...@@ -21,19 +21,15 @@ class AdminUserSerializer(AuthSerializerMixin, BulkOrgResourceModelSerializer): ...@@ -21,19 +21,15 @@ class AdminUserSerializer(AuthSerializerMixin, BulkOrgResourceModelSerializer):
model = AdminUser model = AdminUser
fields = [ fields = [
'id', 'name', 'username', 'password', 'private_key', 'public_key', 'id', 'name', 'username', 'password', 'private_key', 'public_key',
'comment', 'connectivity_amount', 'assets_amount', 'comment', 'assets_amount', 'date_created', 'date_updated', 'created_by',
'date_created', 'date_updated', 'created_by',
] ]
read_only_fields = ['date_created', 'date_updated', 'created_by', 'assets_amount']
extra_kwargs = { extra_kwargs = {
'password': {"write_only": True}, 'password': {"write_only": True},
'private_key': {"write_only": True}, 'private_key': {"write_only": True},
'public_key': {"write_only": True}, 'public_key': {"write_only": True},
'date_created': {'read_only': True},
'date_updated': {'read_only': True},
'created_by': {'read_only': True},
'assets_amount': {'label': _('Asset')}, 'assets_amount': {'label': _('Asset')},
'connectivity_amount': {'label': _('Connectivity')},
} }
......
...@@ -10,13 +10,19 @@ from orgs.mixins import BulkOrgResourceModelSerializer ...@@ -10,13 +10,19 @@ from orgs.mixins import BulkOrgResourceModelSerializer
class CommandFilterSerializer(BulkOrgResourceModelSerializer): class CommandFilterSerializer(BulkOrgResourceModelSerializer):
rules = serializers.PrimaryKeyRelatedField(queryset=CommandFilterRule.objects.all(), many=True)
system_users = serializers.PrimaryKeyRelatedField(queryset=SystemUser.objects.all(), many=True)
class Meta: class Meta:
model = CommandFilter model = CommandFilter
list_serializer_class = AdaptedBulkListSerializer list_serializer_class = AdaptedBulkListSerializer
fields = '__all__' fields = [
'id', 'name', 'org_id', 'org_name', 'is_active', 'comment',
'created_by', 'date_created', 'date_updated', 'rules', 'system_users'
]
extra_kwargs = {
'rules': {'read_only': True},
'system_users': {'read_only': True}
}
class CommandFilterRuleSerializer(BulkOrgResourceModelSerializer): class CommandFilterRuleSerializer(BulkOrgResourceModelSerializer):
......
...@@ -6,6 +6,7 @@ from common.serializers import AdaptedBulkListSerializer ...@@ -6,6 +6,7 @@ from common.serializers import AdaptedBulkListSerializer
from orgs.mixins import BulkOrgResourceModelSerializer from orgs.mixins import BulkOrgResourceModelSerializer
from ..models import Domain, Gateway from ..models import Domain, Gateway
from .base import AuthSerializerMixin
class DomainSerializer(BulkOrgResourceModelSerializer): class DomainSerializer(BulkOrgResourceModelSerializer):
...@@ -14,7 +15,11 @@ class DomainSerializer(BulkOrgResourceModelSerializer): ...@@ -14,7 +15,11 @@ class DomainSerializer(BulkOrgResourceModelSerializer):
class Meta: class Meta:
model = Domain model = Domain
fields = '__all__' fields = [
'id', 'name', 'asset_count', 'gateway_count', 'comment', 'assets',
'date_created'
]
read_only_fields = ( 'asset_count', 'gateway_count', 'date_created')
list_serializer_class = AdaptedBulkListSerializer list_serializer_class = AdaptedBulkListSerializer
@staticmethod @staticmethod
...@@ -26,14 +31,14 @@ class DomainSerializer(BulkOrgResourceModelSerializer): ...@@ -26,14 +31,14 @@ class DomainSerializer(BulkOrgResourceModelSerializer):
return obj.gateway_set.all().count() return obj.gateway_set.all().count()
class GatewaySerializer(BulkOrgResourceModelSerializer): class GatewaySerializer(AuthSerializerMixin, BulkOrgResourceModelSerializer):
class Meta: class Meta:
model = Gateway model = Gateway
list_serializer_class = AdaptedBulkListSerializer list_serializer_class = AdaptedBulkListSerializer
fields = [ fields = [
'id', 'name', 'ip', 'port', 'protocol', 'username', 'id', 'name', 'ip', 'port', 'protocol', 'username', 'password',
'domain', 'is_active', 'date_created', 'date_updated', 'private_key', 'public_key', 'domain', 'is_active', 'date_created',
'created_by', 'comment', 'date_updated', 'created_by', 'comment',
] ]
......
...@@ -13,7 +13,13 @@ class LabelSerializer(BulkOrgResourceModelSerializer): ...@@ -13,7 +13,13 @@ class LabelSerializer(BulkOrgResourceModelSerializer):
class Meta: class Meta:
model = Label model = Label
fields = '__all__' fields = [
'id', 'name', 'value', 'category', 'is_active', 'comment',
'date_created', 'asset_count', 'assets', 'get_category_display'
]
read_only_fields = (
'category', 'date_created', 'asset_count', 'get_category_display'
)
list_serializer_class = AdaptedBulkListSerializer list_serializer_class = AdaptedBulkListSerializer
@staticmethod @staticmethod
......
...@@ -17,9 +17,8 @@ class NodeSerializer(BulkOrgResourceModelSerializer): ...@@ -17,9 +17,8 @@ class NodeSerializer(BulkOrgResourceModelSerializer):
class Meta: class Meta:
model = Node model = Node
fields = [ only_fields = ['id', 'key', 'value', 'org_id']
'id', 'key', 'value', 'assets_amount', 'org_id', fields = only_fields + ['assets_amount']
]
read_only_fields = [ read_only_fields = [
'key', 'assets_amount', 'org_id', 'key', 'assets_amount', 'org_id',
] ]
......
...@@ -21,14 +21,13 @@ class SystemUserSerializer(AuthSerializerMixin, BulkOrgResourceModelSerializer): ...@@ -21,14 +21,13 @@ class SystemUserSerializer(AuthSerializerMixin, BulkOrgResourceModelSerializer):
'id', 'name', 'username', 'password', 'public_key', 'private_key', 'id', 'name', 'username', 'password', 'public_key', 'private_key',
'login_mode', 'login_mode_display', 'priority', 'protocol', 'login_mode', 'login_mode_display', 'priority', 'protocol',
'auto_push', 'cmd_filters', 'sudo', 'shell', 'comment', 'nodes', 'auto_push', 'cmd_filters', 'sudo', 'shell', 'comment', 'nodes',
'assets_amount', 'connectivity_amount', 'auto_generate_key' 'assets_amount', 'auto_generate_key'
] ]
extra_kwargs = { extra_kwargs = {
'password': {"write_only": True}, 'password': {"write_only": True},
'public_key': {"write_only": True}, 'public_key': {"write_only": True},
'private_key': {"write_only": True}, 'private_key': {"write_only": True},
'assets_amount': {'label': _('Asset')}, 'assets_amount': {'label': _('Asset')},
'connectivity_amount': {'label': _('Connectivity')},
'login_mode_display': {'label': _('Login mode display')}, 'login_mode_display': {'label': _('Login mode display')},
'created_by': {'read_only': True}, 'created_by': {'read_only': True},
} }
......
...@@ -78,6 +78,7 @@ def on_system_user_assets_change(sender, instance=None, **kwargs): ...@@ -78,6 +78,7 @@ def on_system_user_assets_change(sender, instance=None, **kwargs):
@receiver(m2m_changed, sender=Asset.nodes.through) @receiver(m2m_changed, sender=Asset.nodes.through)
def on_asset_node_changed(sender, instance=None, **kwargs): def on_asset_node_changed(sender, instance=None, **kwargs):
logger.debug("Asset nodes change signal received") logger.debug("Asset nodes change signal received")
Asset.expire_all_nodes_keys_cache()
if isinstance(instance, Asset): if isinstance(instance, Asset):
if kwargs['action'] == 'pre_remove': if kwargs['action'] == 'pre_remove':
nodes = kwargs['model'].objects.filter(pk__in=kwargs['pk_set']) nodes = kwargs['model'].objects.filter(pk__in=kwargs['pk_set'])
......
...@@ -70,7 +70,7 @@ function showAuth() { ...@@ -70,7 +70,7 @@ function showAuth() {
var msg = "{% trans 'Get auth info error' %}"; var msg = "{% trans 'Get auth info error' %}";
toastr.error(msg) toastr.error(msg)
}; };
APIUpdateAttr({ requestApi({
url: url, url: url,
method: "GET", method: "GET",
success: success, success: success,
......
...@@ -141,7 +141,7 @@ $(document).ready(function(){ ...@@ -141,7 +141,7 @@ $(document).ready(function(){
var url = '{% url "ops:celery-task-log" pk=DEFAULT_PK %}'.replace("{{ DEFAULT_PK }}", task_id); var url = '{% url "ops:celery-task-log" pk=DEFAULT_PK %}'.replace("{{ DEFAULT_PK }}", task_id);
window.open(url, '', 'width=800,height=600,left=400,top=400') window.open(url, '', 'width=800,height=600,left=400,top=400')
}; };
APIUpdateAttr({ requestApi({
url: the_url, url: the_url,
method: 'GET', method: 'GET',
success: success, success: success,
......
...@@ -235,7 +235,7 @@ function onRename(event, treeId, treeNode, isCancel){ ...@@ -235,7 +235,7 @@ function onRename(event, treeId, treeNode, isCancel){
if (isCancel){ if (isCancel){
return return
} }
APIUpdateAttr({ requestApi({
url: url, url: url,
body: JSON.stringify(data), body: JSON.stringify(data),
method: "PATCH", method: "PATCH",
...@@ -274,7 +274,7 @@ function onDrop(event, treeId, treeNodes, targetNode, moveType) { ...@@ -274,7 +274,7 @@ function onDrop(event, treeId, treeNodes, targetNode, moveType) {
var the_url = "{% url 'api-assets:node-add-children' pk=DEFAULT_PK %}".replace("{{ DEFAULT_PK }}", targetNode.meta.node.id); var the_url = "{% url 'api-assets:node-add-children' pk=DEFAULT_PK %}".replace("{{ DEFAULT_PK }}", targetNode.meta.node.id);
var body = {nodes: treeNodesIds}; var body = {nodes: treeNodesIds};
APIUpdateAttr({ requestApi({
url: the_url, url: the_url,
method: "PUT", method: "PUT",
body: JSON.stringify(body) body: JSON.stringify(body)
...@@ -291,42 +291,6 @@ function defaultCallback(action) { ...@@ -291,42 +291,6 @@ function defaultCallback(action) {
$(document).ready(function () { $(document).ready(function () {
}) })
.on('click', '.btn-refresh-hardware', function () {
var url = "{% url 'api-assets:node-refresh-hardware-info' pk=DEFAULT_PK %}";
var the_url = url.replace("{{ DEFAULT_PK }}", current_node_id);
function success(data) {
rMenu.css({"visibility" : "hidden"});
var task_id = data.task;
var url = '{% url "ops:celery-task-log" pk=DEFAULT_PK %}'.replace("{{ DEFAULT_PK }}", task_id);
window.open(url, '', 'width=800,height=600')
}
APIUpdateAttr({
url: the_url,
method: "GET",
success: success,
flash_message: false
});
})
.on('click', '.btn-test-connective', function () {
var url = "{% url 'api-assets:node-test-connective' pk=DEFAULT_PK %}";
if (!current_node_id) {
return null;
}
var the_url = url.replace("{{ DEFAULT_PK }}", current_node_id);
function success(data) {
rMenu.css({"visibility" : "hidden"});
var task_id = data.task;
var url = '{% url "ops:celery-task-log" pk=DEFAULT_PK %}'.replace("{{ DEFAULT_PK }}", task_id);
window.open(url, '', 'width=800,height=600')
}
APIUpdateAttr({
url: the_url,
method: "GET",
success: success,
flash_message: false
});
})
.on('click', '.btn-show-current-asset', function(){ .on('click', '.btn-show-current-asset', function(){
hideRMenu(); hideRMenu();
$(this).css('display', 'none'); $(this).css('display', 'none');
...@@ -341,17 +305,5 @@ $(document).ready(function () { ...@@ -341,17 +305,5 @@ $(document).ready(function () {
setCookie('show_current_asset', ''); setCookie('show_current_asset', '');
location.reload(); location.reload();
}) })
.on('click', '.btn-test-connective', function () {
hideRMenu();
})
.on('click', '#menu_refresh_assets_amount', function () {
hideRMenu();
var url = "{% url 'api-assets:refresh-assets-amount' %}";
APIUpdateAttr({
'url': url,
'method': 'GET'
});
window.location.reload();
})
</script> </script>
\ No newline at end of file
...@@ -228,6 +228,7 @@ $(document).ready(function () { ...@@ -228,6 +228,7 @@ $(document).ready(function () {
var form = $("form"); var form = $("form");
var data = form.serializeObject(); var data = form.serializeObject();
objectAttrsIsList(data, ['cmd_filters']);
objectAttrsIsBool(data, ["auto_generate_key", "auto_push"]); objectAttrsIsBool(data, ["auto_generate_key", "auto_push"]);
data["private_key"] = $("#id_private_key_file").data('file'); data["private_key"] = $("#id_private_key_file").data('file');
......
...@@ -88,7 +88,7 @@ $(document).ready(function () { ...@@ -88,7 +88,7 @@ $(document).ready(function () {
var url = '{% url "ops:celery-task-log" pk=DEFAULT_PK %}'.replace("{{ DEFAULT_PK }}", task_id); var url = '{% url "ops:celery-task-log" pk=DEFAULT_PK %}'.replace("{{ DEFAULT_PK }}", task_id);
window.open(url, '', 'width=800,height=600,left=400,top=400') window.open(url, '', 'width=800,height=600,left=400,top=400')
}; };
APIUpdateAttr({ requestApi({
url: the_url, url: the_url,
method: 'GET', method: 'GET',
success: success, success: success,
......
...@@ -131,7 +131,7 @@ function replaceNodeAssetsAdminUser(nodes) { ...@@ -131,7 +131,7 @@ function replaceNodeAssetsAdminUser(nodes) {
// clear jumpserver.groups_selected // clear jumpserver.groups_selected
jumpserver.nodes_selected = {}; jumpserver.nodes_selected = {};
}; };
APIUpdateAttr({ requestApi({
url: the_url, url: the_url,
body: JSON.stringify(body), body: JSON.stringify(body),
success: success success: success
......
...@@ -2,8 +2,6 @@ ...@@ -2,8 +2,6 @@
{% load i18n static %} {% load i18n static %}
{% block help_message %} {% block help_message %}
<div class="alert alert-info help-message"> <div class="alert alert-info help-message">
{# 管理用户是资产(被控服务器)上的root,或拥有 NOPASSWD: ALL sudo权限的用户,Jumpserver使用该用户来 `推送系统用户`、`获取资产硬件信息`等。#}
{# Windows或其它硬件可以随意设置一个#}
{% trans 'Admin users are asset (charged server) on the root, or have NOPASSWD: ALL sudo permissions users, '%} {% trans 'Admin users are asset (charged server) on the root, or have NOPASSWD: ALL sudo permissions users, '%}
{% trans 'Jumpserver users of the system using the user to `push system user`, `get assets hardware information`, etc. '%} {% trans 'Jumpserver users of the system using the user to `push system user`, `get assets hardware information`, etc. '%}
{% trans 'You can set any one for Windows or other hardware.' %} {% trans 'You can set any one for Windows or other hardware.' %}
...@@ -47,9 +45,9 @@ ...@@ -47,9 +45,9 @@
<th class="text-center">{% trans 'Name' %}</th> <th class="text-center">{% trans 'Name' %}</th>
<th class="text-center">{% trans 'Username' %}</th> <th class="text-center">{% trans 'Username' %}</th>
<th class="text-center">{% trans 'Asset' %}</th> <th class="text-center">{% trans 'Asset' %}</th>
<th class="text-center">{% trans 'Reachable' %}</th> {# <th class="text-center">{% trans 'Reachable' %}</th>#}
<th class="text-center">{% trans 'Unreachable' %}</th> {# <th class="text-center">{% trans 'Unreachable' %}</th>#}
<th class="text-center">{% trans 'Ratio' %}</th> {# <th class="text-center">{% trans 'Ratio' %}</th>#}
<th class="text-center">{% trans 'Comment' %}</th> <th class="text-center">{% trans 'Comment' %}</th>
<th class="text-center">{% trans 'Action' %}</th> <th class="text-center">{% trans 'Action' %}</th>
</tr> </tr>
...@@ -73,44 +71,44 @@ function initTable() { ...@@ -73,44 +71,44 @@ function initTable() {
var detail_btn = '<a href="{% url "assets:admin-user-detail" pk=DEFAULT_PK %}">' + cellData + '</a>'; var detail_btn = '<a href="{% url "assets:admin-user-detail" pk=DEFAULT_PK %}">' + cellData + '</a>';
return detail_btn.replace('{{ DEFAULT_PK }}', rowData.id); return detail_btn.replace('{{ DEFAULT_PK }}', rowData.id);
}}, }},
{targets: 4, createdCell: function (td, cellData) { {#{targets: 4, createdCell: function (td, cellData) {#}
var innerHtml = ""; {# var innerHtml = "";#}
var data = cellData.reachable; {# var data = cellData.reachable;#}
if (data !== 0) { {# if (data !== 0) {#}
innerHtml = "<span class='text-navy'>" + data + "</span>"; {# innerHtml = "<span class='text-navy'>" + data + "</span>";#}
} else { {# } else {#}
innerHtml = "<span>" + data + "</span>"; {# innerHtml = "<span>" + data + "</span>";#}
} {# }#}
$(td).html(innerHtml) {# $(td).html(innerHtml)#}
}}, {#}},#}
{targets: 5, createdCell: function (td, cellData) { {#{targets: 5, createdCell: function (td, cellData) {#}
var data = cellData.unreachable; {# var data = cellData.unreachable;#}
var innerHtml = ""; {# var innerHtml = "";#}
if (data !== 0) { {# if (data !== 0) {#}
innerHtml = "<span class='text-danger'>" + data + "</span>"; {# innerHtml = "<span class='text-danger'>" + data + "</span>";#}
} else { {# } else {#}
innerHtml = "<span>" + data + "</span>"; {# innerHtml = "<span>" + data + "</span>";#}
} {# }#}
$(td).html('<span href="javascript:void(0);" data-toggle="tooltip" title="' + data + '">' + innerHtml + '</span>'); {# $(td).html('<span href="javascript:void(0);" data-toggle="tooltip" title="' + data + '">' + innerHtml + '</span>');#}
}}, {#}},#}
{targets: 6, createdCell: function (td, cellData, rowData) { {#{targets: 6, createdCell: function (td, cellData, rowData) {#}
var val = 0; {# var val = 0;#}
var innerHtml = ""; {# var innerHtml = "";#}
var total = rowData.assets_amount; {# var total = rowData.assets_amount;#}
var reachable = cellData.reachable; {# var reachable = cellData.reachable;#}
if (total !== 0) { {# if (total !== 0) {#}
val = reachable/total * 100; {# val = reachable/total * 100;#}
} {# }#}
{##}
if (val === 100) { {# if (val === 100) {#}
innerHtml = "<span class='text-navy'>" + val + "% </span>"; {# innerHtml = "<span class='text-navy'>" + val + "% </span>";#}
} else { {# } else {#}
var num = new Number(val); {# var num = new Number(val);#}
innerHtml = "<span class='text-danger'>" + num.toFixed(1) + "% </span>"; {# innerHtml = "<span class='text-danger'>" + num.toFixed(1) + "% </span>";#}
} {# }#}
$(td).html('<span href="javascript:void(0);" data-toggle="tooltip" title="' + cellData + '">' + innerHtml + '</span>'); {# $(td).html('<span href="javascript:void(0);" data-toggle="tooltip" title="' + cellData + '">' + innerHtml + '</span>');#}
}}, {#}},#}
{targets: 8, createdCell: function (td, cellData, rowData) { {targets: 5, createdCell: function (td, cellData, rowData) {
var update_btn = '<a href="{% url "assets:admin-user-update" pk=DEFAULT_PK %}" class="btn btn-xs m-l-xs btn-info">{% trans "Update" %}</a>'.replace('{{ DEFAULT_PK }}', cellData); var update_btn = '<a href="{% url "assets:admin-user-update" pk=DEFAULT_PK %}" class="btn btn-xs m-l-xs btn-info">{% trans "Update" %}</a>'.replace('{{ DEFAULT_PK }}', cellData);
var del_btn = '<a class="btn btn-xs btn-danger m-l-xs btn_admin_user_delete" data-uid="{{ DEFAULT_PK }}">{% trans "Delete" %}</a>'.replace('{{ DEFAULT_PK }}', cellData); var del_btn = '<a class="btn btn-xs btn-danger m-l-xs btn_admin_user_delete" data-uid="{{ DEFAULT_PK }}">{% trans "Delete" %}</a>'.replace('{{ DEFAULT_PK }}', cellData);
$(td).html(update_btn + del_btn) $(td).html(update_btn + del_btn)
...@@ -118,7 +116,7 @@ function initTable() { ...@@ -118,7 +116,7 @@ function initTable() {
ajax_url: '{% url "api-assets:admin-user-list" %}', ajax_url: '{% url "api-assets:admin-user-list" %}',
columns: [ columns: [
{data: function(){return ""}}, {data: "name"}, {data: "username" }, {data: "assets_amount" }, {data: function(){return ""}}, {data: "name"}, {data: "username" }, {data: "assets_amount" },
{data: "connectivity_amount"}, {data: "connectivity_amount"}, {data: "connectivity_amount"}, {#{data: "connectivity_amount"}, {data: "connectivity_amount"}, {data: "connectivity_amount"},#}
{data: "comment"}, {data: "id"} {data: "comment"}, {data: "id"}
] ]
}; };
......
...@@ -84,7 +84,7 @@ $(document).ready(function () { ...@@ -84,7 +84,7 @@ $(document).ready(function () {
var url = '{% url "ops:celery-task-log" pk=DEFAULT_PK %}'.replace("{{ DEFAULT_PK }}", task_id); var url = '{% url "ops:celery-task-log" pk=DEFAULT_PK %}'.replace("{{ DEFAULT_PK }}", task_id);
window.open(url, '', 'width=800,height=600,left=400,top=400') window.open(url, '', 'width=800,height=600,left=400,top=400')
}; };
APIUpdateAttr({ requestApi({
url: the_url, url: the_url,
method: 'GET', method: 'GET',
success: success, success: success,
......
...@@ -137,7 +137,6 @@ $(document).ready(function () { ...@@ -137,7 +137,6 @@ $(document).ready(function () {
protocolRef.val(protocolShould); protocolRef.val(protocolShould);
protocolRef.trigger("change") protocolRef.trigger("change")
} }
}) })
.on("click", ".btn-protocol.btn-del", function () { .on("click", ".btn-protocol.btn-del", function () {
$(this).parent().parent().remove(); $(this).parent().parent().remove();
......
...@@ -70,7 +70,7 @@ ...@@ -70,7 +70,7 @@
</tr> </tr>
<tr> <tr>
<td>{% trans 'Protocol' %}</td> <td>{% trans 'Protocol' %}</td>
<td>{{ asset.protocols }}</td> <td><b>{{ asset.protocols }}</b></td>
</tr> </tr>
<tr> <tr>
<td>{% trans 'Admin user' %}:</td> <td>{% trans 'Admin user' %}:</td>
...@@ -267,7 +267,7 @@ function updateAssetNodes(nodes) { ...@@ -267,7 +267,7 @@ function updateAssetNodes(nodes) {
// clear jumpserver.groups_selected // clear jumpserver.groups_selected
jumpserver.nodes_selected = {}; jumpserver.nodes_selected = {};
}; };
APIUpdateAttr({ requestApi({
url: the_url, url: the_url,
body: JSON.stringify(body), body: JSON.stringify(body),
success: success success: success
...@@ -282,7 +282,7 @@ function refreshAssetHardware() { ...@@ -282,7 +282,7 @@ function refreshAssetHardware() {
var url = '{% url "ops:celery-task-log" pk=DEFAULT_PK %}'.replace("{{ DEFAULT_PK }}", task_id); var url = '{% url "ops:celery-task-log" pk=DEFAULT_PK %}'.replace("{{ DEFAULT_PK }}", task_id);
window.open(url, '', 'width=800,height=600') window.open(url, '', 'width=800,height=600')
}; };
APIUpdateAttr({ requestApi({
url: the_url, url: the_url,
success: success, success: success,
method: 'GET' method: 'GET'
...@@ -306,7 +306,7 @@ $(document).ready(function () { ...@@ -306,7 +306,7 @@ $(document).ready(function () {
}; };
var success = '{% trans "Update successfully!" %}'; var success = '{% trans "Update successfully!" %}';
var status = $(".ibox-content > table > tbody > tr:nth-child(13) > td:last >b").text(); var status = $(".ibox-content > table > tbody > tr:nth-child(13) > td:last >b").text();
APIUpdateAttr({ requestApi({
url: the_url, url: the_url,
body: JSON.stringify(body), body: JSON.stringify(body),
success_message: success success_message: success
...@@ -360,7 +360,7 @@ $(document).ready(function () { ...@@ -360,7 +360,7 @@ $(document).ready(function () {
window.open(url, '', 'width=800,height=600') window.open(url, '', 'width=800,height=600')
}; };
APIUpdateAttr({ requestApi({
url: the_url, url: the_url,
method: 'GET', method: 'GET',
success: success success: success
......
...@@ -167,7 +167,7 @@ function initTable() { ...@@ -167,7 +167,7 @@ function initTable() {
}}, }},
{targets: 5, createdCell: function (td, cellData, rowData) { {targets: 5, createdCell: function (td, cellData, rowData) {
var update_btn = '<a href="{% url "assets:asset-update" pk=DEFAULT_PK %}" class="btn btn-xs btn-info">{% trans "Update" %}</a>'.replace("{{ DEFAULT_PK }}", cellData); var update_btn = '<a href="{% url "assets:asset-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_asset_delete" data-uid="{{ DEFAULT_PK }}">{% trans "Delete" %}</a>'.replace('{{ DEFAULT_PK }}', cellData); var del_btn = '<a class="btn btn-xs btn-danger m-l-xs btn-asset-delete" data-uid="{{ DEFAULT_PK }}">{% trans "Delete" %}</a>'.replace('{{ DEFAULT_PK }}', cellData);
$(td).html(update_btn + del_btn) $(td).html(update_btn + del_btn)
}} }}
], ],
...@@ -325,8 +325,7 @@ $(document).ready(function(){ ...@@ -325,8 +325,7 @@ $(document).ready(function(){
} }
window.open(url, '_self'); window.open(url, '_self');
}) })
.on('click', '.btn-asset-delete', function () {
.on('click', '.btn_asset_delete', function () {
var $this = $(this); var $this = $(this);
var $data_table = $("#asset_list_table").DataTable(); var $data_table = $("#asset_list_table").DataTable();
var name = $(this).closest("tr").find(":nth-child(2)").children('a').html(); var name = $(this).closest("tr").find(":nth-child(2)").children('a').html();
...@@ -361,7 +360,7 @@ $(document).ready(function(){ ...@@ -361,7 +360,7 @@ $(document).ready(function(){
setTimeout( function () { setTimeout( function () {
window.location.reload();}, 500); window.location.reload();}, 500);
} }
APIUpdateAttr({ requestApi({
url: the_url, url: the_url,
method: 'PATCH', method: 'PATCH',
body: JSON.stringify(data), body: JSON.stringify(data),
...@@ -378,7 +377,7 @@ $(document).ready(function(){ ...@@ -378,7 +377,7 @@ $(document).ready(function(){
setTimeout( function () { setTimeout( function () {
window.location.reload();}, 300); window.location.reload();}, 300);
} }
APIUpdateAttr({ requestApi({
url: the_url, url: the_url,
method: 'PATCH', method: 'PATCH',
body: JSON.stringify(data), body: JSON.stringify(data),
...@@ -398,7 +397,7 @@ $(document).ready(function(){ ...@@ -398,7 +397,7 @@ $(document).ready(function(){
},function () { },function () {
function success(data) { function success(data) {
url = setUrlParam(the_url, 'spm', data.spm); url = setUrlParam(the_url, 'spm', data.spm);
APIUpdateAttr({ requestApi({
url:url, url:url,
method:'DELETE', method:'DELETE',
success:refreshTag, success:refreshTag,
...@@ -411,7 +410,7 @@ $(document).ready(function(){ ...@@ -411,7 +410,7 @@ $(document).ready(function(){
var msg = "{% trans 'Asset Deleting failed.' %}"; var msg = "{% trans 'Asset Deleting failed.' %}";
swal("{% trans 'Asset Delete' %}", msg, "error"); swal("{% trans 'Asset Delete' %}", msg, "error");
} }
APIUpdateAttr({ requestApi({
url: "{% url 'api-common:resources-cache' %}", url: "{% url 'api-common:resources-cache' %}",
method:'POST', method:'POST',
body:JSON.stringify(data), body:JSON.stringify(data),
...@@ -429,7 +428,7 @@ $(document).ready(function(){ ...@@ -429,7 +428,7 @@ $(document).ready(function(){
var url = "{% url 'assets:asset-bulk-update' %}"; var url = "{% url 'assets:asset-bulk-update' %}";
location.href= setUrlParam(url, 'spm', data.spm); location.href= setUrlParam(url, 'spm', data.spm);
} }
APIUpdateAttr({ requestApi({
url: "{% url 'api-common:resources-cache' %}", url: "{% url 'api-common:resources-cache' %}",
method:'POST', method:'POST',
body:JSON.stringify(data), body:JSON.stringify(data),
...@@ -453,7 +452,7 @@ $(document).ready(function(){ ...@@ -453,7 +452,7 @@ $(document).ready(function(){
asset_table.ajax.reload() asset_table.ajax.reload()
}; };
APIUpdateAttr({ requestApi({
'url': '/api/assets/v1/nodes/' + current_node_id + '/assets/remove/', 'url': '/api/assets/v1/nodes/' + current_node_id + '/assets/remove/',
'method': 'PUT', 'method': 'PUT',
'body': JSON.stringify(data), 'body': JSON.stringify(data),
...@@ -501,7 +500,7 @@ $(document).ready(function(){ ...@@ -501,7 +500,7 @@ $(document).ready(function(){
url = "{% url 'api-assets:node-add-assets' pk=DEFAULT_PK %}".replace("{{ DEFAULT_PK }}", current_node_id); url = "{% url 'api-assets:node-add-assets' pk=DEFAULT_PK %}".replace("{{ DEFAULT_PK }}", current_node_id);
} }
APIUpdateAttr({ requestApi({
'url': url, 'url': url,
'method': 'PUT', 'method': 'PUT',
'body': JSON.stringify(data), 'body': JSON.stringify(data),
...@@ -513,6 +512,40 @@ $(document).ready(function(){ ...@@ -513,6 +512,40 @@ $(document).ready(function(){
update_node_action = "add" update_node_action = "add"
}).on('click', '#menu_asset_move', function () { }).on('click', '#menu_asset_move', function () {
update_node_action = "move" update_node_action = "move"
}).on('click', '.btn-test-connective', function () {
var url = "{% url 'api-assets:node-test-connective' pk=DEFAULT_PK %}";
if (!current_node_id) {
return null;
}
var the_url = url.replace("{{ DEFAULT_PK }}", current_node_id);
function success(data) {
rMenu.css({"visibility" : "hidden"});
var task_id = data.task;
var url = '{% url "ops:celery-task-log" pk=DEFAULT_PK %}'.replace("{{ DEFAULT_PK }}", task_id);
window.open(url, '', 'width=800,height=600')
}
requestApi({
url: the_url,
method: "GET",
success: success,
flash_message: false
});
}).on('click', '.btn-refresh-hardware', function () {
var url = "{% url 'api-assets:node-refresh-hardware-info' pk=DEFAULT_PK %}";
var the_url = url.replace("{{ DEFAULT_PK }}", current_node_id);
function success(data) {
rMenu.css({"visibility" : "hidden"});
var task_id = data.task;
var url = '{% url "ops:celery-task-log" pk=DEFAULT_PK %}'.replace("{{ DEFAULT_PK }}", task_id);
window.open(url, '', 'width=800,height=600')
}
requestApi({
url: the_url,
method: "GET",
success: success,
flash_message: false
});
}) })
</script> </script>
......
...@@ -18,3 +18,29 @@ ...@@ -18,3 +18,29 @@
</div> </div>
</form> </form>
{% endblock %} {% endblock %}
{% block custom_foot_js %}
<script>
$(document).ready(function () {
})
.on("submit", "form", function (evt) {
evt.preventDefault();
var the_url = '{% url 'api-assets:cmd-filter-list' %}';
var redirect_to = '{% url "assets:cmd-filter-list" %}';
var method = "POST";
{% if type == "update" %}
the_url = '{% url 'api-assets:cmd-filter-detail' pk=object.id %}';
method = "PUT";
{% endif %}
var form = $("form");
var data = form.serializeObject();
var props = {
url: the_url,
data: data,
method: method,
form: form,
redirect_to: redirect_to
};
formSubmit(props);
})
</script>
{% endblock %}
\ No newline at end of file
...@@ -136,7 +136,7 @@ function updateCMDFilterSystemUsers(system_users) { ...@@ -136,7 +136,7 @@ function updateCMDFilterSystemUsers(system_users) {
var success = function(data) { var success = function(data) {
location.reload(); location.reload();
}; };
APIUpdateAttr({ requestApi({
url: the_url, url: the_url,
body: JSON.stringify(body), body: JSON.stringify(body),
method: 'PATCH', method: 'PATCH',
......
...@@ -70,5 +70,25 @@ $(document).ready(function(){ ...@@ -70,5 +70,25 @@ $(document).ready(function(){
content_help_ref.html(content_origin_help_text); content_help_ref.html(content_origin_help_text);
} }
}) })
.on("submit", "form", function (evt) {
evt.preventDefault();
var form = $("form");
var data = form.serializeObject();
var the_url = '{% url "api-assets:cmd-filter-rule-list" filter_pk=DEFAULT_PK %}'.replace("{{ DEFAULT_PK }}", data.filter);
var redirect_to = '{% url "assets:cmd-filter-rule-list" pk=DEFAULT_PK %}'.replace("{{ DEFAULT_PK }}", data.filter);
var method = "POST";
{% if request_type == "update" %}
the_url = '{% url "api-assets:cmd-filter-rule-detail" filter_pk=DEFAULT_PK pk=rule.id %}'.replace('{{ DEFAULT_PK }}', data.filter);
method = "PUT";
{% endif %}
var props = {
url: the_url,
data: data,
method: method,
form: form,
redirect_to: redirect_to
};
formSubmit(props);
})
</script> </script>
{% endblock %} {% endblock %}
\ No newline at end of file
...@@ -48,5 +48,26 @@ $(document).ready(function () { ...@@ -48,5 +48,26 @@ $(document).ready(function () {
$("#asset_list_modal").modal('hide'); $("#asset_list_modal").modal('hide');
}) })
.on("submit", "form", function (evt) {
evt.preventDefault();
var form = $("form");
var data = form.serializeObject();
var method = "POST";
var the_url = '{% url "api-assets:domain-list" %}';
var redirect_to = '{% url "assets:domain-list" %}';
{% if type == "update" %}
the_url = '{% url 'api-assets:domain-detail' pk=object.id %}';
method = "PUT";
{% endif %}
objectAttrsIsList(data, ['assets']);
var props = {
url:the_url,
data:data,
method:method,
form:form,
redirect_to:redirect_to
};
formSubmit(props);
})
</script> </script>
{% endblock %} {% endblock %}
\ No newline at end of file
...@@ -134,7 +134,7 @@ $(document).ready(function(){ ...@@ -134,7 +134,7 @@ $(document).ready(function(){
var data = $("#test_gateway_form").serializeObject(); var data = $("#test_gateway_form").serializeObject();
var uid = data.gateway_id; var uid = data.gateway_id;
var the_url = '{% url "api-assets:test-gateway-connective" pk=DEFAULT_PK %}'.replace('{{ DEFAULT_PK }}', uid); var the_url = '{% url "api-assets:test-gateway-connective" pk=DEFAULT_PK %}'.replace('{{ DEFAULT_PK }}', uid);
APIUpdateAttr({ requestApi({
url: the_url, url: the_url,
method: "POST", method: "POST",
body: JSON.stringify({'port': parseInt(data.port)}), body: JSON.stringify({'port': parseInt(data.port)}),
......
...@@ -95,6 +95,32 @@ function protocolChange() { ...@@ -95,6 +95,32 @@ function protocolChange() {
$(document).ready(function(){ $(document).ready(function(){
protocolChange(); protocolChange();
}) })
.on("submit", "form", function (evt) {
evt.preventDefault();
var form = $("form");
var data = form.serializeObject();
data["private_key"] = $("#id_private_key_file").data('file');
var method = "POST";
var the_url = '{% url "api-assets:gateway-list" %}';
var redirect_to = '{% url "assets:domain-gateway-list" pk=DEFAULT_PK %}'.replace("{{ DEFAULT_PK }}", data.domain);
{% if type == "update" %}
the_url = '{% url 'api-assets:gateway-detail' pk=object.id %}';
method = "PUT";
{% endif %}
var props = {
url:the_url,
data:data,
method:method,
form:form,
redirect_to:redirect_to
};
formSubmit(props);
})
.on('change', '#id_private_key_file', function () {
readFile($(this)).on("onload", function (evt, data) {
$(this).attr("data-file", data)
})
})
.on('change', protocol_id, function(){ .on('change', protocol_id, function(){
protocolChange(); protocolChange();
}); });
......
...@@ -51,5 +51,26 @@ $(document).ready(function () { ...@@ -51,5 +51,26 @@ $(document).ready(function () {
$('#id_assets').val(assets).trigger('change'); $('#id_assets').val(assets).trigger('change');
$("#asset_list_modal").modal('hide'); $("#asset_list_modal").modal('hide');
}) })
.on("submit", "form", function (evt) {
evt.preventDefault();
var the_url = '{% url 'api-assets:label-list' %}';
var redirect_to = '{% url "assets:label-list" %}';
var method = "POST";
{% if type == "update" %}
the_url = '{% url 'api-assets:label-detail' pk=object.id %}';
method = "PUT";
{% endif %}
var form = $("form");
var data = form.serializeObject();
objectAttrsIsList(data, ['assets']);
var props = {
url: the_url,
data: data,
method: method,
form: form,
redirect_to: redirect_to
};
formSubmit(props);
})
</script> </script>
{% endblock %} {% endblock %}
\ No newline at end of file
...@@ -146,7 +146,7 @@ function updateSystemUserNode(nodes) { ...@@ -146,7 +146,7 @@ function updateSystemUserNode(nodes) {
// clear jumpserver.nodes_selected // clear jumpserver.nodes_selected
jumpserver.nodes_selected = {}; jumpserver.nodes_selected = {};
}; };
APIUpdateAttr({ requestApi({
url: the_url, url: the_url,
body: JSON.stringify(body), body: JSON.stringify(body),
success: success success: success
...@@ -206,7 +206,7 @@ $(document).ready(function () { ...@@ -206,7 +206,7 @@ $(document).ready(function () {
var url = '{% url "ops:celery-task-log" pk=DEFAULT_PK %}'.replace("{{ DEFAULT_PK }}", task_id); var url = '{% url "ops:celery-task-log" pk=DEFAULT_PK %}'.replace("{{ DEFAULT_PK }}", task_id);
window.open(url, '', 'width=800,height=600,left=400,top=400') window.open(url, '', 'width=800,height=600,left=400,top=400')
}; };
APIUpdateAttr({ requestApi({
url: the_url, url: the_url,
error: error, error: error,
method: 'GET', method: 'GET',
...@@ -226,7 +226,7 @@ $(document).ready(function () { ...@@ -226,7 +226,7 @@ $(document).ready(function () {
var error = function (data) { var error = function (data) {
alert(data) alert(data)
}; };
APIUpdateAttr({ requestApi({
url: the_url, url: the_url,
method: 'GET', method: 'GET',
success: success, success: success,
...@@ -243,7 +243,7 @@ $(document).ready(function () { ...@@ -243,7 +243,7 @@ $(document).ready(function () {
var url = '{% url "ops:celery-task-log" pk=DEFAULT_PK %}'.replace("{{ DEFAULT_PK }}", task_id); var url = '{% url "ops:celery-task-log" pk=DEFAULT_PK %}'.replace("{{ DEFAULT_PK }}", task_id);
window.open(url, '', 'width=800,height=600,left=400,top=400') window.open(url, '', 'width=800,height=600,left=400,top=400')
}; };
APIUpdateAttr({ requestApi({
url: the_url, url: the_url,
error: error, error: error,
method: 'GET', method: 'GET',
......
...@@ -212,7 +212,7 @@ function updateCommandFilters(command_filters) { ...@@ -212,7 +212,7 @@ function updateCommandFilters(command_filters) {
var success = function(data) { var success = function(data) {
location.reload(); location.reload();
}; };
APIUpdateAttr({ requestApi({
url: the_url, url: the_url,
body: JSON.stringify(body), body: JSON.stringify(body),
success: success success: success
...@@ -235,7 +235,7 @@ $(document).ready(function () { ...@@ -235,7 +235,7 @@ $(document).ready(function () {
var body = { var body = {
'auto_push': checked 'auto_push': checked
}; };
APIUpdateAttr({ requestApi({
url: the_url, url: the_url,
body: JSON.stringify(body) body: JSON.stringify(body)
}); });
...@@ -254,7 +254,7 @@ $(document).ready(function () { ...@@ -254,7 +254,7 @@ $(document).ready(function () {
var url = '{% url "ops:celery-task-log" pk=DEFAULT_PK %}'.replace("{{ DEFAULT_PK }}", task_id); var url = '{% url "ops:celery-task-log" pk=DEFAULT_PK %}'.replace("{{ DEFAULT_PK }}", task_id);
window.open(url, '', 'width=800,height=600,left=400,top=400') window.open(url, '', 'width=800,height=600,left=400,top=400')
}; };
APIUpdateAttr({ requestApi({
url: the_url, url: the_url,
method: 'GET', method: 'GET',
success: success, success: success,
...@@ -268,7 +268,7 @@ $(document).ready(function () { ...@@ -268,7 +268,7 @@ $(document).ready(function () {
var url = '{% url "ops:celery-task-log" pk=DEFAULT_PK %}'.replace("{{ DEFAULT_PK }}", task_id); var url = '{% url "ops:celery-task-log" pk=DEFAULT_PK %}'.replace("{{ DEFAULT_PK }}", task_id);
window.open(url, '', 'width=800,height=600') window.open(url, '', 'width=800,height=600')
}; };
APIUpdateAttr({ requestApi({
url: the_url, url: the_url,
method: 'GET', method: 'GET',
success: success, success: success,
......
...@@ -53,9 +53,9 @@ ...@@ -53,9 +53,9 @@
<th class="text-center">{% trans 'Protocol' %}</th> <th class="text-center">{% trans 'Protocol' %}</th>
<th class="text-center">{% trans 'Login mode' %}</th> <th class="text-center">{% trans 'Login mode' %}</th>
<th class="text-center">{% trans 'Asset' %}</th> <th class="text-center">{% trans 'Asset' %}</th>
<th class="text-center">{% trans 'Reachable' %}</th> {# <th class="text-center">{% trans 'Reachable' %}</th>#}
<th class="text-center">{% trans 'Unreachable' %}</th> {# <th class="text-center">{% trans 'Unreachable' %}</th>#}
<th class="text-center">{% trans 'Ratio' %}</th> {# <th class="text-center">{% trans 'Ratio' %}</th>#}
<th class="text-center">{% trans 'Comment' %}</th> <th class="text-center">{% trans 'Comment' %}</th>
<th class="text-center">{% trans 'Action' %}</th> <th class="text-center">{% trans 'Action' %}</th>
</tr> </tr>
...@@ -78,44 +78,44 @@ function initTable() { ...@@ -78,44 +78,44 @@ function initTable() {
var detail_btn = '<a href="{% url "assets:system-user-detail" pk=DEFAULT_PK %}">' + cellData + '</a>'; var detail_btn = '<a href="{% url "assets:system-user-detail" pk=DEFAULT_PK %}">' + cellData + '</a>';
$(td).html(detail_btn.replace('{{ DEFAULT_PK }}', rowData.id)); $(td).html(detail_btn.replace('{{ DEFAULT_PK }}', rowData.id));
}}, }},
{targets: 6, createdCell: function (td, cellData) { {#{targets: 6, createdCell: function (td, cellData) {#}
var innerHtml = ""; {# var innerHtml = "";#}
var data = cellData.reachable; {# var data = cellData.reachable;#}
if (data !== 0) { {# if (data !== 0) {#}
innerHtml = "<span class='text-navy'>" + data + "</span>"; {# innerHtml = "<span class='text-navy'>" + data + "</span>";#}
} else { {# } else {#}
innerHtml = "<span>" + data + "</span>"; {# innerHtml = "<span>" + data + "</span>";#}
} {# }#}
$(td).html(innerHtml) {# $(td).html(innerHtml)#}
}}, {#}},#}
{targets: 7, createdCell: function (td, cellData) { {#{targets: 7, createdCell: function (td, cellData) {#}
var data = cellData.unreachable; {# var data = cellData.unreachable;#}
var innerHtml = ""; {# var innerHtml = "";#}
if (data !== 0) { {# if (data !== 0) {#}
innerHtml = "<span class='text-danger'>" + data + "</span>"; {# innerHtml = "<span class='text-danger'>" + data + "</span>";#}
} else { {# } else {#}
innerHtml = "<span>" + data + "</span>"; {# innerHtml = "<span>" + data + "</span>";#}
} {# }#}
$(td).html('<span href="javascript:void(0);" data-toggle="tooltip" title="' + data + '">' + innerHtml + '</span>'); {# $(td).html('<span href="javascript:void(0);" data-toggle="tooltip" title="' + data + '">' + innerHtml + '</span>');#}
}}, {#}},#}
{targets: 8, createdCell: function (td, cellData, rowData) { {#{targets: 8, createdCell: function (td, cellData, rowData) {#}
var val = 0; {# var val = 0;#}
var innerHtml = ""; {# var innerHtml = "";#}
var total = rowData.assets_amount; {# var total = rowData.assets_amount;#}
var reachable = cellData.reachable; {# var reachable = cellData.reachable;#}
if (total && total !== 0) { {# if (total && total !== 0) {#}
val = reachable/total * 100; {# val = reachable/total * 100;#}
} {# }#}
{##}
if (val === 100) { {# if (val === 100) {#}
innerHtml = "<span class='text-navy'>" + val + "% </span>"; {# innerHtml = "<span class='text-navy'>" + val + "% </span>";#}
} else { {# } else {#}
var num = new Number(val); {# var num = new Number(val);#}
innerHtml = "<span class='text-danger'>" + num.toFixed(1) + "% </span>"; {# innerHtml = "<span class='text-danger'>" + num.toFixed(1) + "% </span>";#}
} {# }#}
$(td).html('<span href="javascript:void(0);" data-toggle="tooltip" title="' + cellData + '">' + innerHtml + '</span>'); {# $(td).html('<span href="javascript:void(0);" data-toggle="tooltip" title="' + cellData + '">' + innerHtml + '</span>');#}
}}, {#}},#}
{targets: 10, createdCell: function (td, cellData, rowData) { {targets: 7, createdCell: function (td, cellData, rowData) {
var update_btn = '<a href="{% url "assets:system-user-update" pk=DEFAULT_PK %}" class="btn btn-xs m-l-xs btn-info">{% trans "Update" %}</a>'.replace('{{ DEFAULT_PK }}', cellData); var update_btn = '<a href="{% url "assets:system-user-update" pk=DEFAULT_PK %}" class="btn btn-xs m-l-xs btn-info">{% trans "Update" %}</a>'.replace('{{ DEFAULT_PK }}', cellData);
var del_btn = '<a class="btn btn-xs btn-danger m-l-xs btn_admin_user_delete" data-uid="{{ DEFAULT_PK }}">{% trans "Delete" %}</a>'.replace('{{ DEFAULT_PK }}', cellData); var del_btn = '<a class="btn btn-xs btn-danger m-l-xs btn_admin_user_delete" data-uid="{{ DEFAULT_PK }}">{% trans "Delete" %}</a>'.replace('{{ DEFAULT_PK }}', cellData);
$(td).html(update_btn + del_btn) $(td).html(update_btn + del_btn)
...@@ -124,7 +124,7 @@ function initTable() { ...@@ -124,7 +124,7 @@ function initTable() {
ajax_url: '{% url "api-assets:system-user-list" %}', ajax_url: '{% url "api-assets:system-user-list" %}',
columns: [ columns: [
{data: "id" }, {data: "name" }, {data: "username" }, {data: "protocol"}, {data: "login_mode_display"}, {data: "assets_amount" }, {data: "id" }, {data: "name" }, {data: "username" }, {data: "protocol"}, {data: "login_mode_display"}, {data: "assets_amount" },
{data: "connectivity_amount"}, {data: "connectivity_amount"}, {data: "connectivity_amount"}, {data: "comment" }, {data: "id" } {data: "comment" }, {data: "id" }
], ],
op_html: $('#actions').html() op_html: $('#actions').html()
}; };
...@@ -182,7 +182,7 @@ $(document).ready(function(){ ...@@ -182,7 +182,7 @@ $(document).ready(function(){
swal("{% trans 'System Users Delete' %}", msg, "error"); swal("{% trans 'System Users Delete' %}", msg, "error");
}; };
var url_delete = the_url + '?id__in=' + JSON.stringify(plain_id_list); var url_delete = the_url + '?id__in=' + JSON.stringify(plain_id_list);
APIUpdateAttr({url: url_delete, method: 'DELETE', success: success, error: fail}); requestApi({url: url_delete, method: 'DELETE', success: success, error: fail});
$data_table.ajax.reload(); $data_table.ajax.reload();
jumpserver.checked = false; jumpserver.checked = false;
}); });
......
...@@ -11,48 +11,7 @@ ...@@ -11,48 +11,7 @@
{% block content %} {% block content %}
<div class="wrapper wrapper-content"> <div class="wrapper wrapper-content">
<div class="row"> <div class="row">
<div class="col-lg-3" id="split-left" style="padding-left: 3px"> {% include 'users/_granted_assets.html' %}
<div class="ibox float-e-margins">
<div class="ibox-content mailbox-content" style="padding-top: 0;padding-left: 1px">
<div class="file-manager ">
<div id="assetTree" class="ztree">
</div>
<div class="clearfix"></div>
</div>
</div>
</div>
</div>
<div class="col-lg-9 animated fadeInRight" id="split-right">
<div class="tree-toggle">
<div class="btn btn-sm btn-primary tree-toggle-btn" onclick="toggle()">
<i class="fa fa-angle-left fa-x" id="toggle-icon"></i>
</div>
</div>
<div class="mail-box-header">
<div class="btn-group" style="float: right">
<button data-toggle="dropdown" class="btn btn-default btn-sm dropdown-toggle">{% trans 'Label' %} <span class="caret"></span></button>
<ul class="dropdown-menu labels">
{% for label in labels %}
<li><a style="font-weight: bolder">{{ label.name }}:{{ label.value }}</a></li>
{% endfor %}
</ul>
</div>
<table class="table table-striped table-bordered table-hover " id="user_assets_table" style="width: 100%">
<thead>
<tr>
<th class="text-center"><input type="checkbox" class="ipt_check_all"></th>
<th class="text-center">{% trans 'Hostname' %}</th>
<th class="text-center">{% trans 'IP' %}</th>
<th class="text-center">{% trans 'Active' %}</th>
<th class="text-center">{% trans 'System users' %}</th>
<th class="text-center">{% trans 'Action' %}</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
</div>
</div> </div>
</div> </div>
...@@ -62,130 +21,52 @@ ...@@ -62,130 +21,52 @@
{% block custom_foot_js %} {% block custom_foot_js %}
<script> <script>
var treeUrl = "{% url 'api-perms:my-nodes-assets-as-tree' %}?show_assets=0&cache_policy=1"; var treeUrl = "{% url 'api-perms:my-nodes-as-tree' %}?&cache_policy=1";
var zTree, asset_table, show=0; var assetTableUrl = "{% url 'api-perms:my-assets' %}?cache_policy=1";
var inited = false; var selectUrl = '{% url "api-perms:my-node-assets" node_id=DEFAULT_PK %}?cache_policy=1';
var url; var showAssetHref = false; // Need input default true
var actions = {
targets: 4, createdCell: function (td, cellData) {
function initTable() { var conn_btn = '<a href="{% url "luna-view" %}?login_to=' + cellData +
if (inited){ '" class="btn btn-xs btn-primary">{% trans "Connect" %}</a>';
return $(td).html(conn_btn)
} else { }};
inited = true;
}
url = "{% url 'api-perms:my-assets' %}?cache_policy=1";
var options = {
ele: $('#user_assets_table'),
columnDefs: [
{targets: 1, createdCell: function (td, cellData, rowData) {
var detail_btn = '<a class="asset_detail" asset-id="rowData_id" data-toggle="modal" data-target="#user_asset_detail_modal" tabindex="0">'+ cellData +'</a>'
$(td).html(detail_btn.replace("rowData_id", rowData.id));
}},
{targets: 3, createdCell: function (td, cellData) {
if (!cellData) {
$(td).html('<i class="fa fa-times text-danger"></i>')
} else {
$(td).html('<i class="fa fa-check text-navy"></i>')
}
}},
{targets: 4, createdCell: function (td, cellData) {
var users = [];
$.each(cellData, function (id, data) {
users.push(data.name);
});
$(td).html(users.join(', '))
}},
{targets: 5, createdCell: function (td, cellData) {
var conn_btn = '<a href="{% url "luna-view" %}?login_to=' + cellData +'" class="btn btn-xs btn-primary">{% trans "Connect" %}</a>'.replace("{{ DEFAULT_PK }}", cellData);
$(td).html(conn_btn)
}}
],
ajax_url: url,
columns: [
{data: "id"}, {data: "hostname" }, {data: "ip" },
{data: "is_active", orderable: false },
{data: "system_users_granted", orderable: false},
{data: "id", orderable: false}
]
};
asset_table = jumpserver.initServerSideDataTable(options);
return asset_table
}
function onSelected(event, treeNode) {
url = '{% url "api-perms:my-node-assets" node_id=DEFAULT_PK %}?cache_policy=1';
var node_id = treeNode.meta.node.id;
url = url.replace("{{ DEFAULT_PK }}", node_id);
setCookie('node_selected', treeNode.id);
asset_table.ajax.url(url);
asset_table.ajax.reload();
}
function initTree() {
var setting = {
view: {
dblClickExpand: false,
showLine: true,
},
data: {
simpleData: {
enable: true
}
},
callback: {
onSelected: onSelected
}
};
var zNodes = [];
$.get(treeUrl, function(data, status){
zNodes = data;
$.fn.zTree.init($("#assetTree"), setting, zNodes);
zTree = $.fn.zTree.getZTreeObj("assetTree");
rootNodeAddDom(zTree, function () {
treeUrl = treeUrl.replace('cache_policy=1', 'cache_policy=2');
initTree();
});
});
}
$(document).ready(function () { $(document).ready(function () {
initTree(); initTree();
initTable(); initTable();
}).on('click', '.labels li', function () { }).on('click', '.labels li', function () {
var val = $(this).text(); var val = $(this).text();
$("#user_assets_table_filter input").val(val); $("#user_assets_table_filter input").val(val);
asset_table.search(val).draw(); assetTable.search(val).draw();
}) })
.on('click', '.asset_detail', function() { .on('click', '.asset-detail', function(e) {
var data = asset_table.ajax.json(); e.preventDefault();
var asset_id = this.getAttribute("asset-id"); var data = assetTable.ajax.json();
var assetId = $(this).data("asset");
var trs = ''; var trs = '';
var desc = { var desc = {
'hostname': "{% trans 'Hostname' %}", 'hostname': "{% trans 'Hostname' %}",
'ip': "{% trans 'IP' %}", 'ip': "{% trans 'IP' %}",
'port': "{% trans 'Port' %}", 'protocols': "{% trans 'Protocols' %}",
'protocol': "{% trans 'Protocol' %}",
'platform': "{% trans 'Platform' %}", 'platform': "{% trans 'Platform' %}",
'os': "{% trans 'OS' %}",
'system_users_join': "{% trans 'System user' %}", 'system_users_join': "{% trans 'System user' %}",
'domain': "{% trans 'Domain' %}", 'domain': "{% trans 'Domain' %}",
'is_active': "{% trans 'Is active' %}",
'comment': "{% trans 'Comment' %}"
{#'date_joined': "{% trans 'Date joined' %}",#}
}; };
$.each(data.results, function(index, value){ var value;
if(value.id === asset_id){ for (var i = 0; i < data.results.length; i++) {
value = data.results[i];
if(value.id === assetId){
for(var i in desc){ for(var i in desc){
trs += "<tr class='no-borders-tr'>\n" + trs += "<tr class='no-borders-tr'>\n" +
"<td>"+ desc[i] + ":</td>"+ "<td>"+ desc[i] + ":</td>"+
"<td><b>"+ (value[i] === null?'':value[i]) + "</b></td>\n" + "<td><b>"+ (value[i] === null?'':value[i]) + "</b></td>\n" +
"</tr>"; "</tr>";
} }
break
} }
}); };
$('#asset_detail_tbody').html(trs) $('#asset_detail_tbody').html(trs)
$('#user_asset_detail_modal').modal();
}); });
function toggle() { function toggle() {
...@@ -204,5 +85,4 @@ function toggle() { ...@@ -204,5 +85,4 @@ function toggle() {
} }
</script> </script>
{% endblock %} {% endblock %}
\ No newline at end of file
# ~*~ coding: utf-8 ~*~ # ~*~ coding: utf-8 ~*~
# #
import time import time
from django.db.models import Prefetch from functools import reduce
from django.db.models import Prefetch, Q
from common.utils import get_object_or_none, get_logger from common.utils import get_object_or_none, get_logger
from common.struct import Stack from common.struct import Stack
...@@ -21,24 +22,34 @@ def get_system_user_by_id(id): ...@@ -21,24 +22,34 @@ def get_system_user_by_id(id):
return system_user return system_user
class LabelFilter: class LabelFilterMixin:
def filter_queryset(self, queryset): def get_filter_labels_ids(self):
queryset = super().filter_queryset(queryset) query_params = self.request.query_params
query_keys = self.request.query_params.keys() query_keys = query_params.keys()
all_label_keys = Label.objects.values_list('name', flat=True) all_label_keys = Label.objects.values_list('name', flat=True)
valid_keys = set(all_label_keys) & set(query_keys) valid_keys = set(all_label_keys) & set(query_keys)
labels_query = {}
for key in valid_keys: if not valid_keys:
labels_query[key] = self.request.query_params.get(key) return []
conditions = [] labels_query = [
for k, v in labels_query.items(): {"name": key, "value": query_params[key]}
query = {'labels__name': k, 'labels__value': v} for key in valid_keys
conditions.append(query) ]
args = [Q(**kwargs) for kwargs in labels_query]
if conditions: args = reduce(lambda x, y: x | y, args)
for kwargs in conditions: labels_id = Label.objects.filter(args).values_list('id', flat=True)
queryset = queryset.filter(**kwargs) return labels_id
class LabelFilter(LabelFilterMixin):
def filter_queryset(self, queryset):
queryset = super().filter_queryset(queryset)
labels_ids = self.get_filter_labels_ids()
if not labels_ids:
return queryset
for labels_id in labels_ids:
queryset = queryset.filter(labels=labels_id)
return queryset return queryset
...@@ -104,7 +115,7 @@ class NodeUtil: ...@@ -104,7 +115,7 @@ class NodeUtil:
_node._assets_amount = len(_node._assets) _node._assets_amount = len(_node._assets)
delattr(_node, '_assets') delattr(_node, '_assets')
self.stack.top._children.append(_node) self.stack.top._children.append(_node)
self.stack.top._all_children.extend([_node] + _node._children) self.stack.top._all_children.extend([_node] + _node._all_children)
def init(self): def init(self):
all_nodes = self.get_all_nodes() all_nodes = self.get_all_nodes()
...@@ -145,29 +156,69 @@ class NodeUtil: ...@@ -145,29 +156,69 @@ class NodeUtil:
def nodes(self): def nodes(self):
return list(self._nodes.values()) return list(self._nodes.values())
def get_family_by_key(self, key):
tree_nodes = set()
node = self.get_node_by_key(key)
if not node:
return []
tree_nodes.update(node._parents)
tree_nodes.add(node)
tree_nodes.update(node._all_children)
return list(tree_nodes)
# 使用给定节点生成一颗树 # 使用给定节点生成一颗树
# 找到他们的祖先节点 # 找到他们的祖先节点
# 可选找到他们的子孙节点 # 可选找到他们的子孙节点
def get_family(self, nodes, with_children=False): def get_family(self, node):
tree_nodes = set() return self.get_family_by_key(node.key)
for n in nodes:
node = self.get_node_by_key(n.key) def get_family_keys_by_key(self, key):
if not node: nodes = self.get_family_by_key(key)
continue return [n.key for n in nodes]
tree_nodes.update(node._parents)
tree_nodes.add(node) def get_some_nodes_family_by_keys(self, keys):
if with_children: family = set()
tree_nodes.update(node._children) for key in keys:
return list(tree_nodes) family.update(self.get_family_by_key(key))
return family
def get_nodes_parents(self, nodes, with_self=True): def get_some_nodes_family_keys_by_keys(self, keys):
family = self.get_some_nodes_family_by_keys(keys)
return [n.key for n in family]
def get_nodes_parents_by_key(self, key, with_self=True):
parents = set() parents = set()
for n in nodes: node = self.get_node_by_key(key)
node = self.get_node_by_key(n.key) if not node:
parents.update(set(node._parents)) return []
if with_self: parents.update(set(node._parents))
parents.add(node) if with_self:
return parents parents.add(node)
return list(parents)
def get_node_parents(self, node, with_self=True):
return self.get_nodes_parents_by_key(node.key, with_self=with_self)
def get_nodes_parents_keys_by_key(self, key, with_self=True):
nodes = self.get_nodes_parents_by_key(key, with_self=with_self)
return [n.key for n in nodes]
def get_all_children_by_key(self, key, with_self=True):
children = set()
node = self.get_node_by_key(key)
if not node:
return []
children.update(set(node._all_children))
if with_self:
children.add(node)
return list(children)
def get_children(self, node, with_self=True):
return self.get_all_children_by_key(node.key, with_self=with_self)
def get_children_keys_by_key(self, key, with_self=True):
nodes = self.get_all_children_by_key(key, with_self=with_self)
return [n.key for n in nodes]
def test_node_tree(): def test_node_tree():
......
...@@ -69,7 +69,7 @@ class UserAssetListView(PermissionsMixin, TemplateView): ...@@ -69,7 +69,7 @@ class UserAssetListView(PermissionsMixin, TemplateView):
context = { context = {
'action': _('My assets'), 'action': _('My assets'),
'labels': Label.objects.all().order_by('name'), 'labels': Label.objects.all().order_by('name'),
'system_users': SystemUser.objects.all(), 'show_actions': True
} }
kwargs.update(context) kwargs.update(context)
return super().get_context_data(**kwargs) return super().get_context_data(**kwargs)
......
...@@ -47,6 +47,7 @@ class CommandFilterCreateView(PermissionsMixin, CreateView): ...@@ -47,6 +47,7 @@ class CommandFilterCreateView(PermissionsMixin, CreateView):
context = { context = {
'app': _('Assets'), 'app': _('Assets'),
'action': _('Create command filter'), 'action': _('Create command filter'),
'type': 'create'
} }
kwargs.update(context) kwargs.update(context)
return super().get_context_data(**kwargs) return super().get_context_data(**kwargs)
...@@ -64,6 +65,7 @@ class CommandFilterUpdateView(PermissionsMixin, UpdateView): ...@@ -64,6 +65,7 @@ class CommandFilterUpdateView(PermissionsMixin, UpdateView):
context = { context = {
'app': _('Assets'), 'app': _('Assets'),
'action': _('Update command filter'), 'action': _('Update command filter'),
'type': 'update'
} }
kwargs.update(context) kwargs.update(context)
return super().get_context_data(**kwargs) return super().get_context_data(**kwargs)
...@@ -136,6 +138,7 @@ class CommandFilterRuleCreateView(PermissionsMixin, CreateView): ...@@ -136,6 +138,7 @@ class CommandFilterRuleCreateView(PermissionsMixin, CreateView):
'app': _('Assets'), 'app': _('Assets'),
'action': _('Create command filter rule'), 'action': _('Create command filter rule'),
'object': self.cmd_filter, 'object': self.cmd_filter,
'request_type': 'create'
} }
kwargs.update(context) kwargs.update(context)
return super().get_context_data(**kwargs) return super().get_context_data(**kwargs)
...@@ -170,6 +173,8 @@ class CommandFilterRuleUpdateView(PermissionsMixin, UpdateView): ...@@ -170,6 +173,8 @@ class CommandFilterRuleUpdateView(PermissionsMixin, UpdateView):
'app': _('Assets'), 'app': _('Assets'),
'action': _('Update command filter rule'), 'action': _('Update command filter rule'),
'object': self.cmd_filter, 'object': self.cmd_filter,
'rule': self.get_object(),
'request_type': 'update'
} }
kwargs.update(context) kwargs.update(context)
return super().get_context_data(**kwargs) return super().get_context_data(**kwargs)
\ No newline at end of file
...@@ -46,6 +46,7 @@ class DomainCreateView(PermissionsMixin, CreateView): ...@@ -46,6 +46,7 @@ class DomainCreateView(PermissionsMixin, CreateView):
context = { context = {
'app': _('Assets'), 'app': _('Assets'),
'action': _('Create domain'), 'action': _('Create domain'),
'type': 'create'
} }
kwargs.update(context) kwargs.update(context)
return super().get_context_data(**kwargs) return super().get_context_data(**kwargs)
...@@ -63,6 +64,7 @@ class DomainUpdateView(PermissionsMixin, UpdateView): ...@@ -63,6 +64,7 @@ class DomainUpdateView(PermissionsMixin, UpdateView):
context = { context = {
'app': _('Assets'), 'app': _('Assets'),
'action': _('Update domain'), 'action': _('Update domain'),
'type': 'update'
} }
kwargs.update(context) kwargs.update(context)
return super().get_context_data(**kwargs) return super().get_context_data(**kwargs)
...@@ -132,6 +134,7 @@ class DomainGatewayCreateView(PermissionsMixin, CreateView): ...@@ -132,6 +134,7 @@ class DomainGatewayCreateView(PermissionsMixin, CreateView):
context = { context = {
'app': _('Assets'), 'app': _('Assets'),
'action': _('Create gateway'), 'action': _('Create gateway'),
'type': 'create'
} }
kwargs.update(context) kwargs.update(context)
return super().get_context_data(**kwargs) return super().get_context_data(**kwargs)
...@@ -152,6 +155,7 @@ class DomainGatewayUpdateView(PermissionsMixin, UpdateView): ...@@ -152,6 +155,7 @@ class DomainGatewayUpdateView(PermissionsMixin, UpdateView):
context = { context = {
'app': _('Assets'), 'app': _('Assets'),
'action': _('Update gateway'), 'action': _('Update gateway'),
"type": "update"
} }
kwargs.update(context) kwargs.update(context)
return super().get_context_data(**kwargs) return super().get_context_data(**kwargs)
...@@ -44,6 +44,7 @@ class LabelCreateView(PermissionsMixin, CreateView): ...@@ -44,6 +44,7 @@ class LabelCreateView(PermissionsMixin, CreateView):
context = { context = {
'app': _('Assets'), 'app': _('Assets'),
'action': _('Create label'), 'action': _('Create label'),
'type': 'create'
} }
kwargs.update(context) kwargs.update(context)
return super().get_context_data(**kwargs) return super().get_context_data(**kwargs)
...@@ -71,6 +72,7 @@ class LabelUpdateView(PermissionsMixin, UpdateView): ...@@ -71,6 +72,7 @@ class LabelUpdateView(PermissionsMixin, UpdateView):
context = { context = {
'app': _('Assets'), 'app': _('Assets'),
'action': _('Update label'), 'action': _('Update label'),
'type': 'update'
} }
kwargs.update(context) kwargs.update(context)
return super().get_context_data(**kwargs) return super().get_context_data(**kwargs)
......
...@@ -38,7 +38,7 @@ $(document).ready(function () { ...@@ -38,7 +38,7 @@ $(document).ready(function () {
var error = function () { var error = function () {
$("#mfa_error").addClass("text-danger").html(codeError); $("#mfa_error").addClass("text-danger").html(codeError);
}; };
APIUpdateAttr({ requestApi({
url: url, url: url,
method: "POST", method: "POST",
body: JSON.stringify(data), body: JSON.stringify(data),
......
...@@ -4,11 +4,13 @@ import os ...@@ -4,11 +4,13 @@ import os
import uuid import uuid
from django.core.cache import cache from django.core.cache import cache
from django.views.decorators.csrf import csrf_exempt
from rest_framework.views import APIView from rest_framework.views import APIView
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework import generics, serializers from rest_framework import generics, serializers
from .http import HttpResponseTemporaryRedirect
from .const import KEY_CACHE_RESOURCES_ID from .const import KEY_CACHE_RESOURCES_ID
__all__ = [ __all__ = [
...@@ -86,3 +88,11 @@ class ResourcesIDCacheApi(APIView): ...@@ -86,3 +88,11 @@ class ResourcesIDCacheApi(APIView):
cache_key = KEY_CACHE_RESOURCES_ID.format(spm) cache_key = KEY_CACHE_RESOURCES_ID.format(spm)
cache.set(cache_key, resources_id, 300) cache.set(cache_key, resources_id, 300)
return Response({'spm': spm}) return Response({'spm': spm})
@csrf_exempt
def redirect_plural_name_api(request, *args, **kwargs):
resource = kwargs.get("resource", "")
full_path = request.get_full_path()
full_path = full_path.replace(resource, resource+"s", 1)
return HttpResponseTemporaryRedirect(full_path)
# -*- coding: utf-8 -*-
#
from django.http import HttpResponse
from django.utils.encoding import iri_to_uri
class HttpResponseTemporaryRedirect(HttpResponse):
status_code = 307
def __init__(self, redirect_to):
HttpResponse.__init__(self)
self['Location'] = iri_to_uri(redirect_to)
...@@ -145,13 +145,13 @@ class NeedMFAVerify(permissions.BasePermission): ...@@ -145,13 +145,13 @@ class NeedMFAVerify(permissions.BasePermission):
return False return False
class CanUpdateSuperUser(permissions.BasePermission): class CanUpdateDeleteSuperUser(permissions.BasePermission):
def has_object_permission(self, request, view, obj): def has_object_permission(self, request, view, obj):
if request.method in ['GET', 'OPTIONS']: if request.method in ['GET', 'OPTIONS']:
return True return True
if str(request.user.id) == str(obj.id): elif request.method == 'DELETE' and str(request.user.id) == str(obj.id):
return False return False
if request.user.is_superuser: elif request.user.is_superuser:
return True return True
if hasattr(obj, 'is_superuser') and obj.is_superuser: if hasattr(obj, 'is_superuser') and obj.is_superuser:
return False return False
......
...@@ -46,12 +46,17 @@ class TreeNode: ...@@ -46,12 +46,17 @@ class TreeNode:
def __gt__(self, other): def __gt__(self, other):
if self.isParent and not other.isParent: if self.isParent and not other.isParent:
return False result = False
elif not self.isParent and other.isParent: elif not self.isParent and other.isParent:
return True result = True
if self.pId != other.pId: elif self.pId != other.pId:
return self.pId > other.pId result = self.pId > other.pId
return self.name > other.name else:
result = self.name > other.name
return result
def __le__(self, other):
return not self.__gt__(other)
def __eq__(self, other): def __eq__(self, other):
return self.id == other.id return self.id == other.id
...@@ -74,7 +79,7 @@ class Tree: ...@@ -74,7 +79,7 @@ class Tree:
raise ValueError("Parent must not be node parent") raise ValueError("Parent must not be node parent")
node.pId = parent.id node.pId = parent.id
parent.isParent = True parent.isParent = True
self.nodes[node.id] = node self.nodes[node.key] = node
def get_nodes(self): def get_nodes(self):
return sorted(self.nodes.values()) return sorted(self.nodes.values())
......
...@@ -7,6 +7,7 @@ import logging ...@@ -7,6 +7,7 @@ import logging
import datetime import datetime
import uuid import uuid
from functools import wraps from functools import wraps
import time
import copy import copy
import ipaddress import ipaddress
...@@ -179,3 +180,18 @@ def random_string(length): ...@@ -179,3 +180,18 @@ def random_string(length):
charset = string.ascii_letters + string.digits charset = string.ascii_letters + string.digits
s = [random.choice(charset) for i in range(length)] s = [random.choice(charset) for i in range(length)]
return ''.join(s) return ''.join(s)
logger = get_logger(__name__)
def timeit(func):
def wrapper(*args, **kwargs):
logger.debug("Start call: {}".format(func.__name__))
now = time.time()
result = func(*args, **kwargs)
using = (time.time() - now) * 1000
msg = "Call {} end, using: {:.1f}ms".format(func.__name__, using)
logger.debug(msg)
return result
return wrapper
...@@ -359,6 +359,7 @@ defaults = { ...@@ -359,6 +359,7 @@ defaults = {
'TERMINAL_TELNET_REGEX': '', 'TERMINAL_TELNET_REGEX': '',
'TERMINAL_COMMAND_STORAGE': {}, 'TERMINAL_COMMAND_STORAGE': {},
'SECURITY_MFA_AUTH': False, 'SECURITY_MFA_AUTH': False,
'SECURITY_SERVICE_ACCOUNT_REGISTRATION': True,
'SECURITY_LOGIN_LIMIT_COUNT': 7, 'SECURITY_LOGIN_LIMIT_COUNT': 7,
'SECURITY_LOGIN_LIMIT_TIME': 30, 'SECURITY_LOGIN_LIMIT_TIME': 30,
'SECURITY_MAX_IDLE_TIME': 30, 'SECURITY_MAX_IDLE_TIME': 30,
......
...@@ -568,7 +568,7 @@ SECURITY_PASSWORD_RULES = [ ...@@ -568,7 +568,7 @@ SECURITY_PASSWORD_RULES = [
'SECURITY_PASSWORD_SPECIAL_CHAR' 'SECURITY_PASSWORD_SPECIAL_CHAR'
] ]
SECURITY_MFA_VERIFY_TTL = CONFIG.SECURITY_MFA_VERIFY_TTL SECURITY_MFA_VERIFY_TTL = CONFIG.SECURITY_MFA_VERIFY_TTL
SECURITY_SERVICE_ACCOUNT_REGISTRATION = CONFIG.SECURITY_SERVICE_ACCOUNT_REGISTRATION
TERMINAL_PASSWORD_AUTH = CONFIG.TERMINAL_PASSWORD_AUTH TERMINAL_PASSWORD_AUTH = CONFIG.TERMINAL_PASSWORD_AUTH
TERMINAL_PUBLIC_KEY_AUTH = CONFIG.TERMINAL_PUBLIC_KEY_AUTH TERMINAL_PUBLIC_KEY_AUTH = CONFIG.TERMINAL_PUBLIC_KEY_AUTH
TERMINAL_HEARTBEAT_INTERVAL = CONFIG.TERMINAL_HEARTBEAT_INTERVAL TERMINAL_HEARTBEAT_INTERVAL = CONFIG.TERMINAL_HEARTBEAT_INTERVAL
......
...@@ -2,7 +2,7 @@ import datetime ...@@ -2,7 +2,7 @@ import datetime
import re import re
import time import time
from django.http import HttpResponse, HttpResponseRedirect from django.http import HttpResponseRedirect
from django.conf import settings from django.conf import settings
from django.views.generic import TemplateView, View from django.views.generic import TemplateView, View
from django.utils import timezone from django.utils import timezone
...@@ -13,13 +13,14 @@ from rest_framework.response import Response ...@@ -13,13 +13,14 @@ from rest_framework.response import Response
from rest_framework.views import APIView from rest_framework.views import APIView
from django.views.decorators.csrf import csrf_exempt from django.views.decorators.csrf import csrf_exempt
from django.http import HttpResponse from django.http import HttpResponse
from django.utils.encoding import iri_to_uri
from users.models import User from users.models import User
from assets.models import Asset from assets.models import Asset
from terminal.models import Session from terminal.models import Session
from orgs.utils import current_org from orgs.utils import current_org
from common.permissions import PermissionsMixin, IsValidUser from common.permissions import PermissionsMixin, IsValidUser
from common.http import HttpResponseTemporaryRedirect
class IndexView(PermissionsMixin, TemplateView): class IndexView(PermissionsMixin, TemplateView):
...@@ -203,14 +204,6 @@ class I18NView(View): ...@@ -203,14 +204,6 @@ class I18NView(View):
api_url_pattern = re.compile(r'^/api/(?P<version>\w+)/(?P<app>\w+)/(?P<extra>.*)$') api_url_pattern = re.compile(r'^/api/(?P<version>\w+)/(?P<app>\w+)/(?P<extra>.*)$')
class HttpResponseTemporaryRedirect(HttpResponse):
status_code = 307
def __init__(self, redirect_to):
HttpResponse.__init__(self)
self['Location'] = iri_to_uri(redirect_to)
@csrf_exempt @csrf_exempt
def redirect_format_api(request, *args, **kwargs): def redirect_format_api(request, *args, **kwargs):
_path, query = request.path, request.GET.urlencode() _path, query = request.path, request.GET.urlencode()
......
This diff is collapsed.
...@@ -255,7 +255,7 @@ function execute() { ...@@ -255,7 +255,7 @@ function execute() {
} }
} }
APIUpdateAttr({ requestApi({
url: url, url: url,
body: JSON.stringify(data), body: JSON.stringify(data),
method: 'POST', method: 'POST',
......
...@@ -109,7 +109,7 @@ $(document).ready(function() { ...@@ -109,7 +109,7 @@ $(document).ready(function() {
var url = '{% url "ops:celery-task-log" pk=DEFAULT_PK %}'.replace("{{ DEFAULT_PK }}", task_id); var url = '{% url "ops:celery-task-log" pk=DEFAULT_PK %}'.replace("{{ DEFAULT_PK }}", task_id);
window.open(url, '', 'width=800,height=600,left=400,top=400') window.open(url, '', 'width=800,height=600,left=400,top=400')
}; };
APIUpdateAttr({ requestApi({
url: the_url, url: the_url,
error: error, error: error,
method: 'GET', method: 'GET',
......
...@@ -4,7 +4,8 @@ ...@@ -4,7 +4,8 @@
from django.utils import timezone from django.utils import timezone
from django.db.models import Q from django.db.models import Q
from rest_framework.views import Response from rest_framework.views import Response
from rest_framework.generics import RetrieveUpdateAPIView from django.shortcuts import get_object_or_404
from rest_framework.generics import RetrieveUpdateAPIView, ListAPIView
from rest_framework import viewsets from rest_framework import viewsets
from rest_framework.pagination import LimitOffsetPagination from rest_framework.pagination import LimitOffsetPagination
...@@ -20,7 +21,7 @@ from .. import serializers ...@@ -20,7 +21,7 @@ from .. import serializers
__all__ = [ __all__ = [
'AssetPermissionViewSet', 'AssetPermissionRemoveUserApi', 'AssetPermissionViewSet', 'AssetPermissionRemoveUserApi',
'AssetPermissionAddUserApi', 'AssetPermissionRemoveAssetApi', 'AssetPermissionAddUserApi', 'AssetPermissionRemoveAssetApi',
'AssetPermissionAddAssetApi', 'AssetPermissionAddAssetApi', 'AssetPermissionAssetsApi',
] ]
...@@ -232,3 +233,22 @@ class AssetPermissionAddAssetApi(RetrieveUpdateAPIView): ...@@ -232,3 +233,22 @@ class AssetPermissionAddAssetApi(RetrieveUpdateAPIView):
return Response({"msg": "ok"}) return Response({"msg": "ok"})
else: else:
return Response({"error": serializer.errors}) return Response({"error": serializer.errors})
class AssetPermissionAssetsApi(ListAPIView):
permission_classes = (IsOrgAdmin,)
pagination_class = LimitOffsetPagination
serializer_class = serializers.AssetPermissionAssetsSerializer
filter_fields = ("hostname", "ip")
search_fields = filter_fields
def get_object(self):
pk = self.kwargs.get('pk')
return get_object_or_404(AssetPermission, pk=pk)
def get_queryset(self):
perm = self.get_object()
assets = perm.get_all_assets().only(
*self.serializer_class.Meta.only_fields
)
return assets
# -*- coding: utf-8 -*-
#
from functools import reduce
from hashlib import md5
from django.core.cache import cache
from django.db.models import Q
from django.conf import settings
from rest_framework.views import Response
from django.utils.translation import ugettext as _
from common.utils import get_logger
from assets.utils import LabelFilterMixin
from ..utils import (
AssetPermissionUtil
)
from .. import const
from ..hands import Asset, Node, SystemUser, Label
from .. import serializers
logger = get_logger(__name__)
__all__ = ['UserPermissionCacheMixin', 'GrantAssetsMixin', 'NodesWithUngroupMixin']
class UserPermissionCacheMixin:
cache_policy = '0'
RESP_CACHE_KEY = '_PERMISSION_RESPONSE_CACHE_V2_{}'
CACHE_TIME = settings.ASSETS_PERM_CACHE_TIME
_object = None
def get_object(self):
return None
# 内部使用可控制缓存
def _get_object(self):
if not self._object:
self._object = self.get_object()
return self._object
def get_object_id(self):
obj = self._get_object()
if obj:
return str(obj.id)
return None
def get_request_md5(self):
path = self.request.path
query = {k: v for k, v in self.request.GET.items()}
query.pop("_", None)
query = "&".join(["{}={}".format(k, v) for k, v in query.items()])
full_path = "{}?{}".format(path, query)
return md5(full_path.encode()).hexdigest()
def get_meta_cache_id(self):
obj = self._get_object()
util = AssetPermissionUtil(obj, cache_policy=self.cache_policy)
meta_cache_id = util.cache_meta.get('id')
return meta_cache_id
def get_response_cache_id(self):
obj_id = self.get_object_id()
request_md5 = self.get_request_md5()
meta_cache_id = self.get_meta_cache_id()
resp_cache_id = '{}_{}_{}'.format(obj_id, request_md5, meta_cache_id)
return resp_cache_id
def get_response_from_cache(self):
# 没有数据缓冲
meta_cache_id = self.get_meta_cache_id()
if not meta_cache_id:
logger.debug("Not get meta id: {}".format(meta_cache_id))
return None
# 从响应缓冲里获取响应
key = self.get_response_key()
data = cache.get(key)
if not data:
logger.debug("Not get response from cache: {}".format(key))
return None
logger.debug("Get user permission from cache: {}".format(self.get_object()))
response = Response(data)
return response
def expire_response_cache(self):
obj_id = self.get_object_id()
expire_cache_id = '{}_{}'.format(obj_id, '*')
key = self.RESP_CACHE_KEY.format(expire_cache_id)
cache.delete_pattern(key)
def get_response_key(self):
resp_cache_id = self.get_response_cache_id()
key = self.RESP_CACHE_KEY.format(resp_cache_id)
return key
def set_response_to_cache(self, response):
key = self.get_response_key()
cache.set(key, response.data, self.CACHE_TIME)
logger.debug("Set response to cache: {}".format(key))
def get(self, request, *args, **kwargs):
self.cache_policy = request.GET.get('cache_policy', '0')
obj = self._get_object()
if obj is None:
logger.debug("Not get response from cache: obj is none")
return super().get(request, *args, **kwargs)
if AssetPermissionUtil.is_not_using_cache(self.cache_policy):
logger.debug("Not get resp from cache: {}".format(self.cache_policy))
return super().get(request, *args, **kwargs)
elif AssetPermissionUtil.is_refresh_cache(self.cache_policy):
logger.debug("Not get resp from cache: {}".format(self.cache_policy))
self.expire_response_cache()
logger.debug("Try get response from cache")
resp = self.get_response_from_cache()
if not resp:
resp = super().get(request, *args, **kwargs)
self.set_response_to_cache(resp)
return resp
class NodesWithUngroupMixin:
util = None
@staticmethod
def get_ungrouped_node(ungroup_key):
return Node(key=ungroup_key, id=const.UNGROUPED_NODE_ID,
value=_("ungrouped"))
@staticmethod
def get_empty_node():
return Node(key=const.EMPTY_NODE_KEY, id=const.EMPTY_NODE_ID,
value=_("empty"))
def add_ungrouped_nodes(self, node_map, node_keys):
ungroup_key = '1:-1'
for key in node_keys:
if key.endswith('-1'):
ungroup_key = key
break
ungroup_node = self.get_ungrouped_node(ungroup_key)
empty_node = self.get_empty_node()
node_map[ungroup_key] = ungroup_node
node_map[const.EMPTY_NODE_KEY] = empty_node
class GrantAssetsMixin(LabelFilterMixin):
serializer_class = serializers.AssetGrantedSerializer
def get_serializer_queryset(self, queryset):
assets_ids = []
system_users_ids = set()
for asset in queryset:
assets_ids.append(asset["id"])
system_users_ids.update(set(asset["system_users"]))
assets = Asset.objects.filter(id__in=assets_ids).only(
*self.serializer_class.Meta.only_fields
)
assets_map = {asset.id: asset for asset in assets}
system_users = SystemUser.objects.filter(id__in=system_users_ids).only(
*self.serializer_class.system_users_only_fields
)
system_users_map = {s.id: s for s in system_users}
data = []
for item in queryset:
i = item["id"]
asset = assets_map.get(i)
if not asset:
continue
_system_users = item["system_users"]
system_users_granted = []
for sid, action in _system_users.items():
system_user = system_users_map.get(sid)
if not system_user:
continue
system_user.actions = action
system_users_granted.append(system_user)
asset.system_users_granted = system_users_granted
data.append(asset)
return data
def get_serializer(self, queryset_list, many=True):
data = self.get_serializer_queryset(queryset_list)
return super().get_serializer(data, many=True)
def search_queryset(self, assets_items):
search = self.request.query_params.get("search")
if not search:
return assets_items
assets_map = {asset['id']: asset for asset in assets_items}
assets_ids = set(assets_map.keys())
assets_ids_search = Asset.objects.filter(id__in=assets_ids).filter(
Q(hostname__icontains=search) | Q(ip__icontains=search)
).values_list('id', flat=True)
return [assets_map.get(asset_id) for asset_id in assets_ids_search]
def filter_queryset_by_label(self, assets_items):
labels_id = self.get_filter_labels_ids()
if not labels_id:
return assets_items
assets_map = {asset['id']: asset for asset in assets_items}
assets_matched = Asset.objects.filter(id__in=assets_map.keys())
for label_id in labels_id:
assets_matched = assets_matched.filter(labels=label_id)
assets_ids_matched = assets_matched.values_list('id', flat=True)
return [assets_map.get(asset_id) for asset_id in assets_ids_matched]
def sort_queryset(self, assets_items):
order_by = self.request.query_params.get('order', 'hostname')
if order_by not in ['hostname', '-hostname', 'ip', '-ip']:
order_by = 'hostname'
assets_map = {asset['id']: asset for asset in assets_items}
assets_ids_search = Asset.objects.filter(id__in=assets_map.keys())\
.order_by(order_by)\
.values_list('id', flat=True)
return [assets_map.get(asset_id) for asset_id in assets_ids_search]
def filter_queryset(self, assets_items):
assets_items = self.search_queryset(assets_items)
assets_items = self.filter_queryset_by_label(assets_items)
assets_items = self.sort_queryset(assets_items)
return assets_items
\ No newline at end of file
...@@ -99,3 +99,4 @@ class RemoteAppPermissionRemoveRemoteAppApi(generics.RetrieveUpdateAPIView): ...@@ -99,3 +99,4 @@ class RemoteAppPermissionRemoveRemoteAppApi(generics.RetrieveUpdateAPIView):
else: else:
return Response({"error": serializer.errors}) return Response({"error": serializer.errors})
...@@ -2,153 +2,67 @@ ...@@ -2,153 +2,67 @@
# #
from django.shortcuts import get_object_or_404 from django.shortcuts import get_object_or_404
from rest_framework.generics import (
ListAPIView, get_object_or_404,
)
from common.permissions import IsOrgAdmin, IsOrgAdminOrAppUser from common.permissions import IsOrgAdmin, IsOrgAdminOrAppUser
from common.tree import TreeNodeSerializer from ..hands import UserGroup
from ..utils import ( from .. import serializers
AssetPermissionUtil, parse_asset_to_tree_node, parse_node_to_tree_node,
RemoteAppPermissionUtil,
)
from ..hands import (
UserGroup, Node, NodeSerializer, RemoteAppSerializer,
)
from .. import serializers, const
from .user_permission import (
UserGrantedAssetsApi, UserGrantedNodesApi, UserGrantedNodesWithAssetsApi,
UserGrantedNodesWithAssetsAsTreeApi, UserGrantedNodeAssetsApi,
UserGrantedNodesAsTreeApi,
)
__all__ = [ __all__ = [
'UserGroupGrantedAssetsApi', 'UserGroupGrantedNodesApi', 'UserGroupGrantedAssetsApi', 'UserGroupGrantedNodesApi',
'UserGroupGrantedNodesWithAssetsApi', 'UserGroupGrantedNodeAssetsApi', 'UserGroupGrantedNodesWithAssetsApi', 'UserGroupGrantedNodeAssetsApi',
'UserGroupGrantedNodesWithAssetsAsTreeApi', 'UserGroupGrantedNodesWithAssetsAsTreeApi', 'UserGroupGrantedNodesAsTreeApi',
'UserGroupGrantedRemoteAppsApi',
] ]
class UserGroupGrantedAssetsApi(ListAPIView): class UserGroupGrantedAssetsApi(UserGrantedAssetsApi):
permission_classes = (IsOrgAdmin,) def get_object(self):
serializer_class = serializers.AssetGrantedSerializer
def get_queryset(self):
user_group_id = self.kwargs.get('pk', '') user_group_id = self.kwargs.get('pk', '')
queryset = []
if not user_group_id:
return queryset
user_group = get_object_or_404(UserGroup, id=user_group_id) user_group = get_object_or_404(UserGroup, id=user_group_id)
util = AssetPermissionUtil(user_group) return user_group
assets = util.get_assets()
for k, v in assets.items():
k.system_users_granted = v
queryset.append(k)
return queryset
class UserGroupGrantedNodesApi(ListAPIView): class UserGroupGrantedNodesApi(UserGrantedNodesApi):
permission_classes = (IsOrgAdmin,) def get_object(self):
serializer_class = NodeSerializer user_group_id = self.kwargs.get('pk', '')
user_group = get_object_or_404(UserGroup, id=user_group_id)
return user_group
def get_queryset(self):
group_id = self.kwargs.get('pk', '')
queryset = []
if group_id: class UserGroupGrantedNodesAsTreeApi(UserGrantedNodesAsTreeApi):
group = get_object_or_404(UserGroup, id=group_id) def get_object(self):
util = AssetPermissionUtil(group) user_group_id = self.kwargs.get('pk', '')
nodes = util.get_nodes_with_assets() user_group = get_object_or_404(UserGroup, id=user_group_id)
return nodes.keys() return user_group
return queryset
class UserGroupGrantedNodesWithAssetsApi(ListAPIView): class UserGroupGrantedNodesWithAssetsApi(UserGrantedNodesWithAssetsApi):
permission_classes = (IsOrgAdmin,) permission_classes = (IsOrgAdmin,)
serializer_class = serializers.NodeGrantedSerializer serializer_class = serializers.NodeGrantedSerializer
def get_queryset(self): def get_object(self):
user_group_id = self.kwargs.get('pk', '') user_group_id = self.kwargs.get('pk', '')
queryset = [] user_group = get_object_or_404(UserGroup, id=user_group_id)
return user_group
if not user_group_id:
return queryset
class UserGroupGrantedNodesWithAssetsAsTreeApi(UserGrantedNodesWithAssetsAsTreeApi):
def get_object(self):
user_group_id = self.kwargs.get('pk', '')
user_group = get_object_or_404(UserGroup, id=user_group_id) user_group = get_object_or_404(UserGroup, id=user_group_id)
util = AssetPermissionUtil(user_group) return user_group
nodes = util.get_nodes_with_assets()
for node, _assets in nodes.items():
assets = _assets.keys()
for asset, system_users in _assets.items():
asset.system_users_granted = system_users
node.assets_granted = assets
queryset.append(node)
return queryset
class UserGroupGrantedNodesWithAssetsAsTreeApi(ListAPIView):
serializer_class = TreeNodeSerializer
permission_classes = (IsOrgAdminOrAppUser,)
show_assets = True
system_user_id = None
def get(self, request, *args, **kwargs):
self.show_assets = request.query_params.get('show_assets', '1') == '1'
self.system_user_id = request.query_params.get('system_user')
return super().get(request, *args, **kwargs)
def get_queryset(self): class UserGroupGrantedNodeAssetsApi(UserGrantedNodeAssetsApi):
user_group_id = self.kwargs.get('pk', '')
queryset = []
group = get_object_or_404(UserGroup, id=user_group_id)
util = AssetPermissionUtil(group)
if self.system_user_id:
util.filter_permissions(system_users=self.system_user_id)
nodes = util.get_nodes_with_assets()
for node, assets in nodes.items():
data = parse_node_to_tree_node(node)
queryset.append(data)
if not self.show_assets:
continue
for asset, system_users in assets.items():
data = parse_asset_to_tree_node(node, asset, system_users)
queryset.append(data)
queryset = sorted(queryset)
return queryset
class UserGroupGrantedNodeAssetsApi(ListAPIView):
permission_classes = (IsOrgAdminOrAppUser,) permission_classes = (IsOrgAdminOrAppUser,)
serializer_class = serializers.AssetGrantedSerializer serializer_class = serializers.AssetGrantedSerializer
def get_queryset(self): def get_object(self):
user_group_id = self.kwargs.get('pk', '') user_group_id = self.kwargs.get('pk', '')
node_id = self.kwargs.get('node_id')
user_group = get_object_or_404(UserGroup, id=user_group_id)
util = AssetPermissionUtil(user_group)
if str(node_id) == const.UNGROUPED_NODE_ID:
node = util.tree.ungrouped_node
else:
node = get_object_or_404(Node, id=node_id)
nodes = util.get_nodes_with_assets()
assets = nodes.get(node, [])
for asset, system_users in assets.items():
asset.system_users_granted = system_users
return assets
# RemoteApp permission
class UserGroupGrantedRemoteAppsApi(ListAPIView):
permission_classes = (IsOrgAdmin, )
serializer_class = RemoteAppSerializer
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) user_group = get_object_or_404(UserGroup, id=user_group_id)
util = RemoteAppPermissionUtil(user_group) return user_group
queryset = util.get_remote_apps()
return queryset
This diff is collapsed.
...@@ -13,13 +13,13 @@ from ..utils import ( ...@@ -13,13 +13,13 @@ from ..utils import (
RemoteAppPermissionUtil, construct_remote_apps_tree_root, RemoteAppPermissionUtil, construct_remote_apps_tree_root,
parse_remote_app_to_tree_node, parse_remote_app_to_tree_node,
) )
from ..hands import User, RemoteApp, RemoteAppSerializer from ..hands import User, RemoteApp, RemoteAppSerializer, UserGroup
from ..mixins import RemoteAppFilterMixin from ..mixins import RemoteAppFilterMixin
__all__ = [ __all__ = [
'UserGrantedRemoteAppsApi', 'ValidateUserRemoteAppPermissionApi', 'UserGrantedRemoteAppsApi', 'ValidateUserRemoteAppPermissionApi',
'UserGrantedRemoteAppsAsTreeApi', 'UserGrantedRemoteAppsAsTreeApi', 'UserGroupGrantedRemoteAppsApi',
] ]
...@@ -94,3 +94,20 @@ class ValidateUserRemoteAppPermissionApi(APIView): ...@@ -94,3 +94,20 @@ class ValidateUserRemoteAppPermissionApi(APIView):
if remote_app not in remote_apps: if remote_app not in remote_apps:
return Response({'msg': False}, status=403) return Response({'msg': False}, status=403)
return Response({'msg': True}, status=200) return Response({'msg': True}, status=200)
# RemoteApp permission
class UserGroupGrantedRemoteAppsApi(ListAPIView):
permission_classes = (IsOrgAdminOrAppUser, )
serializer_class = RemoteAppSerializer
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 = RemoteAppPermissionUtil(user_group)
queryset = util.get_remote_apps()
return queryset
...@@ -3,3 +3,4 @@ ...@@ -3,3 +3,4 @@
UNGROUPED_NODE_ID = "00000000-0000-0000-0000-000000000002" UNGROUPED_NODE_ID = "00000000-0000-0000-0000-000000000002"
EMPTY_NODE_ID = "00000000-0000-0000-0000-000000000003" EMPTY_NODE_ID = "00000000-0000-0000-0000-000000000003"
EMPTY_NODE_KEY = "1:-2"
...@@ -41,6 +41,9 @@ class AssetPermissionForm(OrgModelForm): ...@@ -41,6 +41,9 @@ class AssetPermissionForm(OrgModelForm):
users_field = self.fields.get('users') users_field = self.fields.get('users')
users_field.queryset = current_org.get_org_users() users_field.queryset = current_org.get_org_users()
nodes_field = self.fields['nodes']
nodes_field.choices = ((n.id, n.full_value) for n in Node.get_queryset())
# 前端渲染优化, 防止过多资产 # 前端渲染优化, 防止过多资产
if not self.data: if not self.data:
instance = kwargs.get('instance') instance = kwargs.get('instance')
...@@ -49,8 +52,6 @@ class AssetPermissionForm(OrgModelForm): ...@@ -49,8 +52,6 @@ class AssetPermissionForm(OrgModelForm):
assets_field.queryset = instance.assets.all() assets_field.queryset = instance.assets.all()
else: else:
assets_field.queryset = Asset.objects.none() assets_field.queryset = Asset.objects.none()
nodes_field = self.fields['nodes']
nodes_field._queryset = Node.get_queryset()
class Meta: class Meta:
model = AssetPermission model = AssetPermission
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
# #
from users.models import User, UserGroup from users.models import User, UserGroup
from assets.models import Asset, SystemUser, Node from assets.models import Asset, SystemUser, Node, Label
from assets.serializers import NodeSerializer from assets.serializers import NodeSerializer
from applications.serializers import RemoteAppSerializer from applications.serializers import RemoteAppSerializer
from applications.models import RemoteApp from applications.models import RemoteApp
......
...@@ -2,10 +2,12 @@ import uuid ...@@ -2,10 +2,12 @@ import uuid
from functools import reduce from functools import reduce
from django.db import models from django.db import models
from django.db.models import Q
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from common.utils import date_expired_default, set_or_append_attr_bulk from common.utils import date_expired_default, set_or_append_attr_bulk
from orgs.mixins import OrgModelMixin from orgs.mixins import OrgModelMixin
from assets.models import Asset, SystemUser, Node
from .base import BasePermission from .base import BasePermission
...@@ -85,14 +87,23 @@ class AssetPermission(BasePermission): ...@@ -85,14 +87,23 @@ class AssetPermission(BasePermission):
@classmethod @classmethod
def get_queryset_with_prefetch(cls): def get_queryset_with_prefetch(cls):
return cls.objects.all().valid().prefetch_related('nodes', 'assets', 'system_users') return cls.objects.all().valid().prefetch_related(
models.Prefetch('nodes', queryset=Node.objects.all().only('key')),
models.Prefetch('assets', queryset=Asset.objects.all().only('id')),
models.Prefetch('system_users', queryset=SystemUser.objects.all().only('id'))
)
def get_all_assets(self): def get_all_assets(self):
assets = set(self.assets.all()) args = [Q(granted_by_permissions=self)]
for node in self.nodes.all(): pattern = set()
_assets = node.get_all_assets() nodes_keys = self.nodes.all().values_list('key', flat=True)
set_or_append_attr_bulk(_assets, 'inherit', node.value) for key in nodes_keys:
assets.update(set(_assets)) pattern.add(r'^{0}$|^{0}:'.format(key))
pattern = '|'.join(list(pattern))
if pattern:
args.append(Q(nodes__key__regex=pattern))
args = reduce(lambda x, y: x | y, args)
assets = Asset.objects.filter(args)
return assets return assets
......
...@@ -6,11 +6,12 @@ from rest_framework import serializers ...@@ -6,11 +6,12 @@ from rest_framework import serializers
from common.fields import StringManyToManyField from common.fields import StringManyToManyField
from orgs.mixins import BulkOrgResourceModelSerializer from orgs.mixins import BulkOrgResourceModelSerializer
from perms.models import AssetPermission, Action from perms.models import AssetPermission, Action
from assets.models import Asset
__all__ = [ __all__ = [
'AssetPermissionCreateUpdateSerializer', 'AssetPermissionListSerializer', 'AssetPermissionCreateUpdateSerializer', 'AssetPermissionListSerializer',
'AssetPermissionUpdateUserSerializer', 'AssetPermissionUpdateAssetSerializer', 'AssetPermissionUpdateUserSerializer', 'AssetPermissionUpdateAssetSerializer',
'ActionsField', 'ActionsField', 'AssetPermissionAssetsSerializer',
] ]
...@@ -70,3 +71,11 @@ class AssetPermissionUpdateAssetSerializer(serializers.ModelSerializer): ...@@ -70,3 +71,11 @@ class AssetPermissionUpdateAssetSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = AssetPermission model = AssetPermission
fields = ['id', 'assets'] fields = ['id', 'assets']
class AssetPermissionAssetsSerializer(serializers.ModelSerializer):
class Meta:
model = Asset
only_fields = ['id', 'hostname', 'ip']
fields = tuple(only_fields)
...@@ -2,16 +2,16 @@ ...@@ -2,16 +2,16 @@
# #
from rest_framework import serializers from rest_framework import serializers
from django.utils.translation import ugettext_lazy as _
from assets.models import Node, SystemUser from assets.models import Node, SystemUser, Asset
from assets.serializers import AssetSerializer from assets.serializers import ProtocolsField
from .asset_permission import ActionsField from .asset_permission import ActionsField
__all__ = [ __all__ = [
'AssetPermissionNodeSerializer', 'GrantedNodeSerializer', 'GrantedNodeSerializer',
'NodeGrantedSerializer', 'AssetGrantedSerializer', 'NodeGrantedSerializer', 'AssetGrantedSerializer',
'ActionsSerializer', 'ActionsSerializer', 'AssetSystemUserSerializer',
] ]
...@@ -23,58 +23,36 @@ class AssetSystemUserSerializer(serializers.ModelSerializer): ...@@ -23,58 +23,36 @@ class AssetSystemUserSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = SystemUser model = SystemUser
fields = ( only_fields = (
'id', 'name', 'username', 'priority', "actions", 'id', 'name', 'username', 'priority',
'protocol', 'login_mode', 'protocol', 'login_mode',
) )
fields = list(only_fields) + ["actions"]
read_only_fields = fields
class AssetGrantedSerializer(AssetSerializer): class AssetGrantedSerializer(serializers.ModelSerializer):
""" """
被授权资产的数据结构 被授权资产的数据结构
""" """
protocols = ProtocolsField(label=_('Protocols'), required=False, read_only=True)
system_users_granted = AssetSystemUserSerializer(many=True, read_only=True) system_users_granted = AssetSystemUserSerializer(many=True, read_only=True)
system_users_join = serializers.SerializerMethodField() system_users_join = serializers.SerializerMethodField()
system_users_only_fields = AssetSystemUserSerializer.Meta.only_fields
@staticmethod
def get_system_users_join(obj):
system_users = [s.username for s in obj.system_users_granted]
return ', '.join(system_users)
def get_field_names(self, declared_fields, info):
fields = (
"id", "hostname", "ip", "protocols",
"system_users_granted", "is_active", "system_users_join", "os",
'domain', "platform", "comment", "org_id", "org_name",
)
return fields
class AssetPermissionNodeSerializer(serializers.ModelSerializer):
asset = AssetGrantedSerializer(required=False)
assets_amount = serializers.SerializerMethodField()
tree_id = serializers.SerializerMethodField()
tree_parent = serializers.SerializerMethodField()
class Meta: class Meta:
model = Node model = Asset
fields = [ only_fields = [
'id', 'key', 'value', 'asset', 'is_node', 'org_id', "id", "hostname", "ip", "protocols", "os", 'domain',
'tree_id', 'tree_parent', 'assets_amount', "platform", "org_id",
] ]
fields = only_fields + ['system_users_granted', 'system_users_join', "org_name"]
read_only_fields = fields
@staticmethod @staticmethod
def get_assets_amount(obj): def get_system_users_join(obj):
return obj.assets_amount system_users = [s.username for s in obj.system_users_granted]
return ', '.join(system_users)
@staticmethod
def get_tree_id(obj):
return obj.key
@staticmethod
def get_tree_parent(obj):
return obj.parent_key
class NodeGrantedSerializer(serializers.ModelSerializer): class NodeGrantedSerializer(serializers.ModelSerializer):
...@@ -82,28 +60,19 @@ class NodeGrantedSerializer(serializers.ModelSerializer): ...@@ -82,28 +60,19 @@ class NodeGrantedSerializer(serializers.ModelSerializer):
授权资产组 授权资产组
""" """
assets_granted = AssetGrantedSerializer(many=True, read_only=True) assets_granted = AssetGrantedSerializer(many=True, read_only=True)
assets_amount = serializers.SerializerMethodField() assets_amount = serializers.ReadOnlyField()
parent = serializers.SerializerMethodField() name = serializers.ReadOnlyField(source='value')
name = serializers.SerializerMethodField()
assets_only_fields = AssetGrantedSerializer.Meta.only_fields
system_users_only_fields = AssetGrantedSerializer.system_users_only_fields
class Meta: class Meta:
model = Node model = Node
fields = [ only_fields = ['id', 'key', 'value', "org_id"]
'id', 'key', 'name', 'value', 'parent', fields = only_fields + [
'assets_granted', 'assets_amount', 'org_id', 'name', 'assets_granted', 'assets_amount',
] ]
read_only_fields = fields
@staticmethod
def get_assets_amount(obj):
return len(obj.assets_granted)
@staticmethod
def get_name(obj):
return obj.name
@staticmethod
def get_parent(obj):
return obj.parent.id
class GrantedNodeSerializer(serializers.ModelSerializer): class GrantedNodeSerializer(serializers.ModelSerializer):
...@@ -112,6 +81,7 @@ class GrantedNodeSerializer(serializers.ModelSerializer): ...@@ -112,6 +81,7 @@ class GrantedNodeSerializer(serializers.ModelSerializer):
fields = [ fields = [
'id', 'name', 'key', 'value', 'id', 'name', 'key', 'value',
] ]
read_only_fields = fields
class ActionsSerializer(serializers.Serializer): class ActionsSerializer(serializers.Serializer):
......
...@@ -48,29 +48,19 @@ ...@@ -48,29 +48,19 @@
</div> </div>
</div> </div>
<div class="ibox-content"> <div class="ibox-content">
<table class="table table-hover"> <table class="table table-striped table-bordered table-hover" id="asset_list_table" style="width: 100%">
<thead> <thead>
<tr> <tr>
<th>{% trans 'Hostname' %}</th> <th class="text-center">
<th>{% trans 'IP' %}</th> <input type="checkbox" id="check_all" class="ipt_check_all" >
<th></th> </th>
</tr> <th class="text-center">{% trans 'Hostname' %}</th>
<th class="text-center">{% trans 'IP' %}</th>
</tr>
</thead> </thead>
<tbody> <tbody>
{% for asset in object_list %}
<tr>
<td>{{ asset.hostname }}</td>
<td>{{ asset.ip }}</td>
<td>
<button title="{{ asset.inherit }}" data-gid="{{ asset.id }}" class="btn btn-danger btn-xs btn-remove-asset {% if asset.inherit %} disabled {% endif %}" type="button" style="float: right;"><i class="fa fa-minus"></i></button>
</td>
</tr>
{% endfor %}
</tbody> </tbody>
</table> </table>
<div class="row">
{% include '_pagination.html' %}
</div>
</div> </div>
</div> </div>
</div> </div>
...@@ -86,9 +76,6 @@ ...@@ -86,9 +76,6 @@
<tr class="no-borders-tr"> <tr class="no-borders-tr">
<td colspan="2"> <td colspan="2">
<select data-placeholder="{% trans 'Select assets' %}" class="select2" id="asset_select2" style="width: 100%" multiple="" tabindex="4"> <select data-placeholder="{% trans 'Select assets' %}" class="select2" id="asset_select2" style="width: 100%" multiple="" tabindex="4">
{% for asset in assets_remain %}
<option value="{{ asset.id }}">{{ asset }}</option>
{% endfor %}
</select> </select>
</td> </td>
</tr> </tr>
...@@ -146,6 +133,7 @@ ...@@ -146,6 +133,7 @@
</div> </div>
</div> </div>
{% include 'assets/_asset_list_modal.html' %}
{% endblock %} {% endblock %}
{% block custom_foot_js %} {% block custom_foot_js %}
<script> <script>
...@@ -157,12 +145,12 @@ function addAssets(assets) { ...@@ -157,12 +145,12 @@ function addAssets(assets) {
var success = function(data) { var success = function(data) {
location.reload(); location.reload();
}; };
APIUpdateAttr({ requestApi({
url: the_url, url: the_url,
body: JSON.stringify(body), body: JSON.stringify(body),
success: success success: success
}); });
} }
function removeAssets(assets) { function removeAssets(assets) {
var the_url = "{% url 'api-perms:asset-permission-remove-asset' pk=asset_permission.id %}"; var the_url = "{% url 'api-perms:asset-permission-remove-asset' pk=asset_permission.id %}";
...@@ -172,7 +160,7 @@ function removeAssets(assets) { ...@@ -172,7 +160,7 @@ function removeAssets(assets) {
var success = function(data) { var success = function(data) {
location.reload(); location.reload();
}; };
APIUpdateAttr({ requestApi({
url: the_url, url: the_url,
body: JSON.stringify(body), body: JSON.stringify(body),
success: success success: success
...@@ -184,16 +172,64 @@ function updateNodes(nodes, success) { ...@@ -184,16 +172,64 @@ function updateNodes(nodes, success) {
var body = { var body = {
nodes: nodes nodes: nodes
}; };
APIUpdateAttr({ requestApi({
url: the_url, url: the_url,
body: JSON.stringify(body), body: JSON.stringify(body),
success: success success: success
}); });
} }
var table;
function initAssetTable() {
var options = {
ele: $('#asset_list_table'),
toggle: true,
columnDefs: [
{
targets: 0, createdCell: function (td, cellData, rowData) {
var html = '<input type="checkbox" class="text-center ipt_check" id="id_' + cellData + '">';
$(td).html(html);
}
},
],
ajax_url: "{% url 'api-perms:asset-permission-assets' pk=object.id %}",
columns: [
{data: "id"}, {data: "hostname"}, {data: "ip"}
],
op_html: $('#actions').html()
};
table = jumpserver.initServerSideDataTable(options);
return table
}
$(document).ready(function () { $(document).ready(function () {
$('.select2').select2(); $('.select2').select2();
table = initAssetTable();
$("#asset_select2").parent().find(".select2-selection").on('click', function (e) {
if ($(e.target).attr('class') !== 'select2-selection__choice__remove'){
e.preventDefault();
e.stopPropagation();
$("#asset_list_modal").modal();
initSelectedAssets2Table('#asset_select2');
}
})
})
.on('click', '#btn_asset_modal_confirm', function () {
var assets = asset_table2.selected;
var options = [];
$('#asset_select2 option').each(function (i, v) {
options.push(v.value)
});
asset_table2.selected_rows.forEach(function (i) {
var name = i.hostname + '(' + i.ip + ')';
var option = new Option(name, i.id, false, true);
if (options.indexOf(i.id) === -1) {
$('#asset_select2').append(option).trigger('change');
}
});
$('#asset_select2').val(assets).trigger('change');
$("#asset_list_modal").modal('hide');
}) })
.on('click', '.btn-add-assets', function () { .on('click', '.btn-add-assets', function () {
var assets_selected = $("#asset_select2 option:selected").map(function () { var assets_selected = $("#asset_select2 option:selected").map(function () {
...@@ -237,7 +273,7 @@ $(document).ready(function () { ...@@ -237,7 +273,7 @@ $(document).ready(function () {
}); });
}; };
updateNodes(nodes, success); updateNodes(nodes, success);
}) })
.on('click', '.btn-remove-node', function () { .on('click', '.btn-remove-node', function () {
var $this = $(this); var $this = $(this);
var $tr = $this.closest('tr'); var $tr = $this.closest('tr');
......
...@@ -68,7 +68,7 @@ ...@@ -68,7 +68,7 @@
</tr> </tr>
<tr> <tr>
<td>{% trans 'User group count' %}:</td> <td>{% trans 'User group count' %}:</td>
<td><b>{{ object.users.count }}</b></td> <td><b>{{ object.user_groups.count }}</b></td>
</tr> </tr>
<tr> <tr>
<td>{% trans 'Asset count' %}:</td> <td>{% trans 'Asset count' %}:</td>
...@@ -187,7 +187,7 @@ function updateSystemUser(system_users) { ...@@ -187,7 +187,7 @@ function updateSystemUser(system_users) {
var body = { var body = {
system_users: Object.assign([], system_users) system_users: Object.assign([], system_users)
}; };
APIUpdateAttr({ requestApi({
url: the_url, url: the_url,
body: JSON.stringify(body) body: JSON.stringify(body)
}); });
...@@ -247,7 +247,7 @@ $(document).ready(function () { ...@@ -247,7 +247,7 @@ $(document).ready(function () {
var body = { var body = {
'is_active': checked 'is_active': checked
}; };
APIUpdateAttr({ requestApi({
url: the_url, url: the_url,
body: JSON.stringify(body), body: JSON.stringify(body),
}); });
......
...@@ -160,7 +160,7 @@ function addUsers(users) { ...@@ -160,7 +160,7 @@ function addUsers(users) {
var success = function(data) { var success = function(data) {
location.reload(); location.reload();
}; };
APIUpdateAttr({ requestApi({
url: the_url, url: the_url,
body: JSON.stringify(body), body: JSON.stringify(body),
success: success success: success
...@@ -175,7 +175,7 @@ function removeUser(users) { ...@@ -175,7 +175,7 @@ function removeUser(users) {
var success = function(data) { var success = function(data) {
location.reload(); location.reload();
}; };
APIUpdateAttr({ requestApi({
url: the_url, url: the_url,
body: JSON.stringify(body), body: JSON.stringify(body),
success: success success: success
...@@ -187,7 +187,7 @@ function updateGroup(groups) { ...@@ -187,7 +187,7 @@ function updateGroup(groups) {
var body = { var body = {
user_groups: groups user_groups: groups
}; };
APIUpdateAttr({ requestApi({
url: the_url, url: the_url,
body: JSON.stringify(body) body: JSON.stringify(body)
}); });
......
...@@ -116,5 +116,28 @@ $(document).ready(function () { ...@@ -116,5 +116,28 @@ $(document).ready(function () {
$('#date_start').daterangepicker(dateOptions); $('#date_start').daterangepicker(dateOptions);
$('#date_expired').daterangepicker(dateOptions); $('#date_expired').daterangepicker(dateOptions);
}) })
.on("submit", "form", function (evt) {
evt.preventDefault();
var form = $("form");
var data = form.serializeObject();
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" %}
the_url = '{% url "api-perms:remote-app-permission-detail" pk=object.id %}';
method = "PUT";
{% endif %}
objectAttrsIsList(data, ['users', 'user_groups', 'remote_apps']);
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> </script>
{% endblock %} {% endblock %}
\ No newline at end of file
...@@ -160,7 +160,7 @@ $(document).ready(function () { ...@@ -160,7 +160,7 @@ $(document).ready(function () {
var body = { var body = {
'is_active': checked 'is_active': checked
}; };
APIUpdateAttr({ requestApi({
url: the_url, url: the_url,
body: JSON.stringify(body) body: JSON.stringify(body)
}); });
......
...@@ -120,7 +120,7 @@ ...@@ -120,7 +120,7 @@
var success = function(data) { var success = function(data) {
location.reload(); location.reload();
}; };
APIUpdateAttr({ requestApi({
url: the_url, url: the_url,
body: JSON.stringify(body), body: JSON.stringify(body),
success: success success: success
...@@ -134,7 +134,7 @@ ...@@ -134,7 +134,7 @@
var success = function(data) { var success = function(data) {
location.reload(); location.reload();
}; };
APIUpdateAttr({ requestApi({
url: the_url, url: the_url,
body: JSON.stringify(body), body: JSON.stringify(body),
success: success success: success
......
...@@ -158,7 +158,7 @@ ...@@ -158,7 +158,7 @@
var success = function(data) { var success = function(data) {
location.reload(); location.reload();
}; };
APIUpdateAttr({ requestApi({
url: the_url, url: the_url,
body: JSON.stringify(body), body: JSON.stringify(body),
success: success success: success
...@@ -172,7 +172,7 @@ ...@@ -172,7 +172,7 @@
var success = function(data) { var success = function(data) {
location.reload(); location.reload();
}; };
APIUpdateAttr({ requestApi({
url: the_url, url: the_url,
body: JSON.stringify(body), body: JSON.stringify(body),
success: success success: success
...@@ -183,7 +183,7 @@ ...@@ -183,7 +183,7 @@
var body = { var body = {
user_groups: groups user_groups: groups
}; };
APIUpdateAttr({ requestApi({
url: the_url, url: the_url,
body: JSON.stringify(body) body: JSON.stringify(body)
}); });
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
...@@ -163,12 +163,12 @@ class AssetPermissionAssetView(PermissionsMixin, ...@@ -163,12 +163,12 @@ class AssetPermissionAssetView(PermissionsMixin,
return queryset return queryset
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
assets_granted = self.get_queryset() granted_nodes = self.object.nodes.all()
nodes_remain = [n for n in Node.get_queryset() if n not in granted_nodes]
context = { context = {
'app': _('Perms'), 'app': _('Perms'),
'action': _('Asset permission asset list'), 'action': _('Asset permission asset list'),
'assets_remain': Asset.objects.exclude(id__in=[a.id for a in assets_granted]), 'nodes_remain': nodes_remain,
'nodes_remain': Node.objects.exclude(granted_by_permissions=self.object),
} }
kwargs.update(context) kwargs.update(context)
return super().get_context_data(**kwargs) return super().get_context_data(**kwargs)
\ No newline at end of file
...@@ -48,6 +48,7 @@ class RemoteAppPermissionCreateView(PermissionsMixin, CreateView): ...@@ -48,6 +48,7 @@ class RemoteAppPermissionCreateView(PermissionsMixin, CreateView):
context = { context = {
'app': _('Perms'), 'app': _('Perms'),
'action': _('Create RemoteApp permission'), 'action': _('Create RemoteApp permission'),
'type': 'create'
} }
kwargs.update(context) kwargs.update(context)
return super().get_context_data(**kwargs) return super().get_context_data(**kwargs)
...@@ -63,7 +64,8 @@ class RemoteAppPermissionUpdateView(PermissionsMixin, UpdateView): ...@@ -63,7 +64,8 @@ class RemoteAppPermissionUpdateView(PermissionsMixin, UpdateView):
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = { context = {
'app': _('Perms'), 'app': _('Perms'),
'action': _('Update RemoteApp permission') 'action': _('Update RemoteApp permission'),
'type': 'update'
} }
kwargs.update(context) kwargs.update(context)
return super().get_context_data(**kwargs) return super().get_context_data(**kwargs)
......
...@@ -192,6 +192,11 @@ class SecuritySettingForm(BaseForm): ...@@ -192,6 +192,11 @@ class SecuritySettingForm(BaseForm):
required=False, label=_("Batch execute commands"), required=False, label=_("Batch execute commands"),
help_text=_("Allow user batch execute commands") help_text=_("Allow user batch execute commands")
) )
SECURITY_SERVICE_ACCOUNT_REGISTRATION = forms.BooleanField(
required=False, label=_("Service account registration"),
help_text=_("Allow using bootstrap token register service account, "
"when terminal setup, can disable it")
)
# limit login count # limit login count
SECURITY_LOGIN_LIMIT_COUNT = forms.IntegerField( SECURITY_LOGIN_LIMIT_COUNT = forms.IntegerField(
min_value=3, max_value=99999, min_value=3, max_value=99999,
......
...@@ -96,7 +96,7 @@ $(document).ready(function () { ...@@ -96,7 +96,7 @@ $(document).ready(function () {
function success(message) { function success(message) {
toastr.success(message.msg) toastr.success(message.msg)
} }
APIUpdateAttr({ requestApi({
url: the_url, url: the_url,
body: JSON.stringify(data), body: JSON.stringify(data),
method: "POST", method: "POST",
......
...@@ -100,7 +100,7 @@ $(document).ready(function () { ...@@ -100,7 +100,7 @@ $(document).ready(function () {
function success(message) { function success(message) {
toastr.success(message.msg) toastr.success(message.msg)
} }
APIUpdateAttr({ requestApi({
url: the_url, url: the_url,
body: JSON.stringify(data), body: JSON.stringify(data),
method: "POST", method: "POST",
...@@ -127,7 +127,7 @@ $(document).ready(function () { ...@@ -127,7 +127,7 @@ $(document).ready(function () {
function success(message) { function success(message) {
toastr.success(message.msg) toastr.success(message.msg)
} }
APIUpdateAttr({ requestApi({
url: the_url, url: the_url,
body: JSON.stringify({'username_list':username_list}), body: JSON.stringify({'username_list':username_list}),
method: "POST", method: "POST",
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment