mirror of https://github.com/jumpserver/jumpserver
commit
c8623c3b8c
|
@ -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 () {
|
||||||
.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 %}
|
|
@ -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):
|
||||||
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 @@
|
||||||
# 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__)
|
||||||
__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):
|
||||||
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):
|
||||||
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._queryset = Node.get_queryset()
|
nodes_field.choices = ((n.id, n.full_value) for n in
|
||||||
|
Node.get_queryset())
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Asset
|
model = Asset
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
# 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
|
||||||
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:
|
||||||
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):
|
||||||
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):
|
||||||
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:
|
||||||
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):
|
||||||
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):
|
||||||
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
|
||||||
|
|
||||||
|
|
||||||
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
|
||||||
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):
|
||||||
|
|
||||||
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):
|
||||||
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):
|
||||||
|
|
||||||
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):
|
||||||
|
|
||||||
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):
|
||||||
'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):
|
||||||
@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() {
|
||||||
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(){
|
||||||
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){
|
||||||
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) {
|
||||||
|
|
||||||
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) {
|
||||||
|
|
||||||
$(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 () {
|
||||||
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>
|
|
@ -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 () {
|
||||||
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) {
|
||||||
// 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 @@
|
||||||
{% 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 @@
|
||||||
<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() {
|
||||||
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() {
|
||||||
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 () {
|
||||||
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 () {
|
||||||
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 @@
|
||||||
</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) {
|
||||||
// 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() {
|
||||||
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 () {
|
||||||
};
|
};
|
||||||
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 () {
|
||||||
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() {
|
||||||
}},
|
}},
|
||||||
{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(){
|
||||||
}
|
}
|
||||||
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(){
|
||||||
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(){
|
||||||
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(){
|
||||||
},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(){
|
||||||
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(){
|
||||||
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(){
|
||||||
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(){
|
||||||
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(){
|
||||||
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 @@
|
||||||
</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 %}
|
|
@ -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(){
|
||||||
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 %}
|
|
@ -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 %}
|
|
@ -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() {
|
||||||
$(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 () {
|
||||||
$('#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 %}
|
|
@ -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 () {
|
||||||
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 () {
|
||||||
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 () {
|
||||||
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) {
|
||||||
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 () {
|
||||||
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 () {
|
||||||
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 () {
|
||||||
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 @@
|
||||||
<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() {
|
||||||
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() {
|
||||||
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(){
|
||||||
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 @@
|
||||||
{% 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 @@
|
||||||
|
|
||||||
{% 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
|
|
||||||
} 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)
|
$(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() {
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
|
@ -1,7 +1,8 @@
|
||||||
# ~*~ 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):
|
||||||
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:
|
|
||||||
labels_query[key] = self.request.query_params.get(key)
|
|
||||||
|
|
||||||
conditions = []
|
if not valid_keys:
|
||||||
for k, v in labels_query.items():
|
return []
|
||||||
query = {'labels__name': k, 'labels__value': v}
|
|
||||||
conditions.append(query)
|
|
||||||
|
|
||||||
if conditions:
|
labels_query = [
|
||||||
for kwargs in conditions:
|
{"name": key, "value": query_params[key]}
|
||||||
queryset = queryset.filter(**kwargs)
|
for key in valid_keys
|
||||||
|
]
|
||||||
|
args = [Q(**kwargs) for kwargs in labels_query]
|
||||||
|
args = reduce(lambda x, y: x | y, args)
|
||||||
|
labels_id = Label.objects.filter(args).values_list('id', flat=True)
|
||||||
|
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:
|
||||||
_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:
|
||||||
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)
|
|
||||||
if not node:
|
|
||||||
continue
|
|
||||||
tree_nodes.update(node._parents)
|
|
||||||
tree_nodes.add(node)
|
|
||||||
if with_children:
|
|
||||||
tree_nodes.update(node._children)
|
|
||||||
return list(tree_nodes)
|
|
||||||
|
|
||||||
def get_nodes_parents(self, nodes, with_self=True):
|
def get_family_keys_by_key(self, key):
|
||||||
|
nodes = self.get_family_by_key(key)
|
||||||
|
return [n.key for n in nodes]
|
||||||
|
|
||||||
|
def get_some_nodes_family_by_keys(self, keys):
|
||||||
|
family = set()
|
||||||
|
for key in keys:
|
||||||
|
family.update(self.get_family_by_key(key))
|
||||||
|
return family
|
||||||
|
|
||||||
|
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:
|
||||||
|
return []
|
||||||
parents.update(set(node._parents))
|
parents.update(set(node._parents))
|
||||||
if with_self:
|
if with_self:
|
||||||
parents.add(node)
|
parents.add(node)
|
||||||
return parents
|
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):
|
||||||
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):
|
||||||
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):
|
||||||
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):
|
||||||
'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):
|
||||||
'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)
|
|
@ -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):
|
||||||
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):
|
||||||
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):
|
||||||
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):
|
||||||
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):
|
||||||
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 () {
|
||||||
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
|
||||||
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):
|
||||||
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)
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
# -*- 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):
|
||||||
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:
|
||||||
|
|
||||||
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:
|
||||||
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
|
||||||
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):
|
||||||
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 = {
|
||||||
'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 = [
|
||||||
'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
|
||||||
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
|
||||||
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):
|
||||||
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()
|
||||||
|
|
Binary file not shown.
File diff suppressed because it is too large
Load Diff
|
@ -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() {
|
||||||
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 @@
|
||||||
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
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'AssetPermissionViewSet', 'AssetPermissionRemoveUserApi',
|
'AssetPermissionViewSet', 'AssetPermissionRemoveUserApi',
|
||||||
'AssetPermissionAddUserApi', 'AssetPermissionRemoveAssetApi',
|
'AssetPermissionAddUserApi', 'AssetPermissionRemoveAssetApi',
|
||||||
'AssetPermissionAddAssetApi',
|
'AssetPermissionAddAssetApi', 'AssetPermissionAssetsApi',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
|
|
@ -0,0 +1,225 @@
|
||||||
|
# -*- 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
|
|
@ -99,3 +99,4 @@ class RemoteAppPermissionRemoveRemoteAppApi(generics.RetrieveUpdateAPIView):
|
||||||
else:
|
else:
|
||||||
return Response({"error": serializer.errors})
|
return Response({"error": serializer.errors})
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
def get_queryset(self):
|
return user_group
|
||||||
group_id = self.kwargs.get('pk', '')
|
|
||||||
queryset = []
|
|
||||||
|
|
||||||
if group_id:
|
|
||||||
group = get_object_or_404(UserGroup, id=group_id)
|
|
||||||
util = AssetPermissionUtil(group)
|
|
||||||
nodes = util.get_nodes_with_assets()
|
|
||||||
return nodes.keys()
|
|
||||||
return queryset
|
|
||||||
|
|
||||||
|
|
||||||
class UserGroupGrantedNodesWithAssetsApi(ListAPIView):
|
class UserGroupGrantedNodesAsTreeApi(UserGrantedNodesAsTreeApi):
|
||||||
|
def get_object(self):
|
||||||
|
user_group_id = self.kwargs.get('pk', '')
|
||||||
|
user_group = get_object_or_404(UserGroup, id=user_group_id)
|
||||||
|
return user_group
|
||||||
|
|
||||||
|
|
||||||
|
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 = []
|
|
||||||
|
|
||||||
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
|
||||||
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):
|
class UserGroupGrantedNodesWithAssetsAsTreeApi(UserGrantedNodesWithAssetsAsTreeApi):
|
||||||
serializer_class = TreeNodeSerializer
|
def get_object(self):
|
||||||
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):
|
|
||||||
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)
|
||||||
group = get_object_or_404(UserGroup, id=user_group_id)
|
return user_group
|
||||||
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):
|
class UserGroupGrantedNodeAssetsApi(UserGrantedNodeAssetsApi):
|
||||||
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)
|
user_group = get_object_or_404(UserGroup, id=user_group_id)
|
||||||
util = AssetPermissionUtil(user_group)
|
return 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)
|
|
||||||
util = RemoteAppPermissionUtil(user_group)
|
|
||||||
queryset = util.get_remote_apps()
|
|
||||||
return queryset
|
|
||||||
|
|
|
@ -2,9 +2,9 @@
|
||||||
#
|
#
|
||||||
import time
|
import time
|
||||||
import traceback
|
import traceback
|
||||||
from hashlib import md5
|
from functools import reduce
|
||||||
from django.core.cache import cache
|
import uuid
|
||||||
from django.conf import settings
|
from django.db.models import Q
|
||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
from rest_framework.views import APIView, Response
|
from rest_framework.views import APIView, Response
|
||||||
from rest_framework.generics import (
|
from rest_framework.generics import (
|
||||||
|
@ -16,126 +16,30 @@ from common.permissions import IsValidUser, IsOrgAdminOrAppUser
|
||||||
from common.tree import TreeNodeSerializer
|
from common.tree import TreeNodeSerializer
|
||||||
from common.utils import get_logger
|
from common.utils import get_logger
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
AssetPermissionUtil, parse_asset_to_tree_node, parse_node_to_tree_node,
|
AssetPermissionUtil, ParserNode,
|
||||||
)
|
)
|
||||||
|
from .mixin import UserPermissionCacheMixin, GrantAssetsMixin, NodesWithUngroupMixin
|
||||||
|
from .. import const
|
||||||
from ..hands import User, Asset, Node, SystemUser, NodeSerializer
|
from ..hands import User, Asset, Node, SystemUser, NodeSerializer
|
||||||
from .. import serializers, const
|
from .. import serializers
|
||||||
from ..mixins import AssetsFilterMixin
|
|
||||||
from ..models import Action
|
from ..models import Action
|
||||||
|
|
||||||
|
|
||||||
logger = get_logger(__name__)
|
logger = get_logger(__name__)
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'UserGrantedAssetsApi', 'UserGrantedNodesApi',
|
'UserGrantedAssetsApi', 'UserGrantedNodesApi',
|
||||||
'UserGrantedNodesWithAssetsApi', 'UserGrantedNodeAssetsApi',
|
'UserGrantedNodesWithAssetsApi', 'UserGrantedNodeAssetsApi',
|
||||||
'ValidateUserAssetPermissionApi', 'UserGrantedNodeChildrenApi',
|
'ValidateUserAssetPermissionApi', 'UserGrantedNodesAsTreeApi',
|
||||||
'UserGrantedNodesWithAssetsAsTreeApi', 'GetUserAssetPermissionActionsApi',
|
'UserGrantedNodesWithAssetsAsTreeApi', 'GetUserAssetPermissionActionsApi',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class UserPermissionCacheMixin:
|
class UserGrantedAssetsApi(UserPermissionCacheMixin, GrantAssetsMixin, ListAPIView):
|
||||||
cache_policy = '0'
|
|
||||||
RESP_CACHE_KEY = '_PERMISSION_RESPONSE_CACHE_{}'
|
|
||||||
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 UserGrantedAssetsApi(UserPermissionCacheMixin, AssetsFilterMixin, ListAPIView):
|
|
||||||
"""
|
"""
|
||||||
用户授权的所有资产
|
用户授权的所有资产
|
||||||
"""
|
"""
|
||||||
permission_classes = (IsOrgAdminOrAppUser,)
|
permission_classes = (IsOrgAdminOrAppUser,)
|
||||||
serializer_class = serializers.AssetGrantedSerializer
|
|
||||||
pagination_class = LimitOffsetPagination
|
pagination_class = LimitOffsetPagination
|
||||||
|
|
||||||
def get_object(self):
|
def get_object(self):
|
||||||
|
@ -147,17 +51,9 @@ class UserGrantedAssetsApi(UserPermissionCacheMixin, AssetsFilterMixin, ListAPIV
|
||||||
return user
|
return user
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
queryset = []
|
|
||||||
user = self.get_object()
|
user = self.get_object()
|
||||||
util = AssetPermissionUtil(user, cache_policy=self.cache_policy)
|
util = AssetPermissionUtil(user, cache_policy=self.cache_policy)
|
||||||
assets = util.get_assets()
|
queryset = util.get_assets()
|
||||||
for asset, system_users in assets.items():
|
|
||||||
system_users_granted = []
|
|
||||||
for system_user, actions in system_users.items():
|
|
||||||
system_user.actions = actions
|
|
||||||
system_users_granted.append(system_user)
|
|
||||||
asset.system_users_granted = system_users_granted
|
|
||||||
queryset.append(asset)
|
|
||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
def get_permissions(self):
|
def get_permissions(self):
|
||||||
|
@ -166,120 +62,11 @@ class UserGrantedAssetsApi(UserPermissionCacheMixin, AssetsFilterMixin, ListAPIV
|
||||||
return super().get_permissions()
|
return super().get_permissions()
|
||||||
|
|
||||||
|
|
||||||
class UserGrantedNodesApi(UserPermissionCacheMixin, ListAPIView):
|
class UserGrantedNodeAssetsApi(UserPermissionCacheMixin, GrantAssetsMixin, ListAPIView):
|
||||||
"""
|
|
||||||
查询用户授权的所有节点的API
|
|
||||||
"""
|
|
||||||
permission_classes = (IsOrgAdminOrAppUser,)
|
|
||||||
serializer_class = NodeSerializer
|
|
||||||
|
|
||||||
def get_object(self):
|
|
||||||
user_id = self.kwargs.get('pk', '')
|
|
||||||
if user_id:
|
|
||||||
user = get_object_or_404(User, id=user_id)
|
|
||||||
else:
|
|
||||||
user = self.request.user
|
|
||||||
return user
|
|
||||||
|
|
||||||
def get_queryset(self):
|
|
||||||
user = self.get_object()
|
|
||||||
util = AssetPermissionUtil(user, cache_policy=self.cache_policy)
|
|
||||||
nodes = util.get_nodes()
|
|
||||||
return nodes
|
|
||||||
|
|
||||||
def get_permissions(self):
|
|
||||||
if self.kwargs.get('pk') is None:
|
|
||||||
self.permission_classes = (IsValidUser,)
|
|
||||||
return super().get_permissions()
|
|
||||||
|
|
||||||
|
|
||||||
class UserGrantedNodesWithAssetsApi(UserPermissionCacheMixin, AssetsFilterMixin, ListAPIView):
|
|
||||||
"""
|
|
||||||
用户授权的节点并带着节点下资产的api
|
|
||||||
"""
|
|
||||||
permission_classes = (IsOrgAdminOrAppUser,)
|
|
||||||
serializer_class = serializers.NodeGrantedSerializer
|
|
||||||
|
|
||||||
def get_object(self):
|
|
||||||
user_id = self.kwargs.get('pk', '')
|
|
||||||
if not user_id:
|
|
||||||
user = self.request.user
|
|
||||||
else:
|
|
||||||
user = get_object_or_404(User, id=user_id)
|
|
||||||
return user
|
|
||||||
|
|
||||||
def get_queryset(self):
|
|
||||||
queryset = []
|
|
||||||
user = self.get_object()
|
|
||||||
util = AssetPermissionUtil(user, cache_policy=self.cache_policy)
|
|
||||||
nodes = util.get_nodes_with_assets()
|
|
||||||
for node, _assets in nodes.items():
|
|
||||||
assets = _assets.keys()
|
|
||||||
for k, v in _assets.items():
|
|
||||||
k.system_users_granted = v
|
|
||||||
node.assets_granted = assets
|
|
||||||
queryset.append(node)
|
|
||||||
return queryset
|
|
||||||
|
|
||||||
def sort_assets(self, queryset):
|
|
||||||
for node in queryset:
|
|
||||||
node.assets_granted = super().sort_assets(node.assets_granted)
|
|
||||||
return queryset
|
|
||||||
|
|
||||||
def get_permissions(self):
|
|
||||||
if self.kwargs.get('pk') is None:
|
|
||||||
self.permission_classes = (IsValidUser,)
|
|
||||||
return super().get_permissions()
|
|
||||||
|
|
||||||
|
|
||||||
class UserGrantedNodesWithAssetsAsTreeApi(UserPermissionCacheMixin, ListAPIView):
|
|
||||||
serializer_class = TreeNodeSerializer
|
|
||||||
permission_classes = (IsOrgAdminOrAppUser,)
|
|
||||||
show_assets = True
|
|
||||||
system_user_id = None
|
|
||||||
|
|
||||||
def get_permissions(self):
|
|
||||||
if self.kwargs.get('pk') is None:
|
|
||||||
self.permission_classes = (IsValidUser,)
|
|
||||||
return super().get_permissions()
|
|
||||||
|
|
||||||
def get_object(self):
|
|
||||||
user_id = self.kwargs.get('pk', '')
|
|
||||||
if not user_id:
|
|
||||||
user = self.request.user
|
|
||||||
else:
|
|
||||||
user = get_object_or_404(User, id=user_id)
|
|
||||||
return user
|
|
||||||
|
|
||||||
def get_queryset(self):
|
|
||||||
queryset = []
|
|
||||||
self.show_assets = self.request.query_params.get('show_assets', '1') == '1'
|
|
||||||
self.system_user_id = self.request.query_params.get('system_user')
|
|
||||||
user = self.get_object()
|
|
||||||
util = AssetPermissionUtil(user, cache_policy=self.cache_policy)
|
|
||||||
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 UserGrantedNodeAssetsApi(UserPermissionCacheMixin, AssetsFilterMixin, ListAPIView):
|
|
||||||
"""
|
"""
|
||||||
查询用户授权的节点下的资产的api, 与上面api不同的是,只返回某个节点下的资产
|
查询用户授权的节点下的资产的api, 与上面api不同的是,只返回某个节点下的资产
|
||||||
"""
|
"""
|
||||||
permission_classes = (IsOrgAdminOrAppUser,)
|
permission_classes = (IsOrgAdminOrAppUser,)
|
||||||
serializer_class = serializers.AssetGrantedSerializer
|
|
||||||
pagination_class = LimitOffsetPagination
|
pagination_class = LimitOffsetPagination
|
||||||
|
|
||||||
def get_object(self):
|
def get_object(self):
|
||||||
|
@ -291,25 +78,30 @@ class UserGrantedNodeAssetsApi(UserPermissionCacheMixin, AssetsFilterMixin, List
|
||||||
user = self.request.user
|
user = self.request.user
|
||||||
return user
|
return user
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_node_key(self):
|
||||||
user = self.get_object()
|
|
||||||
node_id = self.kwargs.get('node_id')
|
node_id = self.kwargs.get('node_id')
|
||||||
util = AssetPermissionUtil(user, cache_policy=self.cache_policy)
|
|
||||||
nodes = util.get_nodes_with_assets()
|
|
||||||
if str(node_id) == const.UNGROUPED_NODE_ID:
|
if str(node_id) == const.UNGROUPED_NODE_ID:
|
||||||
node = util.tree.ungrouped_node
|
key = self.util.tree.ungrouped_key
|
||||||
elif str(node_id) == const.EMPTY_NODE_ID:
|
elif str(node_id) == const.EMPTY_NODE_ID:
|
||||||
node = util.tree.empty_node
|
key = const.EMPTY_NODE_KEY
|
||||||
else:
|
else:
|
||||||
node = get_object_or_404(Node, id=node_id)
|
node = get_object_or_404(Node, id=node_id)
|
||||||
if node == util.tree.root_node:
|
key = node.key
|
||||||
assets = util.get_assets()
|
return key
|
||||||
else:
|
|
||||||
assets = nodes.get(node, {})
|
|
||||||
for asset, system_users in assets.items():
|
|
||||||
asset.system_users_granted = system_users
|
|
||||||
|
|
||||||
assets = list(assets.keys())
|
def get_queryset(self):
|
||||||
|
user = self.get_object()
|
||||||
|
self.util = AssetPermissionUtil(user, cache_policy=self.cache_policy)
|
||||||
|
key = self.get_node_key()
|
||||||
|
nodes_items = self.util.get_nodes_with_assets()
|
||||||
|
assets_system_users = {}
|
||||||
|
for item in nodes_items:
|
||||||
|
if item["key"] == key:
|
||||||
|
assets_system_users = item["assets"]
|
||||||
|
break
|
||||||
|
assets = []
|
||||||
|
for asset_id, system_users in assets_system_users.items():
|
||||||
|
assets.append({"id": asset_id, "system_users": system_users})
|
||||||
return assets
|
return assets
|
||||||
|
|
||||||
def get_permissions(self):
|
def get_permissions(self):
|
||||||
|
@ -318,90 +110,202 @@ class UserGrantedNodeAssetsApi(UserPermissionCacheMixin, AssetsFilterMixin, List
|
||||||
return super().get_permissions()
|
return super().get_permissions()
|
||||||
|
|
||||||
|
|
||||||
class UserGrantedNodeChildrenApi(UserPermissionCacheMixin, ListAPIView):
|
class UserGrantedNodesApi(UserPermissionCacheMixin, NodesWithUngroupMixin, ListAPIView):
|
||||||
"""
|
"""
|
||||||
获取用户自己授权节点下子节点的api
|
查询用户授权的所有节点的API
|
||||||
"""
|
"""
|
||||||
permission_classes = (IsValidUser,)
|
permission_classes = (IsOrgAdminOrAppUser,)
|
||||||
serializer_class = serializers.AssetPermissionNodeSerializer
|
serializer_class = NodeSerializer
|
||||||
|
pagination_class = LimitOffsetPagination
|
||||||
|
only_fields = NodeSerializer.Meta.only_fields
|
||||||
|
|
||||||
def get_object(self):
|
def get_object(self):
|
||||||
return self.request.user
|
user_id = self.kwargs.get('pk', '')
|
||||||
|
if user_id:
|
||||||
def get_children_queryset(self):
|
user = get_object_or_404(User, id=user_id)
|
||||||
user = self.get_object()
|
|
||||||
util = AssetPermissionUtil(user, cache_policy=self.cache_policy)
|
|
||||||
node_id = self.request.query_params.get('id')
|
|
||||||
nodes_granted = util.get_nodes_with_assets()
|
|
||||||
if not nodes_granted:
|
|
||||||
return []
|
|
||||||
root_nodes = [node for node in nodes_granted.keys() if node.is_root()]
|
|
||||||
|
|
||||||
queryset = []
|
|
||||||
if node_id and node_id in [str(node.id) for node in nodes_granted]:
|
|
||||||
node = [node for node in nodes_granted if str(node.id) == node_id][0]
|
|
||||||
elif len(root_nodes) == 1:
|
|
||||||
node = root_nodes[0]
|
|
||||||
node.assets_amount = len(nodes_granted[node])
|
|
||||||
queryset.append(node)
|
|
||||||
else:
|
else:
|
||||||
for node in root_nodes:
|
user = self.request.user
|
||||||
node.assets_amount = len(nodes_granted[node])
|
return user
|
||||||
queryset.append(node)
|
|
||||||
return queryset
|
|
||||||
|
|
||||||
children = []
|
def get_nodes(self, nodes_with_assets):
|
||||||
for child in node.get_children():
|
node_keys = [n["key"] for n in nodes_with_assets]
|
||||||
if child in nodes_granted:
|
nodes = Node.objects.filter(key__in=node_keys).only(
|
||||||
child.assets_amount = len(nodes_granted[node])
|
*self.only_fields
|
||||||
children.append(child)
|
)
|
||||||
children = sorted(children, key=lambda x: x.value)
|
nodes_map = {n.key: n for n in nodes}
|
||||||
queryset.extend(children)
|
self.add_ungrouped_nodes(nodes_map, node_keys)
|
||||||
fake_nodes = []
|
|
||||||
for asset, system_users in nodes_granted[node].items():
|
|
||||||
fake_node = asset.as_node()
|
|
||||||
fake_node.assets_amount = 0
|
|
||||||
system_users = [s for s in system_users if asset.has_protocol(s.protocol)]
|
|
||||||
fake_node.asset.system_users_granted = system_users
|
|
||||||
fake_node.key = node.key + ':0'
|
|
||||||
fake_nodes.append(fake_node)
|
|
||||||
fake_nodes = sorted(fake_nodes, key=lambda x: x.value)
|
|
||||||
queryset.extend(fake_nodes)
|
|
||||||
return queryset
|
|
||||||
|
|
||||||
def get_search_queryset(self, keyword):
|
_nodes = []
|
||||||
user = self.get_object()
|
for n in nodes_with_assets:
|
||||||
util = AssetPermissionUtil(user, cache_policy=self.cache_policy)
|
key = n["key"]
|
||||||
nodes_granted = util.get_nodes_with_assets()
|
node = nodes_map.get(key)
|
||||||
queryset = []
|
node._assets_amount = n["assets_amount"]
|
||||||
for node, assets in nodes_granted.items():
|
_nodes.append(node)
|
||||||
matched_assets = []
|
return _nodes
|
||||||
node_matched = node.value.lower().find(keyword.lower()) >= 0
|
|
||||||
asset_has_matched = False
|
def get_serializer(self, nodes_with_assets, many=True):
|
||||||
for asset, system_users in assets.items():
|
nodes = self.get_nodes(nodes_with_assets)
|
||||||
asset_matched = (asset.hostname.lower().find(keyword.lower()) >= 0) \
|
return super().get_serializer(nodes, many=True)
|
||||||
or (asset.ip.find(keyword.lower()) >= 0)
|
|
||||||
if node_matched or asset_matched:
|
|
||||||
asset_has_matched = True
|
|
||||||
fake_node = asset.as_node()
|
|
||||||
fake_node.assets_amount = 0
|
|
||||||
system_users = [s for s in system_users if
|
|
||||||
asset.has_protocol(s.protocol)]
|
|
||||||
fake_node.asset.system_users_granted = system_users
|
|
||||||
fake_node.key = node.key + ':0'
|
|
||||||
matched_assets.append(fake_node)
|
|
||||||
if asset_has_matched:
|
|
||||||
node.assets_amount = len(matched_assets)
|
|
||||||
queryset.append(node)
|
|
||||||
queryset.extend(sorted(matched_assets, key=lambda x: x.value))
|
|
||||||
return queryset
|
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
keyword = self.request.query_params.get('search')
|
user = self.get_object()
|
||||||
if keyword:
|
self.util = AssetPermissionUtil(user, cache_policy=self.cache_policy)
|
||||||
return self.get_search_queryset(keyword)
|
nodes_with_assets = self.util.get_nodes_with_assets()
|
||||||
|
return nodes_with_assets
|
||||||
|
|
||||||
|
def get_permissions(self):
|
||||||
|
if self.kwargs.get('pk') is None:
|
||||||
|
self.permission_classes = (IsValidUser,)
|
||||||
|
return super().get_permissions()
|
||||||
|
|
||||||
|
|
||||||
|
class UserGrantedNodesAsTreeApi(UserGrantedNodesApi):
|
||||||
|
serializer_class = TreeNodeSerializer
|
||||||
|
only_fields = ParserNode.nodes_only_fields
|
||||||
|
|
||||||
|
def get_serializer(self, nodes_with_assets, many=True):
|
||||||
|
nodes = self.get_nodes(nodes_with_assets)
|
||||||
|
queryset = []
|
||||||
|
for node in nodes:
|
||||||
|
data = ParserNode.parse_node_to_tree_node(node)
|
||||||
|
queryset.append(data)
|
||||||
|
return self.get_serializer_class()(queryset, many=many)
|
||||||
|
|
||||||
|
|
||||||
|
class UserGrantedNodesWithAssetsApi(UserPermissionCacheMixin, NodesWithUngroupMixin, ListAPIView):
|
||||||
|
"""
|
||||||
|
用户授权的节点并带着节点下资产的api
|
||||||
|
"""
|
||||||
|
permission_classes = (IsOrgAdminOrAppUser,)
|
||||||
|
serializer_class = serializers.NodeGrantedSerializer
|
||||||
|
pagination_class = LimitOffsetPagination
|
||||||
|
|
||||||
|
nodes_only_fields = serializers.NodeGrantedSerializer.Meta.only_fields
|
||||||
|
assets_only_fields = serializers.NodeGrantedSerializer.assets_only_fields
|
||||||
|
system_users_only_fields = serializers.NodeGrantedSerializer.system_users_only_fields
|
||||||
|
|
||||||
|
def get_object(self):
|
||||||
|
user_id = self.kwargs.get('pk', '')
|
||||||
|
if not user_id:
|
||||||
|
user = self.request.user
|
||||||
else:
|
else:
|
||||||
return self.get_children_queryset()
|
user = get_object_or_404(User, id=user_id)
|
||||||
|
return user
|
||||||
|
|
||||||
|
def get_maps(self, nodes_items):
|
||||||
|
"""
|
||||||
|
查库,并加入构造的ungrouped节点
|
||||||
|
:return:
|
||||||
|
({asset.id: asset}, {node.key: node}, {system_user.id: system_user})
|
||||||
|
"""
|
||||||
|
_nodes_keys = set()
|
||||||
|
_assets_ids = set()
|
||||||
|
_system_users_ids = set()
|
||||||
|
for item in nodes_items:
|
||||||
|
_nodes_keys.add(item["key"])
|
||||||
|
_assets_ids.update(set(item["assets"].keys()))
|
||||||
|
for _system_users_id in item["assets"].values():
|
||||||
|
_system_users_ids.update(_system_users_id.keys())
|
||||||
|
|
||||||
|
_nodes = Node.objects.filter(key__in=_nodes_keys).only(
|
||||||
|
*self.nodes_only_fields
|
||||||
|
)
|
||||||
|
_assets = Asset.objects.filter(id__in=_assets_ids).only(
|
||||||
|
*self.assets_only_fields
|
||||||
|
)
|
||||||
|
_system_users = SystemUser.objects.filter(id__in=_system_users_ids).only(
|
||||||
|
*self.system_users_only_fields
|
||||||
|
)
|
||||||
|
_nodes_map = {n.key: n for n in _nodes}
|
||||||
|
self.add_ungrouped_nodes(_nodes_map, _nodes_keys)
|
||||||
|
_assets_map = {a.id: a for a in _assets}
|
||||||
|
_system_users_map = {s.id: s for s in _system_users}
|
||||||
|
return _nodes_map, _assets_map, _system_users_map
|
||||||
|
|
||||||
|
def get_serializer_queryset(self, nodes_items):
|
||||||
|
"""
|
||||||
|
将id转为object,同时构造queryset
|
||||||
|
:param nodes_items:
|
||||||
|
[
|
||||||
|
{
|
||||||
|
'key': node.key,
|
||||||
|
'assets_amount': 10
|
||||||
|
'assets': {
|
||||||
|
asset.id: {
|
||||||
|
system_user.id: actions,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
"""
|
||||||
|
queryset = []
|
||||||
|
_node_map, _assets_map, _system_users_map = self.get_maps(nodes_items)
|
||||||
|
for item in nodes_items:
|
||||||
|
key = item["key"]
|
||||||
|
node = _node_map.get(key)
|
||||||
|
if not node:
|
||||||
|
continue
|
||||||
|
node._assets_amount = item["assets_amount"]
|
||||||
|
assets_granted = []
|
||||||
|
for asset_id, system_users_ids_action in item["assets"].items():
|
||||||
|
asset = _assets_map.get(asset_id)
|
||||||
|
if not asset:
|
||||||
|
continue
|
||||||
|
system_user_granted = []
|
||||||
|
for system_user_id, action in system_users_ids_action.items():
|
||||||
|
system_user = _system_users_map.get(system_user_id)
|
||||||
|
if not system_user:
|
||||||
|
continue
|
||||||
|
system_user.actions = action
|
||||||
|
system_user_granted.append(system_user)
|
||||||
|
asset.system_users_granted = system_user_granted
|
||||||
|
assets_granted.append(asset)
|
||||||
|
node.assets_granted = assets_granted
|
||||||
|
queryset.append(node)
|
||||||
|
return queryset
|
||||||
|
|
||||||
|
def get_serializer(self, nodes_items, many=True):
|
||||||
|
queryset = self.get_serializer_queryset(nodes_items)
|
||||||
|
return super().get_serializer(queryset, many=many)
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
user = self.get_object()
|
||||||
|
self.util = AssetPermissionUtil(user, cache_policy=self.cache_policy)
|
||||||
|
system_user_id = self.request.query_params.get('system_user')
|
||||||
|
if system_user_id:
|
||||||
|
self.util.filter_permissions(
|
||||||
|
system_users=system_user_id
|
||||||
|
)
|
||||||
|
nodes_items = self.util.get_nodes_with_assets()
|
||||||
|
return nodes_items
|
||||||
|
|
||||||
|
def get_permissions(self):
|
||||||
|
if self.kwargs.get('pk') is None:
|
||||||
|
self.permission_classes = (IsValidUser,)
|
||||||
|
return super().get_permissions()
|
||||||
|
|
||||||
|
|
||||||
|
class UserGrantedNodesWithAssetsAsTreeApi(UserGrantedNodesWithAssetsApi):
|
||||||
|
serializer_class = TreeNodeSerializer
|
||||||
|
permission_classes = (IsOrgAdminOrAppUser,)
|
||||||
|
system_user_id = None
|
||||||
|
nodes_only_fields = ParserNode.nodes_only_fields
|
||||||
|
assets_only_fields = ParserNode.assets_only_fields
|
||||||
|
system_users_only_fields = ParserNode.system_users_only_fields
|
||||||
|
|
||||||
|
def get_serializer(self, nodes_items, many=True):
|
||||||
|
_queryset = super().get_serializer_queryset(nodes_items)
|
||||||
|
queryset = []
|
||||||
|
|
||||||
|
for node in _queryset:
|
||||||
|
data = ParserNode.parse_node_to_tree_node(node)
|
||||||
|
queryset.append(data)
|
||||||
|
for asset in node.assets_granted:
|
||||||
|
system_users = asset.system_users_granted
|
||||||
|
data = ParserNode.parse_asset_to_tree_node(node, asset, system_users)
|
||||||
|
queryset.append(data)
|
||||||
|
queryset = sorted(queryset)
|
||||||
|
return self.serializer_class(queryset, many=True)
|
||||||
|
|
||||||
|
|
||||||
class ValidateUserAssetPermissionApi(UserPermissionCacheMixin, APIView):
|
class ValidateUserAssetPermissionApi(UserPermissionCacheMixin, APIView):
|
||||||
|
@ -412,24 +316,24 @@ class ValidateUserAssetPermissionApi(UserPermissionCacheMixin, APIView):
|
||||||
asset_id = request.query_params.get('asset_id', '')
|
asset_id = request.query_params.get('asset_id', '')
|
||||||
system_id = request.query_params.get('system_user_id', '')
|
system_id = request.query_params.get('system_user_id', '')
|
||||||
action_name = request.query_params.get('action_name', '')
|
action_name = request.query_params.get('action_name', '')
|
||||||
|
cache_policy = self.request.query_params.get("cache_policy", '0')
|
||||||
|
|
||||||
|
try:
|
||||||
|
asset_id = uuid.UUID(asset_id)
|
||||||
|
system_id = uuid.UUID(system_id)
|
||||||
|
except ValueError:
|
||||||
|
return Response({'msg': False}, status=403)
|
||||||
|
|
||||||
user = get_object_or_404(User, id=user_id)
|
user = get_object_or_404(User, id=user_id)
|
||||||
asset = get_object_or_404(Asset, id=asset_id)
|
util = AssetPermissionUtil(user, cache_policy=cache_policy)
|
||||||
su = get_object_or_404(SystemUser, id=system_id)
|
assets = util.get_assets()
|
||||||
|
for asset in assets:
|
||||||
util = AssetPermissionUtil(user, cache_policy=self.cache_policy)
|
if asset_id == asset["id"]:
|
||||||
granted_assets = util.get_assets()
|
action = asset["system_users"].get(system_id)
|
||||||
granted_system_users = granted_assets.get(asset, {})
|
if action and action_name in Action.value_to_choices(action):
|
||||||
|
|
||||||
if su not in granted_system_users:
|
|
||||||
return Response({'msg': False}, status=403)
|
|
||||||
|
|
||||||
action = granted_system_users[su]
|
|
||||||
choices = Action.value_to_choices(action)
|
|
||||||
if action_name not in choices:
|
|
||||||
return Response({'msg': False}, status=403)
|
|
||||||
|
|
||||||
return Response({'msg': True}, status=200)
|
return Response({'msg': True}, status=200)
|
||||||
|
break
|
||||||
|
return Response({'msg': False}, status=403)
|
||||||
|
|
||||||
|
|
||||||
class GetUserAssetPermissionActionsApi(UserPermissionCacheMixin, RetrieveAPIView):
|
class GetUserAssetPermissionActionsApi(UserPermissionCacheMixin, RetrieveAPIView):
|
||||||
|
@ -442,16 +346,12 @@ class GetUserAssetPermissionActionsApi(UserPermissionCacheMixin, RetrieveAPIView
|
||||||
system_id = self.request.query_params.get('system_user_id', '')
|
system_id = self.request.query_params.get('system_user_id', '')
|
||||||
|
|
||||||
user = get_object_or_404(User, id=user_id)
|
user = get_object_or_404(User, id=user_id)
|
||||||
asset = get_object_or_404(Asset, id=asset_id)
|
|
||||||
su = get_object_or_404(SystemUser, id=system_id)
|
|
||||||
|
|
||||||
util = AssetPermissionUtil(user, cache_policy=self.cache_policy)
|
util = AssetPermissionUtil(user, cache_policy=self.cache_policy)
|
||||||
granted_assets = util.get_assets()
|
assets = util.get_assets()
|
||||||
granted_system_users = granted_assets.get(asset, {})
|
actions = 0
|
||||||
|
for asset in assets:
|
||||||
_object = {}
|
if asset_id == asset["id"]:
|
||||||
if su not in granted_system_users:
|
actions = asset["system_users"].get(system_id, 0)
|
||||||
_object['actions'] = 0
|
break
|
||||||
else:
|
return {"actions": actions}
|
||||||
_object['actions'] = granted_system_users[su]
|
|
||||||
return _object
|
|
||||||
|
|
|
@ -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):
|
||||||
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 @@
|
||||||
|
|
||||||
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):
|
||||||
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):
|
||||||
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 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
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
|
||||||
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):
|
||||||
|
|
||||||
@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
|
||||||
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):
|
||||||
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 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
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,87 +23,56 @@ 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
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Asset
|
||||||
|
only_fields = [
|
||||||
|
"id", "hostname", "ip", "protocols", "os", 'domain',
|
||||||
|
"platform", "org_id",
|
||||||
|
]
|
||||||
|
fields = only_fields + ['system_users_granted', 'system_users_join', "org_name"]
|
||||||
|
read_only_fields = fields
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_system_users_join(obj):
|
def get_system_users_join(obj):
|
||||||
system_users = [s.username for s in obj.system_users_granted]
|
system_users = [s.username for s in obj.system_users_granted]
|
||||||
return ', '.join(system_users)
|
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:
|
|
||||||
model = Node
|
|
||||||
fields = [
|
|
||||||
'id', 'key', 'value', 'asset', 'is_node', 'org_id',
|
|
||||||
'tree_id', 'tree_parent', 'assets_amount',
|
|
||||||
]
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_assets_amount(obj):
|
|
||||||
return obj.assets_amount
|
|
||||||
|
|
||||||
@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):
|
||||||
"""
|
"""
|
||||||
授权资产组
|
授权资产组
|
||||||
"""
|
"""
|
||||||
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):
|
||||||
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 @@
|
||||||
</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>
|
||||||
|
<th class="text-center">{% trans 'Hostname' %}</th>
|
||||||
|
<th class="text-center">{% trans 'IP' %}</th>
|
||||||
</tr>
|
</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 @@
|
||||||
<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 @@
|
||||||
</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) {
|
||||||
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) {
|
||||||
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) {
|
||||||
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 () {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
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 @@
|
||||||
</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) {
|
||||||
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 () {
|
||||||
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) {
|
||||||
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) {
|
||||||
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) {
|
||||||
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 () {
|
||||||
$('#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 %}
|
|
@ -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 @@
|
||||||
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 @@
|
||||||
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 @@
|
||||||
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 @@
|
||||||
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 @@
|
||||||
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)
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
# coding:utf-8
|
# coding:utf-8
|
||||||
|
|
||||||
from django.urls import path
|
from django.urls import path, re_path
|
||||||
from rest_framework import routers
|
from rest_framework import routers
|
||||||
|
from common import api as capi
|
||||||
from .. import api
|
from .. import api
|
||||||
|
|
||||||
app_name = 'perms'
|
app_name = 'perms'
|
||||||
|
@ -12,26 +13,37 @@ router.register('remote-app-permissions', api.RemoteAppPermissionViewSet, 'remot
|
||||||
|
|
||||||
|
|
||||||
asset_permission_urlpatterns = [
|
asset_permission_urlpatterns = [
|
||||||
# 查询某个用户授权的资产和资产组
|
# Assets
|
||||||
path('user/<uuid:pk>/assets/', api.UserGrantedAssetsApi.as_view()),
|
|
||||||
path('users/<uuid:pk>/assets/', api.UserGrantedAssetsApi.as_view(), name='user-assets'),
|
path('users/<uuid:pk>/assets/', api.UserGrantedAssetsApi.as_view(), name='user-assets'),
|
||||||
path('user/assets/', api.UserGrantedAssetsApi.as_view(), name='my-assets'),
|
path('users/assets/', api.UserGrantedAssetsApi.as_view(), name='my-assets'),
|
||||||
path('user/<uuid:pk>/nodes/', api.UserGrantedNodesApi.as_view(), name='user-nodes'),
|
|
||||||
path('user/nodes/', api.UserGrantedNodesApi.as_view(), name='my-nodes'),
|
# Node as tree
|
||||||
path('user/nodes/children/', api.UserGrantedNodeChildrenApi.as_view(), name='my-node-children'),
|
path('users/<uuid:pk>/nodes/tree/', api.UserGrantedNodesAsTreeApi.as_view(), name='user-nodes-as-tree'),
|
||||||
path('user/<uuid:pk>/nodes/<uuid:node_id>/assets/', api.UserGrantedNodeAssetsApi.as_view(), name='user-node-assets'),
|
path('users/nodes/tree/', api.UserGrantedNodesAsTreeApi.as_view(), name='my-nodes-as-tree'),
|
||||||
path('user/nodes/<uuid:node_id>/assets/', api.UserGrantedNodeAssetsApi.as_view(), name='my-node-assets'),
|
|
||||||
path('user/<uuid:pk>/nodes-assets/', api.UserGrantedNodesWithAssetsApi.as_view(), name='user-nodes-assets'),
|
# Nodes
|
||||||
path('user/nodes-assets/', api.UserGrantedNodesWithAssetsApi.as_view(), name='my-nodes-assets'),
|
path('users/<uuid:pk>/nodes/', api.UserGrantedNodesApi.as_view(), name='user-nodes'),
|
||||||
path('user/<uuid:pk>/nodes-assets/tree/', api.UserGrantedNodesWithAssetsAsTreeApi.as_view(), name='user-nodes-assets-as-tree'),
|
path('users/nodes/', api.UserGrantedNodesApi.as_view(), name='my-nodes'),
|
||||||
path('user/nodes-assets/tree/', api.UserGrantedNodesWithAssetsAsTreeApi.as_view(), name='my-nodes-assets-as-tree'),
|
|
||||||
|
# Node assets
|
||||||
|
path('users/<uuid:pk>/nodes/<uuid:node_id>/assets/', api.UserGrantedNodeAssetsApi.as_view(), name='user-node-assets'),
|
||||||
|
path('users/nodes/<uuid:node_id>/assets/', api.UserGrantedNodeAssetsApi.as_view(), name='my-node-assets'),
|
||||||
|
|
||||||
|
# Node with assets
|
||||||
|
path('users/<uuid:pk>/nodes-assets/', api.UserGrantedNodesWithAssetsApi.as_view(), name='user-nodes-assets'),
|
||||||
|
path('users/nodes-assets/', api.UserGrantedNodesWithAssetsApi.as_view(), name='my-nodes-assets'),
|
||||||
|
|
||||||
|
# Node assets as tree
|
||||||
|
path('users/<uuid:pk>/nodes-assets/tree/', api.UserGrantedNodesWithAssetsAsTreeApi.as_view(), name='user-nodes-assets-as-tree'),
|
||||||
|
path('users/nodes-assets/tree/', api.UserGrantedNodesWithAssetsAsTreeApi.as_view(), name='my-nodes-assets-as-tree'),
|
||||||
|
|
||||||
# 查询某个用户组授权的资产和资产组
|
# 查询某个用户组授权的资产和资产组
|
||||||
path('user-group/<uuid:pk>/assets/', api.UserGroupGrantedAssetsApi.as_view(), name='user-group-assets'),
|
path('user-groups/<uuid:pk>/assets/', api.UserGroupGrantedAssetsApi.as_view(), name='user-group-assets'),
|
||||||
path('user-group/<uuid:pk>/nodes/', api.UserGroupGrantedNodesApi.as_view(), name='user-group-nodes'),
|
path('user-groups/<uuid:pk>/nodes/tree/', api.UserGroupGrantedNodesApi.as_view(), name='user-group-nodes'),
|
||||||
path('user-group/<uuid:pk>/nodes-assets/', api.UserGroupGrantedNodesWithAssetsApi.as_view(), name='user-group-nodes-assets'),
|
path('user-groups/<uuid:pk>/nodes/', api.UserGroupGrantedNodesAsTreeApi.as_view(), name='user-group-nodes-as-tree'),
|
||||||
path('user-group/<uuid:pk>/nodes-assets/tree/', api.UserGroupGrantedNodesWithAssetsAsTreeApi.as_view(), name='user-group-nodes-assets-as-tree'),
|
path('user-groups/<uuid:pk>/nodes-assets/tree/', api.UserGroupGrantedNodesWithAssetsAsTreeApi.as_view(), name='user-group-nodes-assets-as-tree'),
|
||||||
path('user-group/<uuid:pk>/nodes/<uuid:node_id>/assets/', api.UserGroupGrantedNodeAssetsApi.as_view(), name='user-group-node-assets'),
|
path('user-groups/<uuid:pk>/nodes-assets/', api.UserGroupGrantedNodesWithAssetsApi.as_view(), name='user-group-nodes-assets'),
|
||||||
|
path('user-groups/<uuid:pk>/nodes/<uuid:node_id>/assets/', api.UserGroupGrantedNodeAssetsApi.as_view(), name='user-group-node-assets'),
|
||||||
|
|
||||||
# 用户和资产授权变更
|
# 用户和资产授权变更
|
||||||
path('asset-permissions/<uuid:pk>/user/remove/', api.AssetPermissionRemoveUserApi.as_view(), name='asset-permission-remove-user'),
|
path('asset-permissions/<uuid:pk>/user/remove/', api.AssetPermissionRemoveUserApi.as_view(), name='asset-permission-remove-user'),
|
||||||
|
@ -39,26 +51,29 @@ asset_permission_urlpatterns = [
|
||||||
path('asset-permissions/<uuid:pk>/asset/remove/', api.AssetPermissionRemoveAssetApi.as_view(), name='asset-permission-remove-asset'),
|
path('asset-permissions/<uuid:pk>/asset/remove/', api.AssetPermissionRemoveAssetApi.as_view(), name='asset-permission-remove-asset'),
|
||||||
path('asset-permissions/<uuid:pk>/asset/add/', api.AssetPermissionAddAssetApi.as_view(), name='asset-permission-add-asset'),
|
path('asset-permissions/<uuid:pk>/asset/add/', api.AssetPermissionAddAssetApi.as_view(), name='asset-permission-add-asset'),
|
||||||
|
|
||||||
|
# 授权规则中授权的资产
|
||||||
|
path('asset-permissions/<uuid:pk>/assets/', api.AssetPermissionAssetsApi.as_view(), name='asset-permission-assets'),
|
||||||
|
|
||||||
# 验证用户是否有某个资产和系统用户的权限
|
# 验证用户是否有某个资产和系统用户的权限
|
||||||
path('asset-permission/user/validate/', api.ValidateUserAssetPermissionApi.as_view(), name='validate-user-asset-permission'),
|
path('asset-permissions/user/validate/', api.ValidateUserAssetPermissionApi.as_view(), name='validate-user-asset-permission'),
|
||||||
path('asset-permission/user/actions/', api.GetUserAssetPermissionActionsApi.as_view(), name='get-user-asset-permission-actions'),
|
path('asset-permissions/user/actions/', api.GetUserAssetPermissionActionsApi.as_view(), name='get-user-asset-permission-actions'),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
remote_app_permission_urlpatterns = [
|
remote_app_permission_urlpatterns = [
|
||||||
# 查询用户授权的RemoteApp
|
# 查询用户授权的RemoteApp
|
||||||
path('user/<uuid:pk>/remote-apps/', api.UserGrantedRemoteAppsApi.as_view(), name='user-remote-apps'),
|
path('users/<uuid:pk>/remote-apps/', api.UserGrantedRemoteAppsApi.as_view(), name='user-remote-apps'),
|
||||||
path('user/remote-apps/', api.UserGrantedRemoteAppsApi.as_view(), name='my-remote-apps'),
|
path('users/remote-apps/', api.UserGrantedRemoteAppsApi.as_view(), name='my-remote-apps'),
|
||||||
|
|
||||||
# 获取用户授权的RemoteApp树
|
# 获取用户授权的RemoteApp树
|
||||||
path('user/<uuid:pk>/remote-apps/tree/', api.UserGrantedRemoteAppsAsTreeApi.as_view(), name='user-remote-apps-as-tree'),
|
path('users/<uuid:pk>/remote-apps/tree/', api.UserGrantedRemoteAppsAsTreeApi.as_view(), name='user-remote-apps-as-tree'),
|
||||||
path('user/remote-apps/tree/', api.UserGrantedRemoteAppsAsTreeApi.as_view(), name='my-remote-apps-as-tree'),
|
path('users/remote-apps/tree/', api.UserGrantedRemoteAppsAsTreeApi.as_view(), name='my-remote-apps-as-tree'),
|
||||||
|
|
||||||
# 查询用户组授权的RemoteApp
|
# 查询用户组授权的RemoteApp
|
||||||
path('user-group/<uuid:pk>/remote-apps/', api.UserGroupGrantedRemoteAppsApi.as_view(), name='user-group-remote-apps'),
|
path('user-groups/<uuid:pk>/remote-apps/', api.UserGroupGrantedRemoteAppsApi.as_view(), name='user-group-remote-apps'),
|
||||||
|
|
||||||
# 校验用户对RemoteApp的权限
|
# 校验用户对RemoteApp的权限
|
||||||
path('remote-app-permission/user/validate/', api.ValidateUserRemoteAppPermissionApi.as_view(), name='validate-user-remote-app-permission'),
|
path('remote-app-permissions/user/validate/', api.ValidateUserRemoteAppPermissionApi.as_view(), name='validate-user-remote-app-permission'),
|
||||||
|
|
||||||
# 用户和RemoteApp变更
|
# 用户和RemoteApp变更
|
||||||
path('remote-app-permissions/<uuid:pk>/user/add/', api.RemoteAppPermissionAddUserApi.as_view(), name='remote-app-permission-add-user'),
|
path('remote-app-permissions/<uuid:pk>/user/add/', api.RemoteAppPermissionAddUserApi.as_view(), name='remote-app-permission-add-user'),
|
||||||
|
@ -67,7 +82,11 @@ remote_app_permission_urlpatterns = [
|
||||||
path('remote-app-permissions/<uuid:pk>/remote-app/add/', api.RemoteAppPermissionAddRemoteAppApi.as_view(), name='remote-app-permission-add-remote-app'),
|
path('remote-app-permissions/<uuid:pk>/remote-app/add/', api.RemoteAppPermissionAddRemoteAppApi.as_view(), name='remote-app-permission-add-remote-app'),
|
||||||
]
|
]
|
||||||
|
|
||||||
urlpatterns = asset_permission_urlpatterns + remote_app_permission_urlpatterns
|
old_version_urlpatterns = [
|
||||||
|
re_path('(?P<resource>user|user-group|asset-permission|remote-app-permission)/.*', capi.redirect_plural_name_api)
|
||||||
|
]
|
||||||
|
|
||||||
|
urlpatterns = asset_permission_urlpatterns + remote_app_permission_urlpatterns + old_version_urlpatterns
|
||||||
|
|
||||||
urlpatterns += router.urls
|
urlpatterns += router.urls
|
||||||
|
|
||||||
|
|
|
@ -1,80 +1,65 @@
|
||||||
# coding: utf-8
|
# coding: utf-8
|
||||||
|
|
||||||
|
import time
|
||||||
import uuid
|
import uuid
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
import json
|
import json
|
||||||
from hashlib import md5
|
from hashlib import md5
|
||||||
import time
|
|
||||||
import itertools
|
import itertools
|
||||||
|
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
from django.core.cache import cache
|
from django.core.cache import cache
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.utils.translation import ugettext as _
|
|
||||||
|
|
||||||
from orgs.utils import set_to_root_org
|
from orgs.utils import set_to_root_org
|
||||||
from common.utils import get_logger
|
from common.utils import get_logger, timeit
|
||||||
from common.tree import TreeNode
|
from common.tree import TreeNode
|
||||||
|
from assets.utils import NodeUtil
|
||||||
from .. import const
|
from .. import const
|
||||||
from ..models import AssetPermission, Action
|
from ..models import AssetPermission, Action
|
||||||
from ..hands import Node, Asset
|
from ..hands import Node, Asset
|
||||||
from assets.utils import NodeUtil
|
from .stack import PermSystemUserNodeUtil, PermAssetsAmountUtil
|
||||||
|
|
||||||
logger = get_logger(__file__)
|
logger = get_logger(__file__)
|
||||||
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'AssetPermissionUtil', 'is_obj_attr_has', 'sort_assets',
|
'AssetPermissionUtil', 'is_obj_attr_has', 'sort_assets',
|
||||||
'parse_asset_to_tree_node', 'parse_node_to_tree_node',
|
'ParserNode',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class TreeNodeCounter(NodeUtil):
|
|
||||||
def __init__(self, nodes):
|
|
||||||
self.__nodes = nodes
|
|
||||||
super().__init__(with_assets_amount=True)
|
|
||||||
|
|
||||||
def get_queryset(self):
|
|
||||||
return self.__nodes
|
|
||||||
|
|
||||||
|
|
||||||
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
|
|
||||||
logger.debug("Call {} end, using: {:.2}s".format(func.__name__, using))
|
|
||||||
return result
|
|
||||||
return wrapper
|
|
||||||
|
|
||||||
|
|
||||||
class GenerateTree:
|
class GenerateTree:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
"""
|
"""
|
||||||
nodes = {
|
nodes = {
|
||||||
"<node1>": {
|
node.key: {
|
||||||
"system_users": {
|
"system_users": {
|
||||||
"system_user": action,
|
system_user.id: actions,
|
||||||
"system_user2": action,
|
},
|
||||||
|
"assets": set([asset.id,]),
|
||||||
},
|
},
|
||||||
"assets": set([<asset_instance>]),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
assets = {
|
assets = {
|
||||||
"<asset_instance2>": {
|
asset.id: {
|
||||||
"system_user": action,
|
system_user.id: actions,
|
||||||
"system_user2": action,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
self._node_util = None
|
self._node_util = None
|
||||||
self.nodes = defaultdict(lambda: {"system_users": defaultdict(int), "assets": set(), "assets_amount": 0})
|
self.nodes = defaultdict(lambda: {
|
||||||
|
"system_users": defaultdict(int), "assets": set(),
|
||||||
|
"assets_amount": 0, "all_assets": set(),
|
||||||
|
})
|
||||||
self.assets = defaultdict(lambda: defaultdict(int))
|
self.assets = defaultdict(lambda: defaultdict(int))
|
||||||
self._root_node = None
|
self._root_node = None
|
||||||
self._ungroup_node = None
|
self._ungroup_node = None
|
||||||
self._nodes_with_assets = None
|
self._nodes_with_assets = None
|
||||||
|
self._all_assets_nodes_key = None
|
||||||
|
self._asset_counter = 0
|
||||||
|
self._system_user_counter = 0
|
||||||
|
self._nodes_assets_counter = 0
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def node_util(self):
|
def node_util(self):
|
||||||
|
@ -82,115 +67,160 @@ class GenerateTree:
|
||||||
self._node_util = NodeUtil()
|
self._node_util = NodeUtil()
|
||||||
return self._node_util
|
return self._node_util
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def key_sort(key):
|
||||||
|
key_list = [int(i) for i in key.split(':')]
|
||||||
|
return len(key_list), key_list
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def root_node(self):
|
def root_key(self):
|
||||||
if self._root_node:
|
if self._root_node:
|
||||||
return self._root_node
|
return self._root_node
|
||||||
all_nodes = self.nodes.keys()
|
all_keys = self.nodes.keys()
|
||||||
# 如果没有授权节点,就放到默认的根节点下
|
# 如果没有授权节点,就放到默认的根节点下
|
||||||
if not all_nodes:
|
if not all_keys:
|
||||||
return None
|
return None
|
||||||
root_node = min(all_nodes)
|
root_key = min(all_keys, key=self.key_sort)
|
||||||
self._root_node = root_node
|
self._root_key = root_key
|
||||||
return root_node
|
return root_key
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def ungrouped_node(self):
|
def all_assets_nodes_keys(self):
|
||||||
|
if not self._all_assets_nodes_key:
|
||||||
|
self._all_assets_nodes_key = Asset.get_all_nodes_keys()
|
||||||
|
return self._all_assets_nodes_key
|
||||||
|
|
||||||
|
@property
|
||||||
|
def ungrouped_key(self):
|
||||||
if self._ungroup_node:
|
if self._ungroup_node:
|
||||||
return self._ungroup_node
|
return self._ungroup_node
|
||||||
node_id = const.UNGROUPED_NODE_ID
|
if self.root_key:
|
||||||
if self.root_node:
|
node_key = "{}:{}".format(self.root_key, '-1')
|
||||||
node_key = "{}:{}".format(self.root_node.key, self.root_node.child_mark)
|
|
||||||
else:
|
else:
|
||||||
node_key = '0:0'
|
node_key = '1:-1'
|
||||||
node_value = _("Default")
|
self._ungroup_node = node_key
|
||||||
node = Node(id=node_id, key=node_key, value=node_value)
|
return node_key
|
||||||
self.add_node(node, {})
|
|
||||||
self._ungroup_node = node
|
|
||||||
return node
|
|
||||||
|
|
||||||
@property
|
@timeit
|
||||||
def empty_node(self):
|
def add_assets_without_system_users(self, assets_ids):
|
||||||
node_id = const.EMPTY_NODE_ID
|
for asset_id in assets_ids:
|
||||||
value = _('Empty')
|
self.add_asset(asset_id, {})
|
||||||
node = Node(id=node_id, value=value)
|
|
||||||
return node
|
|
||||||
|
|
||||||
#@timeit
|
@timeit
|
||||||
def add_assets_without_system_users(self, assets):
|
def add_assets(self, assets_ids_with_system_users):
|
||||||
for asset in assets:
|
for asset_id, system_users_ids in assets_ids_with_system_users.items():
|
||||||
self.add_asset(asset, {})
|
self.add_asset(asset_id, system_users_ids)
|
||||||
|
|
||||||
#@timeit
|
# @timeit
|
||||||
def add_assets(self, assets):
|
def add_asset(self, asset_id, system_users_ids=None):
|
||||||
for asset, system_users in assets.items():
|
"""
|
||||||
self.add_asset(asset, system_users)
|
:param asset_id:
|
||||||
|
:param system_users_ids: {system_user.id: actions, }
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
if not system_users_ids:
|
||||||
|
system_users_ids = defaultdict(int)
|
||||||
|
|
||||||
# #@timeit
|
# 获取已有资产的系统用户和actions,并更新到最新系统用户信息中
|
||||||
def add_asset(self, asset, system_users=None):
|
old_system_users_ids = self.assets[asset_id]
|
||||||
nodes = asset.nodes.all()
|
for system_user_id, action in old_system_users_ids.items():
|
||||||
nodes = self.node_util.get_nodes_by_queryset(nodes)
|
system_users_ids[system_user_id] |= action
|
||||||
if not system_users:
|
|
||||||
system_users = defaultdict(int)
|
|
||||||
else:
|
|
||||||
system_users = {k: v for k, v in system_users.items()}
|
|
||||||
_system_users = self.assets[asset]
|
|
||||||
for system_user, action in _system_users.items():
|
|
||||||
system_users[system_user] |= action
|
|
||||||
|
|
||||||
# 获取父节点们
|
asset_nodes_keys = self.all_assets_nodes_keys.get(asset_id, [])
|
||||||
parents = self.node_util.get_nodes_parents(nodes, with_self=True)
|
# {asset.id: [node.key, ], }
|
||||||
for node in parents:
|
# 获取用户在的节点
|
||||||
_system_users = self.nodes[node]["system_users"]
|
in_nodes = set(self.nodes.keys()) & set(asset_nodes_keys)
|
||||||
self.nodes[node]["assets_amount"] += 1
|
|
||||||
for system_user, action in _system_users.items():
|
|
||||||
system_users[system_user] |= action
|
|
||||||
|
|
||||||
# 过滤系统用户的协议
|
|
||||||
system_users = {s: v for s, v in system_users.items() if asset.has_protocol(s.protocol)}
|
|
||||||
self.assets[asset] = system_users
|
|
||||||
|
|
||||||
in_nodes = set(self.nodes.keys()) & set(nodes)
|
|
||||||
if not in_nodes:
|
if not in_nodes:
|
||||||
self.nodes[self.ungrouped_node]["assets_amount"] += 1
|
self.nodes[self.ungrouped_key]["assets"].add(asset_id)
|
||||||
self.nodes[self.ungrouped_node]["assets"].add(system_users)
|
self.assets[asset_id] = system_users_ids
|
||||||
return
|
return
|
||||||
|
|
||||||
for node in in_nodes:
|
# 遍历用户应该在的节点
|
||||||
self.nodes[node]["assets"].add(asset)
|
for key in in_nodes:
|
||||||
|
# 把自己加入到树上的节点中
|
||||||
|
self.nodes[key]["assets"].add(asset_id)
|
||||||
|
# 获取自己所在节点的系统用户,并添加进去
|
||||||
|
node_system_users_ids = self.nodes[key]["system_users"]
|
||||||
|
for system_user_id, action in node_system_users_ids.items():
|
||||||
|
system_users_ids[system_user_id] |= action
|
||||||
|
self.assets[asset_id] = system_users_ids
|
||||||
|
|
||||||
def add_node(self, node, system_users=None):
|
def add_node(self, node_key, system_users_ids=None):
|
||||||
if not system_users:
|
"""
|
||||||
system_users = defaultdict(int)
|
:param node_key: node.key
|
||||||
self.nodes[node]["system_users"] = system_users
|
:param system_users_ids: {system_user.id: actions,}
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
if not system_users_ids:
|
||||||
|
system_users_ids = defaultdict(int)
|
||||||
|
self.nodes[node_key]["system_users"] = system_users_ids
|
||||||
|
|
||||||
# 添加树节点
|
# 添加树节点
|
||||||
#@timeit
|
@timeit
|
||||||
def add_nodes(self, nodes):
|
def add_nodes(self, nodes_keys_with_system_users_ids):
|
||||||
_nodes = nodes.keys()
|
"""
|
||||||
family = self.node_util.get_family(_nodes, with_children=True)
|
:param nodes_keys_with_system_users_ids:
|
||||||
for node in family:
|
{node.key: {system_user.id: actions,}, }
|
||||||
self.add_node(node, nodes.get(node, {}))
|
:return:
|
||||||
|
"""
|
||||||
|
util = PermSystemUserNodeUtil()
|
||||||
|
family = util.get_nodes_family_and_system_users(nodes_keys_with_system_users_ids)
|
||||||
|
for key, system_users in family.items():
|
||||||
|
self.add_node(key, system_users)
|
||||||
|
|
||||||
def get_assets(self):
|
def get_assets(self):
|
||||||
return dict(self.assets)
|
"""
|
||||||
|
:return:
|
||||||
|
[
|
||||||
|
{"id": asset.id, "system_users": {system_user.id: actions, }},
|
||||||
|
]
|
||||||
|
"""
|
||||||
|
assets = []
|
||||||
|
for asset_id, system_users in self.assets.items():
|
||||||
|
assets.append({"id": asset_id, "system_users": system_users})
|
||||||
|
return assets
|
||||||
|
|
||||||
#@timeit
|
@timeit
|
||||||
def get_nodes_with_assets(self):
|
def get_nodes_with_assets(self):
|
||||||
|
"""
|
||||||
|
:return:
|
||||||
|
[
|
||||||
|
{
|
||||||
|
'key': node.key,
|
||||||
|
'assets_amount': 10
|
||||||
|
'assets': {
|
||||||
|
asset.id: {
|
||||||
|
system_user.id: actions,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
"""
|
||||||
if self._nodes_with_assets:
|
if self._nodes_with_assets:
|
||||||
return self._nodes_with_assets
|
return self._nodes_with_assets
|
||||||
nodes = {}
|
util = PermAssetsAmountUtil()
|
||||||
for node, values in self.nodes.items():
|
nodes_with_assets_amount = util.compute_nodes_assets_amount(self.nodes)
|
||||||
node._assets_amount = values["assets_amount"]
|
nodes = []
|
||||||
nodes[node] = {asset: self.assets.get(asset, {}) for asset in values["assets"]}
|
for key, values in nodes_with_assets_amount.items():
|
||||||
|
assets = {asset_id: self.assets.get(asset_id) for asset_id in values["assets"]}
|
||||||
|
nodes.append({
|
||||||
|
"key": key, "assets": assets,
|
||||||
|
"assets_amount": values["assets_amount"]
|
||||||
|
})
|
||||||
# 如果返回空节点,页面构造授权资产树报错
|
# 如果返回空节点,页面构造授权资产树报错
|
||||||
if not nodes:
|
if not nodes:
|
||||||
nodes[self.empty_node] = {}
|
nodes.append({
|
||||||
|
"key": const.EMPTY_NODE_KEY, "assets": {}, "assets_amount": 0
|
||||||
|
})
|
||||||
|
nodes.sort(key=lambda n: self.key_sort(n["key"]))
|
||||||
self._nodes_with_assets = nodes
|
self._nodes_with_assets = nodes
|
||||||
return dict(nodes)
|
return nodes
|
||||||
|
|
||||||
def get_nodes(self):
|
def get_nodes(self):
|
||||||
return list(self.nodes.keys())
|
nodes = list(self.nodes.keys())
|
||||||
|
if not nodes:
|
||||||
|
nodes.append(const.EMPTY_NODE_KEY)
|
||||||
|
return list(nodes)
|
||||||
|
|
||||||
|
|
||||||
def get_user_permissions(user, include_group=True):
|
def get_user_permissions(user, include_group=True):
|
||||||
|
@ -228,8 +258,8 @@ def get_system_user_permissions(system_user):
|
||||||
|
|
||||||
|
|
||||||
class AssetPermissionCacheMixin:
|
class AssetPermissionCacheMixin:
|
||||||
CACHE_KEY_PREFIX = '_ASSET_PERM_CACHE_'
|
CACHE_KEY_PREFIX = '_ASSET_PERM_CACHE_V2_'
|
||||||
CACHE_META_KEY_PREFIX = '_ASSET_PERM_META_KEY_'
|
CACHE_META_KEY_PREFIX = '_ASSET_PERM_META_KEY_V2_'
|
||||||
CACHE_TIME = settings.ASSETS_PERM_CACHE_TIME
|
CACHE_TIME = settings.ASSETS_PERM_CACHE_TIME
|
||||||
CACHE_POLICY_MAP = (('0', 'never'), ('1', 'using'), ('2', 'refresh'))
|
CACHE_POLICY_MAP = (('0', 'never'), ('1', 'using'), ('2', 'refresh'))
|
||||||
cache_policy = '1'
|
cache_policy = '1'
|
||||||
|
@ -283,6 +313,7 @@ class AssetPermissionCacheMixin:
|
||||||
return self.get_cache_key('SYSTEM_USER')
|
return self.get_cache_key('SYSTEM_USER')
|
||||||
|
|
||||||
def get_resource_from_cache(self, resource):
|
def get_resource_from_cache(self, resource):
|
||||||
|
logger.debug("Try get resource from cache")
|
||||||
key_map = {
|
key_map = {
|
||||||
"assets": self.asset_key,
|
"assets": self.asset_key,
|
||||||
"nodes": self.node_key,
|
"nodes": self.node_key,
|
||||||
|
@ -294,18 +325,22 @@ class AssetPermissionCacheMixin:
|
||||||
raise ValueError("Not a valid resource: {}".format(resource))
|
raise ValueError("Not a valid resource: {}".format(resource))
|
||||||
cached = cache.get(key)
|
cached = cache.get(key)
|
||||||
if not cached:
|
if not cached:
|
||||||
|
logger.debug("Not found resource cache, update it")
|
||||||
self.update_cache()
|
self.update_cache()
|
||||||
cached = cache.get(key)
|
cached = cache.get(key)
|
||||||
return cached
|
return cached
|
||||||
|
|
||||||
def get_resource(self, resource):
|
def get_resource(self, resource):
|
||||||
if self._is_using_cache():
|
if self._is_using_cache():
|
||||||
|
logger.debug("Using cache to get resource")
|
||||||
return self.get_resource_from_cache(resource)
|
return self.get_resource_from_cache(resource)
|
||||||
elif self._is_refresh_cache():
|
elif self._is_refresh_cache():
|
||||||
|
logger.debug("Need refresh cache")
|
||||||
self.expire_cache()
|
self.expire_cache()
|
||||||
data = self.get_resource_from_cache(resource)
|
data = self.get_resource_from_cache(resource)
|
||||||
return data
|
return data
|
||||||
else:
|
else:
|
||||||
|
logger.debug("Not using cache get source")
|
||||||
return self.get_resource_without_cache(resource)
|
return self.get_resource_without_cache(resource)
|
||||||
|
|
||||||
def get_resource_without_cache(self, resource):
|
def get_resource_without_cache(self, resource):
|
||||||
|
@ -430,88 +465,91 @@ class AssetPermissionUtil(AssetPermissionCacheMixin):
|
||||||
self._permissions = permissions
|
self._permissions = permissions
|
||||||
return permissions
|
return permissions
|
||||||
|
|
||||||
#@timeit
|
@timeit
|
||||||
def filter_permissions(self, **filters):
|
def filter_permissions(self, **filters):
|
||||||
filters_json = json.dumps(filters, sort_keys=True)
|
filters_json = json.dumps(filters, sort_keys=True)
|
||||||
self._permissions = self.permissions.filter(**filters)
|
self._permissions = self.permissions.filter(**filters)
|
||||||
self._filter_id = md5(filters_json.encode()).hexdigest()
|
self._filter_id = md5(filters_json.encode()).hexdigest()
|
||||||
|
|
||||||
#@timeit
|
@timeit
|
||||||
def get_nodes_direct(self):
|
def get_nodes_direct(self):
|
||||||
"""
|
"""
|
||||||
返回用户/组授权规则直接关联的节点
|
返回直接授权的节点,
|
||||||
:return: {node1: {system_user1: {'actions': set()},}}
|
并将节点添加到tree.nodes中,并将节点下的资产添加到tree.assets中
|
||||||
|
:return:
|
||||||
|
{node.key: {system_user.id: actions,}, }
|
||||||
"""
|
"""
|
||||||
if self._nodes_direct:
|
if self._nodes_direct:
|
||||||
return self._nodes_direct
|
return self._nodes_direct
|
||||||
nodes = defaultdict(lambda: defaultdict(int))
|
nodes_keys = defaultdict(lambda: defaultdict(int))
|
||||||
for perm in self.permissions:
|
for perm in self.permissions:
|
||||||
actions = [perm.actions]
|
actions = [perm.actions]
|
||||||
system_users = perm.system_users.all()
|
system_users_ids = [s.id for s in perm.system_users.all()]
|
||||||
_nodes = perm.nodes.all()
|
_nodes_keys = [n.key for n in perm.nodes.all()]
|
||||||
for node, system_user, action in itertools.product(_nodes, system_users, actions):
|
iterable = itertools.product(_nodes_keys, system_users_ids, actions)
|
||||||
nodes[node][system_user] |= action
|
for node_key, sys_id, action in iterable:
|
||||||
self.tree.add_nodes(nodes)
|
nodes_keys[node_key][sys_id] |= action
|
||||||
self._nodes_direct = nodes
|
|
||||||
return nodes
|
self.tree.add_nodes(nodes_keys)
|
||||||
|
|
||||||
|
pattern = set()
|
||||||
|
for key in nodes_keys:
|
||||||
|
pattern.add(r'^{0}$|^{0}:'.format(key))
|
||||||
|
pattern = '|'.join(list(pattern))
|
||||||
|
if pattern:
|
||||||
|
assets_ids = Asset.objects.filter(
|
||||||
|
nodes__key__regex=pattern
|
||||||
|
).values_list("id", flat=True).distinct()
|
||||||
|
else:
|
||||||
|
assets_ids = []
|
||||||
|
self.tree.add_assets_without_system_users(assets_ids)
|
||||||
|
self._nodes_direct = nodes_keys
|
||||||
|
return nodes_keys
|
||||||
|
|
||||||
def get_nodes_without_cache(self):
|
def get_nodes_without_cache(self):
|
||||||
self.get_assets_direct()
|
self.get_assets_without_cache()
|
||||||
return self.tree.get_nodes()
|
return self.tree.get_nodes()
|
||||||
|
|
||||||
#@timeit
|
@timeit
|
||||||
def get_assets_direct(self):
|
def get_assets_direct(self):
|
||||||
"""
|
"""
|
||||||
返回用户授权规则直接关联的资产
|
返回直接授权的资产,
|
||||||
:return: {asset1: {system_user1: 1,}}
|
并添加到tree.assets中
|
||||||
|
:return:
|
||||||
|
{asset.id: {system_user.id: actions, }, }
|
||||||
"""
|
"""
|
||||||
if self._assets_direct:
|
if self._assets_direct:
|
||||||
return self._assets_direct
|
return self._assets_direct
|
||||||
assets = defaultdict(lambda: defaultdict(int))
|
assets_ids = defaultdict(lambda: defaultdict(int))
|
||||||
for perm in self.permissions:
|
for perm in self.permissions:
|
||||||
actions = [perm.actions]
|
actions = [perm.actions]
|
||||||
_assets = perm.assets.valid().only(*self.assets_only)
|
_assets_ids = [a.id for a in perm.assets.all()]
|
||||||
system_users = perm.system_users.all()
|
system_users_ids = [s.id for s in perm.system_users.all()]
|
||||||
iterable = itertools.product(_assets, system_users, actions)
|
iterable = itertools.product(_assets_ids, system_users_ids, actions)
|
||||||
for asset, system_user, action in iterable:
|
for asset_id, sys_id, action in iterable:
|
||||||
assets[asset][system_user] |= action
|
assets_ids[asset_id][sys_id] |= action
|
||||||
self.tree.add_assets(assets)
|
self.tree.add_assets(assets_ids)
|
||||||
self._assets_direct = assets
|
self._assets_direct = assets_ids
|
||||||
return assets
|
return assets_ids
|
||||||
|
|
||||||
#@timeit
|
@timeit
|
||||||
def get_assets_without_cache(self):
|
def get_assets_without_cache(self):
|
||||||
"""
|
"""
|
||||||
:return: {asset1: set(system_user1,)}
|
:return:
|
||||||
|
[
|
||||||
|
{"id": asset.id, "system_users": {system_user.id: actions, }},
|
||||||
|
]
|
||||||
"""
|
"""
|
||||||
if self._assets:
|
if self._assets:
|
||||||
return self._assets
|
return self._assets
|
||||||
|
self.get_nodes_direct()
|
||||||
self.get_assets_direct()
|
self.get_assets_direct()
|
||||||
nodes = self.get_nodes_direct()
|
|
||||||
pattern = set()
|
|
||||||
for node in nodes:
|
|
||||||
pattern.add(r'^{0}$|^{0}:'.format(node.key))
|
|
||||||
pattern = '|'.join(list(pattern))
|
|
||||||
if pattern:
|
|
||||||
assets = Asset.objects.filter(nodes__key__regex=pattern).valid() \
|
|
||||||
.prefetch_related('nodes')\
|
|
||||||
.only(*self.assets_only)\
|
|
||||||
.distinct()
|
|
||||||
else:
|
|
||||||
assets = []
|
|
||||||
assets = list(assets)
|
|
||||||
self.tree.add_assets_without_system_users(assets)
|
|
||||||
assets = self.tree.get_assets()
|
assets = self.tree.get_assets()
|
||||||
self._assets = assets
|
self._assets = assets
|
||||||
return assets
|
return assets
|
||||||
|
|
||||||
#@timeit
|
@timeit
|
||||||
def get_nodes_with_assets_without_cache(self):
|
def get_nodes_with_assets_without_cache(self):
|
||||||
"""
|
|
||||||
返回节点并且包含资产
|
|
||||||
{"node": {"asset": {"system_user": 1})}}
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
self.get_assets_without_cache()
|
self.get_assets_without_cache()
|
||||||
nodes_assets = self.tree.get_nodes_with_assets()
|
nodes_assets = self.tree.get_nodes_with_assets()
|
||||||
return nodes_assets
|
return nodes_assets
|
||||||
|
@ -545,7 +583,15 @@ def sort_assets(assets, order_by='hostname', reverse=False):
|
||||||
return assets
|
return assets
|
||||||
|
|
||||||
|
|
||||||
def parse_node_to_tree_node(node):
|
class ParserNode:
|
||||||
|
nodes_only_fields = ("key", "value", "id")
|
||||||
|
assets_only_fields = ("platform", "hostname", "id", "ip", "protocols")
|
||||||
|
system_users_only_fields = (
|
||||||
|
"id", "name", "username", "protocol", "priority", "login_mode",
|
||||||
|
)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def parse_node_to_tree_node(node):
|
||||||
name = '{} ({})'.format(node.value, node.assets_amount)
|
name = '{} ({})'.format(node.value, node.assets_amount)
|
||||||
data = {
|
data = {
|
||||||
'id': node.key,
|
'id': node.key,
|
||||||
|
@ -566,15 +612,15 @@ def parse_node_to_tree_node(node):
|
||||||
tree_node = TreeNode(**data)
|
tree_node = TreeNode(**data)
|
||||||
return tree_node
|
return tree_node
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
def parse_asset_to_tree_node(node, asset, system_users):
|
def parse_asset_to_tree_node(node, asset, system_users):
|
||||||
icon_skin = 'file'
|
icon_skin = 'file'
|
||||||
if asset.platform.lower() == 'windows':
|
if asset.platform.lower() == 'windows':
|
||||||
icon_skin = 'windows'
|
icon_skin = 'windows'
|
||||||
elif asset.platform.lower() == 'linux':
|
elif asset.platform.lower() == 'linux':
|
||||||
icon_skin = 'linux'
|
icon_skin = 'linux'
|
||||||
_system_users = []
|
_system_users = []
|
||||||
for system_user, action in system_users.items():
|
for system_user in system_users:
|
||||||
_system_users.append({
|
_system_users.append({
|
||||||
'id': system_user.id,
|
'id': system_user.id,
|
||||||
'name': system_user.name,
|
'name': system_user.name,
|
||||||
|
@ -582,7 +628,7 @@ def parse_asset_to_tree_node(node, asset, system_users):
|
||||||
'protocol': system_user.protocol,
|
'protocol': system_user.protocol,
|
||||||
'priority': system_user.priority,
|
'priority': system_user.priority,
|
||||||
'login_mode': system_user.login_mode,
|
'login_mode': system_user.login_mode,
|
||||||
'actions': [Action.value_to_choices(action)],
|
'actions': [Action.value_to_choices(system_user.actions)],
|
||||||
})
|
})
|
||||||
data = {
|
data = {
|
||||||
'id': str(asset.id),
|
'id': str(asset.id),
|
||||||
|
@ -601,9 +647,6 @@ def parse_asset_to_tree_node(node, asset, system_users):
|
||||||
'ip': asset.ip,
|
'ip': asset.ip,
|
||||||
'protocols': asset.protocols_as_list,
|
'protocols': asset.protocols_as_list,
|
||||||
'platform': asset.platform,
|
'platform': asset.platform,
|
||||||
'domain': None if not asset.domain else asset.domain.id,
|
|
||||||
'is_active': asset.is_active,
|
|
||||||
'comment': asset.comment
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,136 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
from collections import defaultdict
|
||||||
|
from common.struct import Stack
|
||||||
|
from common.utils import timeit
|
||||||
|
from assets.utils import NodeUtil
|
||||||
|
|
||||||
|
|
||||||
|
class PermStackUtilMixin:
|
||||||
|
def __init__(self, debug=False):
|
||||||
|
self.stack = None
|
||||||
|
self._nodes = {}
|
||||||
|
self._debug = debug
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def sorted_by(node_dict):
|
||||||
|
return [int(i) for i in node_dict['key'].split(':')]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def is_children(item1, item2):
|
||||||
|
key1 = item1["key"]
|
||||||
|
key2 = item2["key"]
|
||||||
|
return key2.startswith(key1 + ':') and (
|
||||||
|
len(key2.split(':')) - len(key1.split(':'))
|
||||||
|
) == 1
|
||||||
|
|
||||||
|
def debug(self, msg):
|
||||||
|
self._debug and print(msg)
|
||||||
|
|
||||||
|
|
||||||
|
class PermSystemUserNodeUtil(PermStackUtilMixin):
|
||||||
|
"""
|
||||||
|
self._nodes: {node.key: {system_user.id: actions,}}
|
||||||
|
"""
|
||||||
|
@timeit
|
||||||
|
def get_nodes_family_and_system_users(self, nodes_with_system_users):
|
||||||
|
"""
|
||||||
|
返回所有nodes_with_system_users中的node的家族节点的信息,
|
||||||
|
并子会继承祖先的系统用户和actions信息
|
||||||
|
:param nodes_with_system_users:
|
||||||
|
{node.key: {system_user.id: actions,}, }
|
||||||
|
:return:
|
||||||
|
{node.key: {system_user.id: actions,}, }
|
||||||
|
"""
|
||||||
|
node_util = NodeUtil()
|
||||||
|
_nodes_keys = nodes_with_system_users.keys()
|
||||||
|
family_keys = node_util.get_some_nodes_family_keys_by_keys(_nodes_keys)
|
||||||
|
|
||||||
|
nodes_items = []
|
||||||
|
for i in family_keys:
|
||||||
|
system_users = nodes_with_system_users.get(i, defaultdict(int))
|
||||||
|
item = {"key": i, "system_users": system_users}
|
||||||
|
nodes_items.append(item)
|
||||||
|
# 按照父子关系排序
|
||||||
|
nodes_items.sort(key=self.sorted_by)
|
||||||
|
nodes_items.append({"key": "", "system_users": defaultdict(int)})
|
||||||
|
|
||||||
|
self.stack = Stack()
|
||||||
|
for item in nodes_items:
|
||||||
|
self.debug("准备: {} 栈顶: {}".format(
|
||||||
|
item['key'], self.stack.top["key"] if self.stack.top else None)
|
||||||
|
)
|
||||||
|
# 入栈之前检查,该节点是不是栈顶节点的子节点
|
||||||
|
# 如果不是,则栈顶出栈
|
||||||
|
while self.stack.top and not self.is_children(self.stack.top, item):
|
||||||
|
# 出栈
|
||||||
|
self.pop_from_stack_system_users()
|
||||||
|
# 入栈
|
||||||
|
self.push_to_stack_system_users(item)
|
||||||
|
# 出栈最后一个
|
||||||
|
self.debug("剩余: {}".format(', '.join([n["key"] for n in self.stack])))
|
||||||
|
return self._nodes
|
||||||
|
|
||||||
|
def push_to_stack_system_users(self, item):
|
||||||
|
"""
|
||||||
|
:param item:
|
||||||
|
{"key": node.key, "system_users": {system_user.id: actions,},}
|
||||||
|
"""
|
||||||
|
if not self.stack.is_empty():
|
||||||
|
item_system_users = item["system_users"]
|
||||||
|
for system_user, action in self.stack.top["system_users"].items():
|
||||||
|
# 更新栈顶的系统用户和action到将要入栈的item中
|
||||||
|
item_system_users[system_user] |= action
|
||||||
|
item["system_users"] = item_system_users
|
||||||
|
self.debug("入栈: {}".format(item['key']))
|
||||||
|
self.stack.push(item)
|
||||||
|
|
||||||
|
# 出栈
|
||||||
|
def pop_from_stack_system_users(self):
|
||||||
|
_node = self.stack.pop()
|
||||||
|
self._nodes[_node["key"]] = _node["system_users"]
|
||||||
|
self.debug("出栈: {} 栈顶: {}".format(_node['key'], self.stack.top['key'] if self.stack.top else None))
|
||||||
|
|
||||||
|
|
||||||
|
class PermAssetsAmountUtil(PermStackUtilMixin):
|
||||||
|
def push_to_stack_nodes_amount(self, item):
|
||||||
|
self.debug("入栈: {}".format(item['key']))
|
||||||
|
self.stack.push(item)
|
||||||
|
|
||||||
|
def pop_from_stack_nodes_amount(self):
|
||||||
|
_node = self.stack.pop()
|
||||||
|
self.debug("出栈: {} 栈顶: {}".format(
|
||||||
|
_node['key'], self.stack.top['key'] if self.stack.top else None)
|
||||||
|
)
|
||||||
|
_node["assets_amount"] = len(_node["all_assets"] | _node["assets"])
|
||||||
|
self._nodes[_node.pop("key")] = _node
|
||||||
|
|
||||||
|
if not self.stack.top:
|
||||||
|
return
|
||||||
|
self.stack.top["all_assets"]\
|
||||||
|
.update(_node["all_assets"] | _node["assets"])
|
||||||
|
|
||||||
|
def compute_nodes_assets_amount(self, nodes_with_assets):
|
||||||
|
self.stack = Stack()
|
||||||
|
nodes_items = []
|
||||||
|
for key, values in nodes_with_assets.items():
|
||||||
|
nodes_items.append({
|
||||||
|
"key": key, "assets": values["assets"],
|
||||||
|
"all_assets": values["all_assets"], "assets_amount": 0
|
||||||
|
})
|
||||||
|
|
||||||
|
nodes_items.sort(key=self.sorted_by)
|
||||||
|
nodes_items.append({"key": "", "assets": set(), "all_assets": set(), "assets_amount": 0})
|
||||||
|
self.stack = Stack()
|
||||||
|
for item in nodes_items:
|
||||||
|
self.debug("准备: {} 栈顶: {}".format(
|
||||||
|
item['key'], self.stack.top["key"] if self.stack.top else None)
|
||||||
|
)
|
||||||
|
# 入栈之前检查,该节点是不是栈顶节点的子节点
|
||||||
|
# 如果不是,则栈顶出栈
|
||||||
|
while self.stack.top and not self.is_children(self.stack.top, item):
|
||||||
|
self.pop_from_stack_nodes_amount()
|
||||||
|
self.push_to_stack_nodes_amount(item)
|
||||||
|
# 出栈最后一个
|
||||||
|
self.debug("剩余: {}".format(', '.join([n["key"] for n in self.stack])))
|
||||||
|
return self._nodes
|
|
@ -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)
|
|
@ -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):
|
||||||
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):
|
||||||
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 () {
|
||||||
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 () {
|
||||||
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 () {
|
||||||
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",
|
||||||
|
|
|
@ -256,7 +256,7 @@ function formSubmit(props) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function APIUpdateAttr(props) {
|
function requestApi(props) {
|
||||||
// props = {url: .., body: , success: , error: , method: ,}
|
// props = {url: .., body: , success: , error: , method: ,}
|
||||||
props = props || {};
|
props = props || {};
|
||||||
var user_success_message = props.success_message;
|
var user_success_message = props.success_message;
|
||||||
|
@ -328,7 +328,7 @@ function objectDelete(obj, name, url, redirectTo) {
|
||||||
// swal("错误", "删除"+"[ "+name+" ]"+"遇到错误", "error");
|
// swal("错误", "删除"+"[ "+name+" ]"+"遇到错误", "error");
|
||||||
swal(gettext('Error'), "[ "+name+" ]" + gettext("Being used by the asset, please unbind the asset first."), "error");
|
swal(gettext('Error'), "[ "+name+" ]" + gettext("Being used by the asset, please unbind the asset first."), "error");
|
||||||
};
|
};
|
||||||
APIUpdateAttr({
|
requestApi({
|
||||||
url: url,
|
url: url,
|
||||||
body: JSON.stringify(body),
|
body: JSON.stringify(body),
|
||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
|
@ -369,7 +369,7 @@ function orgDelete(obj, name, url, redirectTo){
|
||||||
swal(gettext("Error"), " [ "+ name + " ] " + gettext("Do not perform this operation under this organization. Try again after switching to another organization"), "error");
|
swal(gettext("Error"), " [ "+ name + " ] " + gettext("Do not perform this operation under this organization. Try again after switching to another organization"), "error");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
APIUpdateAttr({
|
requestApi({
|
||||||
url: url,
|
url: url,
|
||||||
body: JSON.stringify(body),
|
body: JSON.stringify(body),
|
||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
|
@ -1109,9 +1109,22 @@ function objectAttrsIsBool(obj, attrs) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function cleanDate(d) {
|
||||||
|
for (var i=0; i<2; i++) {
|
||||||
|
if (isNaN(Date.parse(d))) {
|
||||||
|
d = d.split('+')[0].trimRight();
|
||||||
|
} else {
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
|
||||||
function formatDateAsCN(d) {
|
function formatDateAsCN(d) {
|
||||||
|
d = cleanDate(d);
|
||||||
var date = new Date(d);
|
var date = new Date(d);
|
||||||
return date.toISOString().replace("T", " ").replace(/\..*/, "");
|
var date_s = date.toLocaleString(navigator.language, {hour12: false});
|
||||||
|
return date_s.split("/").join('-')
|
||||||
}
|
}
|
||||||
|
|
||||||
function getUrlParams(url) {
|
function getUrlParams(url) {
|
||||||
|
@ -1137,6 +1150,8 @@ function getTimeUnits(u) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function timeOffset(a, b) {
|
function timeOffset(a, b) {
|
||||||
|
a = cleanDate(a);
|
||||||
|
b = cleanDate(b);
|
||||||
var start = new Date(a);
|
var start = new Date(a);
|
||||||
var end = new Date(b);
|
var end = new Date(b);
|
||||||
var offset = (end - start)/1000;
|
var offset = (end - start)/1000;
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -1,5 +1,6 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
|
from django.conf import settings
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
|
||||||
from common.utils import get_request_ip
|
from common.utils import get_request_ip
|
||||||
|
@ -27,6 +28,9 @@ class TerminalSerializer(serializers.ModelSerializer):
|
||||||
valid = super().is_valid(raise_exception=raise_exception)
|
valid = super().is_valid(raise_exception=raise_exception)
|
||||||
if not valid:
|
if not valid:
|
||||||
return valid
|
return valid
|
||||||
|
if not settings.SECURITY_SERVICE_ACCOUNT_REGISTRATION:
|
||||||
|
error = {"error": "service account registration disabled"}
|
||||||
|
raise serializers.ValidationError(error)
|
||||||
data = {'name': self.validated_data.get('name')}
|
data = {'name': self.validated_data.get('name')}
|
||||||
kwargs = {'data': data}
|
kwargs = {'data': data}
|
||||||
if self.instance and self.instance.user:
|
if self.instance and self.instance.user:
|
||||||
|
|
|
@ -132,7 +132,7 @@
|
||||||
}, 300)
|
}, 300)
|
||||||
}
|
}
|
||||||
var the_url = "{% url 'api-terminal:tasks-list' %}";
|
var the_url = "{% url 'api-terminal:tasks-list' %}";
|
||||||
APIUpdateAttr({url: the_url, method: 'POST', body: JSON.stringify(data), success: success, success_message: 'Terminate success'});
|
requestApi({url: the_url, method: 'POST', body: JSON.stringify(data), success: success, success_message: 'Terminate success'});
|
||||||
}
|
}
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
$('.footable').footable();
|
$('.footable').footable();
|
||||||
|
|
|
@ -90,7 +90,7 @@ function terminateSession(data) {
|
||||||
}
|
}
|
||||||
var success_message = '{% trans "Terminate task send, waiting ..." %}';
|
var success_message = '{% trans "Terminate task send, waiting ..." %}';
|
||||||
var the_url = "{% url 'api-terminal:kill-session' %}";
|
var the_url = "{% url 'api-terminal:kill-session' %}";
|
||||||
APIUpdateAttr({
|
requestApi({
|
||||||
url: the_url,
|
url: the_url,
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: JSON.stringify(data),
|
body: JSON.stringify(data),
|
||||||
|
@ -174,7 +174,7 @@ function finishedSession(data) {
|
||||||
var success = function() {
|
var success = function() {
|
||||||
location.reload();
|
location.reload();
|
||||||
};
|
};
|
||||||
APIUpdateAttr({
|
requestApi({
|
||||||
url: the_url,
|
url: the_url,
|
||||||
method: 'PATCH',
|
method: 'PATCH',
|
||||||
body: JSON.stringify(data),
|
body: JSON.stringify(data),
|
||||||
|
|
|
@ -14,7 +14,7 @@ from rest_framework.pagination import LimitOffsetPagination
|
||||||
|
|
||||||
from common.permissions import (
|
from common.permissions import (
|
||||||
IsOrgAdmin, IsCurrentUserOrReadOnly, IsOrgAdminOrAppUser,
|
IsOrgAdmin, IsCurrentUserOrReadOnly, IsOrgAdminOrAppUser,
|
||||||
CanUpdateSuperUser,
|
CanUpdateDeleteSuperUser,
|
||||||
)
|
)
|
||||||
from common.mixins import IDInCacheFilterMixin
|
from common.mixins import IDInCacheFilterMixin
|
||||||
from common.utils import get_logger
|
from common.utils import get_logger
|
||||||
|
@ -38,7 +38,7 @@ class UserViewSet(IDInCacheFilterMixin, BulkModelViewSet):
|
||||||
search_fields = filter_fields
|
search_fields = filter_fields
|
||||||
queryset = User.objects.exclude(role=User.ROLE_APP)
|
queryset = User.objects.exclude(role=User.ROLE_APP)
|
||||||
serializer_class = UserSerializer
|
serializer_class = UserSerializer
|
||||||
permission_classes = (IsOrgAdmin, CanUpdateSuperUser)
|
permission_classes = (IsOrgAdmin, CanUpdateDeleteSuperUser)
|
||||||
pagination_class = LimitOffsetPagination
|
pagination_class = LimitOffsetPagination
|
||||||
|
|
||||||
def send_created_signal(self, users):
|
def send_created_signal(self, users):
|
||||||
|
|
|
@ -27,97 +27,7 @@ signer = get_signer()
|
||||||
logger = get_logger(__file__)
|
logger = get_logger(__file__)
|
||||||
|
|
||||||
|
|
||||||
class User(AbstractUser):
|
class AuthMixin:
|
||||||
ROLE_ADMIN = 'Admin'
|
|
||||||
ROLE_USER = 'User'
|
|
||||||
ROLE_APP = 'App'
|
|
||||||
ROLE_AUDITOR = 'Auditor'
|
|
||||||
|
|
||||||
ROLE_CHOICES = (
|
|
||||||
(ROLE_ADMIN, _('Administrator')),
|
|
||||||
(ROLE_USER, _('User')),
|
|
||||||
(ROLE_APP, _('Application')),
|
|
||||||
(ROLE_AUDITOR, _("Auditor"))
|
|
||||||
)
|
|
||||||
OTP_LEVEL_CHOICES = (
|
|
||||||
(0, _('Disable')),
|
|
||||||
(1, _('Enable')),
|
|
||||||
(2, _("Force enable")),
|
|
||||||
)
|
|
||||||
SOURCE_LOCAL = 'local'
|
|
||||||
SOURCE_LDAP = 'ldap'
|
|
||||||
SOURCE_OPENID = 'openid'
|
|
||||||
SOURCE_RADIUS = 'radius'
|
|
||||||
SOURCE_CHOICES = (
|
|
||||||
(SOURCE_LOCAL, 'Local'),
|
|
||||||
(SOURCE_LDAP, 'LDAP/AD'),
|
|
||||||
(SOURCE_OPENID, 'OpenID'),
|
|
||||||
(SOURCE_RADIUS, 'Radius'),
|
|
||||||
)
|
|
||||||
|
|
||||||
CACHE_KEY_USER_RESET_PASSWORD_PREFIX = "_KEY_USER_RESET_PASSWORD_{}"
|
|
||||||
|
|
||||||
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
|
|
||||||
username = models.CharField(
|
|
||||||
max_length=128, unique=True, verbose_name=_('Username')
|
|
||||||
)
|
|
||||||
name = models.CharField(max_length=128, verbose_name=_('Name'))
|
|
||||||
email = models.EmailField(
|
|
||||||
max_length=128, unique=True, verbose_name=_('Email')
|
|
||||||
)
|
|
||||||
groups = models.ManyToManyField(
|
|
||||||
'users.UserGroup', related_name='users',
|
|
||||||
blank=True, verbose_name=_('User group')
|
|
||||||
)
|
|
||||||
role = models.CharField(
|
|
||||||
choices=ROLE_CHOICES, default='User', max_length=10,
|
|
||||||
blank=True, verbose_name=_('Role')
|
|
||||||
)
|
|
||||||
avatar = models.ImageField(
|
|
||||||
upload_to="avatar", null=True, verbose_name=_('Avatar')
|
|
||||||
)
|
|
||||||
wechat = models.CharField(
|
|
||||||
max_length=128, blank=True, verbose_name=_('Wechat')
|
|
||||||
)
|
|
||||||
phone = models.CharField(
|
|
||||||
max_length=20, blank=True, null=True, verbose_name=_('Phone')
|
|
||||||
)
|
|
||||||
otp_level = models.SmallIntegerField(
|
|
||||||
default=0, choices=OTP_LEVEL_CHOICES, verbose_name=_('MFA')
|
|
||||||
)
|
|
||||||
otp_secret_key = fields.EncryptCharField(max_length=128, blank=True, null=True)
|
|
||||||
# Todo: Auto generate key, let user download
|
|
||||||
private_key = fields.EncryptTextField(
|
|
||||||
blank=True, null=True, verbose_name=_('Private key')
|
|
||||||
)
|
|
||||||
public_key = fields.EncryptTextField(
|
|
||||||
blank=True, null=True, verbose_name=_('Public key')
|
|
||||||
)
|
|
||||||
comment = models.TextField(
|
|
||||||
blank=True, null=True, verbose_name=_('Comment')
|
|
||||||
)
|
|
||||||
is_first_login = models.BooleanField(default=True)
|
|
||||||
date_expired = models.DateTimeField(
|
|
||||||
default=date_expired_default, blank=True, null=True,
|
|
||||||
db_index=True, verbose_name=_('Date expired')
|
|
||||||
)
|
|
||||||
created_by = models.CharField(
|
|
||||||
max_length=30, default='', verbose_name=_('Created by')
|
|
||||||
)
|
|
||||||
source = models.CharField(
|
|
||||||
max_length=30, default=SOURCE_LOCAL, choices=SOURCE_CHOICES,
|
|
||||||
verbose_name=_('Source')
|
|
||||||
)
|
|
||||||
date_password_last_updated = models.DateTimeField(
|
|
||||||
auto_now_add=True, blank=True, null=True,
|
|
||||||
verbose_name=_('Date password last updated')
|
|
||||||
)
|
|
||||||
|
|
||||||
user_cache_key_prefix = '_User_{}'
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return '{0.name}({0.username})'.format(self)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def password_raw(self):
|
def password_raw(self):
|
||||||
raise AttributeError('Password raw is not a readable attribute')
|
raise AttributeError('Password raw is not a readable attribute')
|
||||||
|
@ -134,9 +44,11 @@ class User(AbstractUser):
|
||||||
def set_password(self, raw_password):
|
def set_password(self, raw_password):
|
||||||
self._set_password = True
|
self._set_password = True
|
||||||
if self.can_update_password():
|
if self.can_update_password():
|
||||||
return super().set_password(raw_password)
|
self.date_password_last_updated = timezone.now()
|
||||||
|
super().set_password(raw_password)
|
||||||
else:
|
else:
|
||||||
error = _("User auth from {}, go there change password").format(self.source)
|
error = _("User auth from {}, go there change password").format(
|
||||||
|
self.source)
|
||||||
raise PermissionError(error)
|
raise PermissionError(error)
|
||||||
|
|
||||||
def can_update_password(self):
|
def can_update_password(self):
|
||||||
|
@ -146,9 +58,6 @@ class User(AbstractUser):
|
||||||
from ..utils import check_otp_code
|
from ..utils import check_otp_code
|
||||||
return check_otp_code(self.otp_secret_key, code)
|
return check_otp_code(self.otp_secret_key, code)
|
||||||
|
|
||||||
def get_absolute_url(self):
|
|
||||||
return reverse('users:user-detail', args=(self.id,))
|
|
||||||
|
|
||||||
def is_public_key_valid(self):
|
def is_public_key_valid(self):
|
||||||
"""
|
"""
|
||||||
Check if the user's ssh public key is valid.
|
Check if the user's ssh public key is valid.
|
||||||
|
@ -158,36 +67,12 @@ class User(AbstractUser):
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@property
|
|
||||||
def groups_display(self):
|
|
||||||
return ' '.join([group.name for group in self.groups.all()])
|
|
||||||
|
|
||||||
@property
|
|
||||||
def role_display(self):
|
|
||||||
return self.get_role_display()
|
|
||||||
|
|
||||||
@property
|
|
||||||
def source_display(self):
|
|
||||||
return self.get_source_display()
|
|
||||||
|
|
||||||
@property
|
|
||||||
def is_expired(self):
|
|
||||||
if self.date_expired and self.date_expired < timezone.now():
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
return False
|
|
||||||
|
|
||||||
@property
|
|
||||||
def is_valid(self):
|
|
||||||
if self.is_active and not self.is_expired:
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def public_key_obj(self):
|
def public_key_obj(self):
|
||||||
class PubKey(object):
|
class PubKey(object):
|
||||||
def __getattr__(self, item):
|
def __getattr__(self, item):
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
if self.public_key:
|
if self.public_key:
|
||||||
import sshpubkeys
|
import sshpubkeys
|
||||||
try:
|
try:
|
||||||
|
@ -196,6 +81,53 @@ class User(AbstractUser):
|
||||||
pass
|
pass
|
||||||
return PubKey()
|
return PubKey()
|
||||||
|
|
||||||
|
def reset_password(self, new_password):
|
||||||
|
self.set_password(new_password)
|
||||||
|
self.save()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def date_password_expired(self):
|
||||||
|
interval = settings.SECURITY_PASSWORD_EXPIRATION_TIME
|
||||||
|
date_expired = self.date_password_last_updated + timezone.timedelta(
|
||||||
|
days=int(interval))
|
||||||
|
return date_expired
|
||||||
|
|
||||||
|
@property
|
||||||
|
def password_expired_remain_days(self):
|
||||||
|
date_remain = self.date_password_expired - timezone.now()
|
||||||
|
return date_remain.days
|
||||||
|
|
||||||
|
@property
|
||||||
|
def password_has_expired(self):
|
||||||
|
if self.is_local and self.password_expired_remain_days < 0:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
@property
|
||||||
|
def password_will_expired(self):
|
||||||
|
if self.is_local and self.password_expired_remain_days < 5:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
class RoleMixin:
|
||||||
|
ROLE_ADMIN = 'Admin'
|
||||||
|
ROLE_USER = 'User'
|
||||||
|
ROLE_APP = 'App'
|
||||||
|
ROLE_AUDITOR = 'Auditor'
|
||||||
|
|
||||||
|
ROLE_CHOICES = (
|
||||||
|
(ROLE_ADMIN, _('Administrator')),
|
||||||
|
(ROLE_USER, _('User')),
|
||||||
|
(ROLE_APP, _('Application')),
|
||||||
|
(ROLE_AUDITOR, _("Auditor"))
|
||||||
|
)
|
||||||
|
role = ROLE_USER
|
||||||
|
|
||||||
|
@property
|
||||||
|
def role_display(self):
|
||||||
|
return self.get_role_display()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_superuser(self):
|
def is_superuser(self):
|
||||||
if self.role == 'Admin':
|
if self.role == 'Admin':
|
||||||
|
@ -251,41 +183,21 @@ class User(AbstractUser):
|
||||||
def is_staff(self, value):
|
def is_staff(self, value):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@property
|
@classmethod
|
||||||
def is_local(self):
|
def create_app_user(cls, name, comment):
|
||||||
return self.source == self.SOURCE_LOCAL
|
app = cls.objects.create(
|
||||||
|
username=name, name=name, email='{}@local.domain'.format(name),
|
||||||
|
is_active=False, role='App', comment=comment,
|
||||||
|
is_first_login=False, created_by='System'
|
||||||
|
)
|
||||||
|
access_key = app.create_access_key()
|
||||||
|
return app, access_key
|
||||||
|
|
||||||
@property
|
|
||||||
def date_password_expired(self):
|
|
||||||
interval = settings.SECURITY_PASSWORD_EXPIRATION_TIME
|
|
||||||
date_expired = self.date_password_last_updated + timezone.timedelta(
|
|
||||||
days=int(interval))
|
|
||||||
return date_expired
|
|
||||||
|
|
||||||
@property
|
class TokenMixin:
|
||||||
def password_expired_remain_days(self):
|
CACHE_KEY_USER_RESET_PASSWORD_PREFIX = "_KEY_USER_RESET_PASSWORD_{}"
|
||||||
date_remain = self.date_password_expired - timezone.now()
|
email = ''
|
||||||
return date_remain.days
|
id = None
|
||||||
|
|
||||||
@property
|
|
||||||
def password_has_expired(self):
|
|
||||||
if self.is_local and self.password_expired_remain_days < 0:
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
@property
|
|
||||||
def password_will_expired(self):
|
|
||||||
if self.is_local and self.password_expired_remain_days < 5:
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
|
||||||
if not self.name:
|
|
||||||
self.name = self.username
|
|
||||||
if self.username == 'admin':
|
|
||||||
self.role = 'Admin'
|
|
||||||
self.is_active = True
|
|
||||||
super().save(*args, **kwargs)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def private_token(self):
|
def private_token(self):
|
||||||
|
@ -333,31 +245,12 @@ class User(AbstractUser):
|
||||||
def access_key(self):
|
def access_key(self):
|
||||||
return self.access_keys.first()
|
return self.access_keys.first()
|
||||||
|
|
||||||
def is_member_of(self, user_group):
|
|
||||||
if user_group in self.groups.all():
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
def avatar_url(self):
|
|
||||||
admin_default = settings.STATIC_URL + "img/avatar/admin.png"
|
|
||||||
user_default = settings.STATIC_URL + "img/avatar/user.png"
|
|
||||||
if self.avatar:
|
|
||||||
return self.avatar.url
|
|
||||||
if self.is_superuser:
|
|
||||||
return admin_default
|
|
||||||
else:
|
|
||||||
return user_default
|
|
||||||
|
|
||||||
def generate_reset_token(self):
|
def generate_reset_token(self):
|
||||||
letter = string.ascii_letters + string.digits
|
letter = string.ascii_letters + string.digits
|
||||||
token = ''.join([random.choice(letter) for _ in range(50)])
|
token = ''.join([random.choice(letter) for _ in range(50)])
|
||||||
self.set_cache(token)
|
self.set_cache(token)
|
||||||
return token
|
return token
|
||||||
|
|
||||||
def set_cache(self, token):
|
|
||||||
key = self.CACHE_KEY_USER_RESET_PASSWORD_PREFIX.format(token)
|
|
||||||
cache.set(key, {'id': self.id, 'email': self.email}, 3600)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def validate_reset_password_token(cls, token):
|
def validate_reset_password_token(cls, token):
|
||||||
try:
|
try:
|
||||||
|
@ -371,11 +264,25 @@ class User(AbstractUser):
|
||||||
user = None
|
user = None
|
||||||
return user
|
return user
|
||||||
|
|
||||||
|
def set_cache(self, token):
|
||||||
|
key = self.CACHE_KEY_USER_RESET_PASSWORD_PREFIX.format(token)
|
||||||
|
cache.set(key, {'id': self.id, 'email': self.email}, 3600)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def expired_reset_password_token(cls, token):
|
def expired_reset_password_token(cls, token):
|
||||||
key = cls.CACHE_KEY_USER_RESET_PASSWORD_PREFIX.format(token)
|
key = cls.CACHE_KEY_USER_RESET_PASSWORD_PREFIX.format(token)
|
||||||
cache.delete(key)
|
cache.delete(key)
|
||||||
|
|
||||||
|
|
||||||
|
class MFAMixin:
|
||||||
|
otp_level = 0
|
||||||
|
otp_secret_key = ''
|
||||||
|
OTP_LEVEL_CHOICES = (
|
||||||
|
(0, _('Disable')),
|
||||||
|
(1, _('Enable')),
|
||||||
|
(2, _("Force enable")),
|
||||||
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def otp_enabled(self):
|
def otp_enabled(self):
|
||||||
return self.otp_force_enabled or self.otp_level > 0
|
return self.otp_force_enabled or self.otp_level > 0
|
||||||
|
@ -397,39 +304,130 @@ class User(AbstractUser):
|
||||||
self.otp_level = 0
|
self.otp_level = 0
|
||||||
self.otp_secret_key = None
|
self.otp_secret_key = None
|
||||||
|
|
||||||
def to_json(self):
|
|
||||||
return OrderedDict({
|
|
||||||
'id': self.id,
|
|
||||||
'username': self.username,
|
|
||||||
'name': self.name,
|
|
||||||
'email': self.email,
|
|
||||||
'is_active': self.is_active,
|
|
||||||
'is_superuser': self.is_superuser,
|
|
||||||
'role': self.get_role_display(),
|
|
||||||
'groups': [group.name for group in self.groups.all()],
|
|
||||||
'source': self.get_source_display(),
|
|
||||||
'wechat': self.wechat,
|
|
||||||
'phone': self.phone,
|
|
||||||
'otp_level': self.otp_level,
|
|
||||||
'comment': self.comment,
|
|
||||||
'date_expired': self.date_expired.strftime('%Y-%m-%d %H:%M:%S') \
|
|
||||||
if self.date_expired is not None else None
|
|
||||||
})
|
|
||||||
|
|
||||||
@classmethod
|
class User(AuthMixin, TokenMixin, RoleMixin, MFAMixin, AbstractUser):
|
||||||
def create_app_user(cls, name, comment):
|
SOURCE_LOCAL = 'local'
|
||||||
app = cls.objects.create(
|
SOURCE_LDAP = 'ldap'
|
||||||
username=name, name=name, email='{}@local.domain'.format(name),
|
SOURCE_OPENID = 'openid'
|
||||||
is_active=False, role='App', comment=comment,
|
SOURCE_RADIUS = 'radius'
|
||||||
is_first_login=False, created_by='System'
|
SOURCE_CHOICES = (
|
||||||
|
(SOURCE_LOCAL, 'Local'),
|
||||||
|
(SOURCE_LDAP, 'LDAP/AD'),
|
||||||
|
(SOURCE_OPENID, 'OpenID'),
|
||||||
|
(SOURCE_RADIUS, 'Radius'),
|
||||||
)
|
)
|
||||||
access_key = app.create_access_key()
|
|
||||||
return app, access_key
|
|
||||||
|
|
||||||
def reset_password(self, new_password):
|
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
|
||||||
self.set_password(new_password)
|
username = models.CharField(
|
||||||
self.date_password_last_updated = timezone.now()
|
max_length=128, unique=True, verbose_name=_('Username')
|
||||||
self.save()
|
)
|
||||||
|
name = models.CharField(max_length=128, verbose_name=_('Name'))
|
||||||
|
email = models.EmailField(
|
||||||
|
max_length=128, unique=True, verbose_name=_('Email')
|
||||||
|
)
|
||||||
|
groups = models.ManyToManyField(
|
||||||
|
'users.UserGroup', related_name='users',
|
||||||
|
blank=True, verbose_name=_('User group')
|
||||||
|
)
|
||||||
|
role = models.CharField(
|
||||||
|
choices=RoleMixin.ROLE_CHOICES, default='User', max_length=10,
|
||||||
|
blank=True, verbose_name=_('Role')
|
||||||
|
)
|
||||||
|
avatar = models.ImageField(
|
||||||
|
upload_to="avatar", null=True, verbose_name=_('Avatar')
|
||||||
|
)
|
||||||
|
wechat = models.CharField(
|
||||||
|
max_length=128, blank=True, verbose_name=_('Wechat')
|
||||||
|
)
|
||||||
|
phone = models.CharField(
|
||||||
|
max_length=20, blank=True, null=True, verbose_name=_('Phone')
|
||||||
|
)
|
||||||
|
otp_level = models.SmallIntegerField(
|
||||||
|
default=0, choices=MFAMixin.OTP_LEVEL_CHOICES, verbose_name=_('MFA')
|
||||||
|
)
|
||||||
|
otp_secret_key = fields.EncryptCharField(max_length=128, blank=True, null=True)
|
||||||
|
# Todo: Auto generate key, let user download
|
||||||
|
private_key = fields.EncryptTextField(
|
||||||
|
blank=True, null=True, verbose_name=_('Private key')
|
||||||
|
)
|
||||||
|
public_key = fields.EncryptTextField(
|
||||||
|
blank=True, null=True, verbose_name=_('Public key')
|
||||||
|
)
|
||||||
|
comment = models.TextField(
|
||||||
|
blank=True, null=True, verbose_name=_('Comment')
|
||||||
|
)
|
||||||
|
is_first_login = models.BooleanField(default=True)
|
||||||
|
date_expired = models.DateTimeField(
|
||||||
|
default=date_expired_default, blank=True, null=True,
|
||||||
|
db_index=True, verbose_name=_('Date expired')
|
||||||
|
)
|
||||||
|
created_by = models.CharField(
|
||||||
|
max_length=30, default='', verbose_name=_('Created by')
|
||||||
|
)
|
||||||
|
source = models.CharField(
|
||||||
|
max_length=30, default=SOURCE_LOCAL, choices=SOURCE_CHOICES,
|
||||||
|
verbose_name=_('Source')
|
||||||
|
)
|
||||||
|
date_password_last_updated = models.DateTimeField(
|
||||||
|
auto_now_add=True, blank=True, null=True,
|
||||||
|
verbose_name=_('Date password last updated')
|
||||||
|
)
|
||||||
|
|
||||||
|
user_cache_key_prefix = '_User_{}'
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return '{0.name}({0.username})'.format(self)
|
||||||
|
|
||||||
|
def get_absolute_url(self):
|
||||||
|
return reverse('users:user-detail', args=(self.id,))
|
||||||
|
|
||||||
|
@property
|
||||||
|
def groups_display(self):
|
||||||
|
return ' '.join([group.name for group in self.groups.all()])
|
||||||
|
|
||||||
|
@property
|
||||||
|
def source_display(self):
|
||||||
|
return self.get_source_display()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_expired(self):
|
||||||
|
if self.date_expired and self.date_expired < timezone.now():
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_valid(self):
|
||||||
|
if self.is_active and not self.is_expired:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_local(self):
|
||||||
|
return self.source == self.SOURCE_LOCAL
|
||||||
|
|
||||||
|
def save(self, *args, **kwargs):
|
||||||
|
if not self.name:
|
||||||
|
self.name = self.username
|
||||||
|
if self.username == 'admin':
|
||||||
|
self.role = 'Admin'
|
||||||
|
self.is_active = True
|
||||||
|
super().save(*args, **kwargs)
|
||||||
|
|
||||||
|
def is_member_of(self, user_group):
|
||||||
|
if user_group in self.groups.all():
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def avatar_url(self):
|
||||||
|
admin_default = settings.STATIC_URL + "img/avatar/admin.png"
|
||||||
|
user_default = settings.STATIC_URL + "img/avatar/user.png"
|
||||||
|
if self.avatar:
|
||||||
|
return self.avatar.url
|
||||||
|
if self.is_superuser:
|
||||||
|
return admin_default
|
||||||
|
else:
|
||||||
|
return user_default
|
||||||
|
|
||||||
def delete(self, using=None, keep_parents=False):
|
def delete(self, using=None, keep_parents=False):
|
||||||
if self.pk == 1 or self.username == 'admin':
|
if self.pk == 1 or self.username == 'admin':
|
||||||
|
|
|
@ -36,7 +36,7 @@ class UserSerializer(BulkSerializerMixin, serializers.ModelSerializer):
|
||||||
'date_password_last_updated', 'date_expired', 'avatar_url',
|
'date_password_last_updated', 'date_expired', 'avatar_url',
|
||||||
]
|
]
|
||||||
extra_kwargs = {
|
extra_kwargs = {
|
||||||
'password': {'write_only': True, 'required': False},
|
'password': {'write_only': True, 'required': False, 'allow_null': True, 'allow_blank': True},
|
||||||
'public_key': {'write_only': True},
|
'public_key': {'write_only': True},
|
||||||
'groups_display': {'label': _('Groups name')},
|
'groups_display': {'label': _('Groups name')},
|
||||||
'source_display': {'label': _('Source name')},
|
'source_display': {'label': _('Source name')},
|
||||||
|
@ -56,13 +56,17 @@ class UserSerializer(BulkSerializerMixin, serializers.ModelSerializer):
|
||||||
raise serializers.ValidationError(msg)
|
raise serializers.ValidationError(msg)
|
||||||
return value
|
return value
|
||||||
|
|
||||||
@staticmethod
|
def validate_password(self, password):
|
||||||
def validate_password(value):
|
|
||||||
from ..utils import check_password_rules
|
from ..utils import check_password_rules
|
||||||
if not check_password_rules(value):
|
password_strategy = self.initial_data.get('password_strategy')
|
||||||
|
if password_strategy == '0':
|
||||||
|
return
|
||||||
|
if password_strategy is None and not password:
|
||||||
|
return
|
||||||
|
if not check_password_rules(password):
|
||||||
msg = _('Password does not match security rules')
|
msg = _('Password does not match security rules')
|
||||||
raise serializers.ValidationError(msg)
|
raise serializers.ValidationError(msg)
|
||||||
return value
|
return password
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def change_password_to_raw(validated_data):
|
def change_password_to_raw(validated_data):
|
||||||
|
|
|
@ -0,0 +1,165 @@
|
||||||
|
{% load i18n %}
|
||||||
|
<div class="col-lg-3" style="padding-left: 0px">
|
||||||
|
<div class="ibox float-e-margins">
|
||||||
|
<div class="ibox-content mailbox-content" style="padding-top: 0">
|
||||||
|
<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">
|
||||||
|
<div class="mail-box-header">
|
||||||
|
<div class="btn-group" style="float: right">
|
||||||
|
<button data-toggle="dropdown" class="btn btn-default btn-sm labels dropdown-toggle">{% trans 'Label' %} <span class="caret"></span></button>
|
||||||
|
<ul class="dropdown-menu labels-menu">
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<table class="table table-striped table-bordered table-hover" id="user_assets_table" >
|
||||||
|
<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 'System user' %}</th>
|
||||||
|
{% if show_actions %}
|
||||||
|
<th class="text-center">{% trans 'Action' %}</th>
|
||||||
|
{% endif %}
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
var zTree;
|
||||||
|
var inited = false;
|
||||||
|
var url;
|
||||||
|
var assetTable;
|
||||||
|
var treeUrl = "NeedInput";
|
||||||
|
var assetTableUrl = 'NeedInput';
|
||||||
|
var selectUrl = 'NeedInput';
|
||||||
|
var showAssetHref = true; // Need input default true
|
||||||
|
var actions = {};
|
||||||
|
var labels = '';
|
||||||
|
var requesting = false;
|
||||||
|
|
||||||
|
function initTable() {
|
||||||
|
if (inited){
|
||||||
|
return assetTable
|
||||||
|
} else {
|
||||||
|
inited = true;
|
||||||
|
}
|
||||||
|
var options = {
|
||||||
|
ele: $('#user_assets_table'),
|
||||||
|
columnDefs: [
|
||||||
|
{targets: 1, createdCell: function (td, cellData, rowData) {
|
||||||
|
cellData = htmlEscape(cellData);
|
||||||
|
var assetDetailUrl = '{% url 'assets:asset-detail' pk=DEFAULT_PK %}'
|
||||||
|
.replace("{{ DEFAULT_PK }}", rowData.id);
|
||||||
|
var detailBtn = '<a href="assetDetailUrl" class="asset-detail" data-asset="assetId">' + cellData + '</a>';
|
||||||
|
if (showAssetHref) {
|
||||||
|
cellData = detailBtn.replace("assetDetailUrl", assetDetailUrl);
|
||||||
|
} else {
|
||||||
|
detailBtn = detailBtn.replace("assetId", rowData.id);
|
||||||
|
cellData = detailBtn.replace("assetDetailUrl", "");
|
||||||
|
}
|
||||||
|
$(td).html(cellData);
|
||||||
|
}},
|
||||||
|
{targets: 3, createdCell: function (td, cellData) {
|
||||||
|
var users = [];
|
||||||
|
$.each(cellData, function (id, data) {
|
||||||
|
var name = htmlEscape(data.name);
|
||||||
|
users.push(name);
|
||||||
|
});
|
||||||
|
$(td).html(users.join(', '))
|
||||||
|
}},
|
||||||
|
],
|
||||||
|
ajax_url: assetTableUrl,
|
||||||
|
columns: [
|
||||||
|
{data: "id"}, {data: "hostname" }, {data: "ip" },
|
||||||
|
{data: "system_users_granted", orderable: false},
|
||||||
|
{% if show_actions %}
|
||||||
|
{data: "id", orderable: false}
|
||||||
|
{% endif %}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
{% if show_actions %}
|
||||||
|
options.columnDefs.push(actions);
|
||||||
|
{% endif %}
|
||||||
|
assetTable = jumpserver.initServerSideDataTable(options);
|
||||||
|
return assetTable
|
||||||
|
}
|
||||||
|
|
||||||
|
function onSelected(event, treeNode) {
|
||||||
|
var node_id = treeNode.meta.node.id;
|
||||||
|
url = selectUrl.replace("{{ DEFAULT_PK }}", node_id);
|
||||||
|
assetTable.ajax.url(url);
|
||||||
|
assetTable.ajax.reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function initTree() {
|
||||||
|
var setting = {
|
||||||
|
view: {
|
||||||
|
dblClickExpand: false,
|
||||||
|
showLine: true
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
simpleData: {
|
||||||
|
enable: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
callback: {
|
||||||
|
onSelected: onSelected
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
$.get(treeUrl, function(data, status) {
|
||||||
|
$.fn.zTree.init($("#assetTree"), setting, data);
|
||||||
|
zTree = $.fn.zTree.getZTreeObj("assetTree");
|
||||||
|
rootNodeAddDom(zTree, function () {
|
||||||
|
treeUrl = treeUrl.replace('cache_policy=1', 'cache_policy=2');
|
||||||
|
initTree();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadLabels() {
|
||||||
|
var labelListUrl = '{% url "api-assets:label-list" %}';
|
||||||
|
var label = '<li><a style="font-weight: bolder">labelName:labelValue</a></li>';
|
||||||
|
if (requesting) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!labels) {
|
||||||
|
var data = {
|
||||||
|
url: labelListUrl,
|
||||||
|
method: "GET",
|
||||||
|
success: function (data) {
|
||||||
|
data.forEach(function (value) {
|
||||||
|
labels += label.replace("labelName", value.name).replace("labelValue", value.value)
|
||||||
|
});
|
||||||
|
$(".labels-menu").append(labels);
|
||||||
|
requesting = false;
|
||||||
|
},
|
||||||
|
error: function() {
|
||||||
|
requesting = false;
|
||||||
|
},
|
||||||
|
flash_message: false
|
||||||
|
};
|
||||||
|
requesting = true;
|
||||||
|
requestApi(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$(document).ready(function () {
|
||||||
|
loadLabels()
|
||||||
|
}).on('click', '.labels-menu li', function () {
|
||||||
|
var val = $(this).text();
|
||||||
|
$("#user_assets_table_filter input").val(val);
|
||||||
|
assetTable.search(val).draw();
|
||||||
|
})
|
||||||
|
</script>
|
|
@ -4,9 +4,7 @@
|
||||||
{% block user_template_title %}{% trans "Create user" %}{% endblock %}
|
{% block user_template_title %}{% trans "Create user" %}{% endblock %}
|
||||||
{% block password %}
|
{% block password %}
|
||||||
{% bootstrap_field form.password_strategy layout="horizontal" %}
|
{% bootstrap_field form.password_strategy layout="horizontal" %}
|
||||||
<div class="form-group" id="custom_password">
|
|
||||||
{% bootstrap_field form.password layout="horizontal" %}
|
{% bootstrap_field form.password layout="horizontal" %}
|
||||||
</div>
|
|
||||||
{# 密码popover #}
|
{# 密码popover #}
|
||||||
<div id="container">
|
<div id="container">
|
||||||
<div class="popover fade bottom in" role="tooltip" id="popover777" style=" display: none; width:260px;">
|
<div class="popover fade bottom in" role="tooltip" id="popover777" style=" display: none; width:260px;">
|
||||||
|
@ -29,7 +27,7 @@ function passwordCheck() {
|
||||||
progress = $('#id_progress'),
|
progress = $('#id_progress'),
|
||||||
password_check_rules = {{ password_check_rules|safe }},
|
password_check_rules = {{ password_check_rules|safe }},
|
||||||
minLength = 6,
|
minLength = 6,
|
||||||
top = idPassword.offset().top - $('.navbar').outerHeight(true) - $('.page-heading').outerHeight(true) - 10 + 34,
|
top = idPassword.offset().top - $('.navbar').outerHeight(true) - $('.page-heading').outerHeight(true) -77 + 34,
|
||||||
left = 377,
|
left = 377,
|
||||||
i18n_fallback = {
|
i18n_fallback = {
|
||||||
"veryWeak": "{% trans 'Very weak' %}",
|
"veryWeak": "{% trans 'Very weak' %}",
|
||||||
|
@ -67,9 +65,9 @@ var password_strategy_radio_input = 'input[type=radio][name=password_strategy]';
|
||||||
function passwordStrategyFieldsDisplay(){
|
function passwordStrategyFieldsDisplay(){
|
||||||
var val = $('input:radio[name="password_strategy"]:checked').val();
|
var val = $('input:radio[name="password_strategy"]:checked').val();
|
||||||
if(val === '0'){
|
if(val === '0'){
|
||||||
$('#custom_password').addClass('hidden')
|
$('#id_password').parents('.form-group').addClass('hidden')
|
||||||
}else {
|
}else {
|
||||||
$('#custom_password').removeClass('hidden')
|
$('#id_password').parents('.form-group').removeClass('hidden')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
|
@ -78,7 +76,25 @@ $(document).ready(function () {
|
||||||
|
|
||||||
}).on('change', password_strategy_radio_input, function(){
|
}).on('change', password_strategy_radio_input, function(){
|
||||||
passwordStrategyFieldsDisplay()
|
passwordStrategyFieldsDisplay()
|
||||||
})
|
})
|
||||||
|
.on("submit", "form", function (evt) {
|
||||||
|
evt.preventDefault();
|
||||||
|
var the_url = '{% url 'api-users:user-list' %}';
|
||||||
|
var redirect_to = '{% url "users:user-list" %}';
|
||||||
|
var method = "POST";
|
||||||
|
var form = $("form");
|
||||||
|
var data = form.serializeObject();
|
||||||
|
objectAttrsIsList(data, ['groups']);
|
||||||
|
objectAttrsIsDatetime(data,['date_expired']);
|
||||||
|
var props = {
|
||||||
|
url: the_url,
|
||||||
|
data: data,
|
||||||
|
method: method,
|
||||||
|
form: form,
|
||||||
|
redirect_to: redirect_to
|
||||||
|
};
|
||||||
|
formSubmit(props);
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
|
@ -280,7 +280,7 @@ function updateUserGroups(groups) {
|
||||||
// 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
|
||||||
|
@ -305,7 +305,7 @@ $(document).ready(function() {
|
||||||
'is_active': checked
|
'is_active': checked
|
||||||
};
|
};
|
||||||
var success = '{% trans "Update successfully!" %}';
|
var success = '{% trans "Update successfully!" %}';
|
||||||
APIUpdateAttr({
|
requestApi({
|
||||||
url: the_url,
|
url: the_url,
|
||||||
body: JSON.stringify(body),
|
body: JSON.stringify(body),
|
||||||
success_message: success
|
success_message: success
|
||||||
|
@ -332,7 +332,7 @@ $(document).ready(function() {
|
||||||
'otp_secret_key': otp_secret_key
|
'otp_secret_key': otp_secret_key
|
||||||
};
|
};
|
||||||
var success = '{% trans "Update successfully!" %}';
|
var success = '{% trans "Update successfully!" %}';
|
||||||
APIUpdateAttr({
|
requestApi({
|
||||||
url: the_url,
|
url: the_url,
|
||||||
body: JSON.stringify(body),
|
body: JSON.stringify(body),
|
||||||
success_message: success
|
success_message: success
|
||||||
|
@ -372,7 +372,7 @@ $(document).ready(function() {
|
||||||
var msg = "{% trans "An e-mail has been sent to the user`s mailbox." %}";
|
var msg = "{% trans "An e-mail has been sent to the user`s mailbox." %}";
|
||||||
swal("{% trans 'Reset password' %}", msg, "success");
|
swal("{% trans 'Reset password' %}", msg, "success");
|
||||||
};
|
};
|
||||||
APIUpdateAttr({
|
requestApi({
|
||||||
url: the_url,
|
url: the_url,
|
||||||
body: JSON.stringify(body),
|
body: JSON.stringify(body),
|
||||||
success: success
|
success: success
|
||||||
|
@ -398,7 +398,7 @@ $(document).ready(function() {
|
||||||
var msg = "{% trans 'The reset-ssh-public-key E-mail has been sent successfully. Please inform the user to update his new ssh public key.' %}";
|
var msg = "{% trans 'The reset-ssh-public-key E-mail has been sent successfully. Please inform the user to update his new ssh public key.' %}";
|
||||||
swal("{% trans 'Reset SSH public key' %}", msg, "success");
|
swal("{% trans 'Reset SSH public key' %}", msg, "success");
|
||||||
};
|
};
|
||||||
APIUpdateAttr({
|
requestApi({
|
||||||
url: the_url,
|
url: the_url,
|
||||||
body: body,
|
body: body,
|
||||||
success: success
|
success: success
|
||||||
|
@ -441,7 +441,7 @@ $(document).ready(function() {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
APIUpdateAttr({ url: the_url, body: JSON.stringify(body), success: success, error: fail});
|
requestApi({ url: the_url, body: JSON.stringify(body), success: success, error: fail});
|
||||||
}).on('click', '.btn-delete-user', function () {
|
}).on('click', '.btn-delete-user', function () {
|
||||||
var $this = $(this);
|
var $this = $(this);
|
||||||
var name = "{{ user_object.name }}";
|
var name = "{{ user_object.name }}";
|
||||||
|
@ -466,7 +466,7 @@ $(document).ready(function() {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
APIUpdateAttr({
|
requestApi({
|
||||||
url: the_url,
|
url: the_url,
|
||||||
body: JSON.stringify(body),
|
body: JSON.stringify(body),
|
||||||
success: success
|
success: success
|
||||||
|
@ -485,7 +485,7 @@ $(document).ready(function() {
|
||||||
doReset();
|
doReset();
|
||||||
});
|
});
|
||||||
}).on('click', '#btn-reset-mfa', function () {
|
}).on('click', '#btn-reset-mfa', function () {
|
||||||
APIUpdateAttr({
|
requestApi({
|
||||||
url: "{% url 'api-users:user-reset-otp' pk=user_object.id %}",
|
url: "{% url 'api-users:user-reset-otp' pk=user_object.id %}",
|
||||||
method: "GET",
|
method: "GET",
|
||||||
success_message: "{% trans 'Reset user MFA success' %}"
|
success_message: "{% trans 'Reset user MFA success' %}"
|
||||||
|
|
|
@ -23,35 +23,7 @@
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="tab-content">
|
<div class="tab-content">
|
||||||
<div class="col-lg-3" style="padding-left: 0px">
|
{% include 'users/_granted_assets.html' %}
|
||||||
<div class="ibox float-e-margins">
|
|
||||||
<div class="ibox-content mailbox-content" style="padding-top: 0">
|
|
||||||
<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">
|
|
||||||
<div class="mail-box-header">
|
|
||||||
<table class="table table-striped table-bordered table-hover" id="user_assets_table" >
|
|
||||||
<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>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -60,88 +32,9 @@
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block custom_foot_js %}
|
{% block custom_foot_js %}
|
||||||
<script>
|
<script>
|
||||||
var zTree;
|
var assetTableUrl = "{% url 'api-perms:user-assets' pk=object.id %}?cache_policy=1";
|
||||||
var inited = false;
|
var selectUrl = '{% url "api-perms:user-node-assets" pk=object.id node_id=DEFAULT_PK %}?cache_policy=1';
|
||||||
var url;
|
var treeUrl = "{% url 'api-perms:user-nodes-as-tree' pk=object.id %}?&cache_policy=1";
|
||||||
var asset_table;
|
|
||||||
var treeUrl = "{% url 'api-perms:user-nodes-assets-as-tree' pk=object.id %}?show_assets=0&cache_policy=1";
|
|
||||||
|
|
||||||
function initTable() {
|
|
||||||
if (inited){
|
|
||||||
return asset_table
|
|
||||||
} else {
|
|
||||||
inited = true;
|
|
||||||
}
|
|
||||||
url = "{% url 'api-perms:user-assets' pk=object.id %}?cache_policy=1";
|
|
||||||
var options = {
|
|
||||||
ele: $('#user_assets_table'),
|
|
||||||
columnDefs: [
|
|
||||||
{targets: 1, createdCell: function (td, cellData, rowData) {
|
|
||||||
cellData = htmlEscape(cellData);
|
|
||||||
{% url 'assets:asset-detail' pk=DEFAULT_PK as the_url %}
|
|
||||||
var detail_btn = '<a href="{{ the_url }}">' + cellData + '</a>';
|
|
||||||
$(td).html(detail_btn.replace('{{ DEFAULT_PK }}', rowData.id));
|
|
||||||
}},
|
|
||||||
{targets: 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) {
|
|
||||||
var name = htmlEscape(data.name);
|
|
||||||
users.push(name);
|
|
||||||
});
|
|
||||||
$(td).html(users.join(', '))
|
|
||||||
}}
|
|
||||||
],
|
|
||||||
ajax_url: url,
|
|
||||||
columns: [
|
|
||||||
{data: "id"}, {data: "hostname" }, {data: "ip" },
|
|
||||||
{data: "is_active", orderable: false },
|
|
||||||
{data: "system_users_granted", orderable: false}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
asset_table = jumpserver.initServerSideDataTable(options)
|
|
||||||
}
|
|
||||||
|
|
||||||
function onSelected(event, treeNode) {
|
|
||||||
url = '{% url "api-perms:user-node-assets" pk=object.id node_id=DEFAULT_PK %}?cache_policy=1';
|
|
||||||
var node_id = treeNode.meta.node.id;
|
|
||||||
url = url.replace("{{ DEFAULT_PK }}", node_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
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
$.get(treeUrl, function(data, status) {
|
|
||||||
$.fn.zTree.init($("#assetTree"), setting, data);
|
|
||||||
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();
|
||||||
|
|
|
@ -47,5 +47,26 @@ $(document).ready(function () {
|
||||||
closeOnSelect: false
|
closeOnSelect: false
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
.on("submit", "form", function (evt) {
|
||||||
|
evt.preventDefault();
|
||||||
|
var the_url = '{% url 'api-users:user-group-list' %}';
|
||||||
|
var redirect_to = '{% url "users:user-group-list" %}';
|
||||||
|
var method = "POST";
|
||||||
|
{% if type == "update" %}
|
||||||
|
the_url = '{% url 'api-users:user-group-detail' pk=object.id %}';
|
||||||
|
method = "PUT";
|
||||||
|
{% endif %}
|
||||||
|
var form = $("form");
|
||||||
|
var data = form.serializeObject();
|
||||||
|
objectAttrsIsList(data, ['users']);
|
||||||
|
var props = {
|
||||||
|
url: the_url,
|
||||||
|
data: data,
|
||||||
|
method: method,
|
||||||
|
form: form,
|
||||||
|
redirect_to: redirect_to
|
||||||
|
};
|
||||||
|
formSubmit(props);
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -142,7 +142,7 @@ function updateGroupMember(users) {
|
||||||
// clear jumpserver.selected_groups
|
// clear jumpserver.selected_groups
|
||||||
jumpserver.users_selected = {};
|
jumpserver.users_selected = {};
|
||||||
};
|
};
|
||||||
APIUpdateAttr({
|
requestApi({
|
||||||
url: the_url,
|
url: the_url,
|
||||||
body: JSON.stringify(body),
|
body: JSON.stringify(body),
|
||||||
success: success
|
success: success
|
||||||
|
|
|
@ -23,142 +23,21 @@
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="tab-content">
|
<div class="tab-content">
|
||||||
<div class="col-lg-3" style="padding-left: 0px">
|
{% include 'users/_granted_assets.html' %}
|
||||||
<div class="ibox float-e-margins">
|
|
||||||
<div class="ibox-content mailbox-content" style="padding-top: 0">
|
|
||||||
<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">
|
|
||||||
<div class="mail-box-header">
|
|
||||||
<table class="table table-striped table-bordered table-hover" id="user_assets_table" >
|
|
||||||
<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 'Reachable' %}</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block custom_foot_js %}
|
{% block custom_foot_js %}
|
||||||
<script>
|
<script>
|
||||||
var zTree;
|
var treeUrl = "{% url 'api-perms:user-group-nodes-as-tree' pk=object.id %}?cache_policy=1";
|
||||||
var inited = false;
|
var assetTableUrl = "{% url 'api-perms:user-group-assets' pk=object.id %}?cache_policy=1";
|
||||||
var url;
|
var selectUrl = '{% url "api-perms:user-group-node-assets" pk=object.id node_id=DEFAULT_PK %}?cache_policy=1';
|
||||||
var asset_table;
|
var showAssetHref = true; // Need input default true
|
||||||
|
|
||||||
function initTable() {
|
|
||||||
if (inited){
|
|
||||||
return asset_table
|
|
||||||
} else {
|
|
||||||
inited = true;
|
|
||||||
}
|
|
||||||
url = "{% url 'api-perms:user-group-assets' pk=object.id %}";
|
|
||||||
var options = {
|
|
||||||
ele: $('#user_assets_table'),
|
|
||||||
columnDefs: [
|
|
||||||
{targets: 1, createdCell: function (td, cellData, rowData) {
|
|
||||||
cellData = htmlEscape(cellData);
|
|
||||||
{% url 'assets:asset-detail' pk=DEFAULT_PK as the_url %}
|
|
||||||
var detail_btn = '<a href="{{ the_url }}">' + cellData + '</a>';
|
|
||||||
$(td).html(detail_btn.replace('{{ DEFAULT_PK }}', rowData.id));
|
|
||||||
}},
|
|
||||||
{targets: 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) {
|
|
||||||
var name = htmlEscape(data.name);
|
|
||||||
users.push(name);
|
|
||||||
});
|
|
||||||
$(td).html(users.join(', '))
|
|
||||||
}}
|
|
||||||
],
|
|
||||||
ajax_url: url,
|
|
||||||
columns: [
|
|
||||||
{data: "id"}, {data: "hostname" }, {data: "ip" },
|
|
||||||
{data: "is_active", orderable: false },
|
|
||||||
{data: "system_users_granted", orderable: false}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
asset_table = jumpserver.initDataTable(options);
|
|
||||||
return asset_table
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function onSelected(event, treeNode) {
|
|
||||||
url = '{% url "api-perms:user-group-node-assets" pk=object.id node_id=DEFAULT_PK %}';
|
|
||||||
var node_id = treeNode.meta.node.id;
|
|
||||||
url = url.replace("{{ DEFAULT_PK }}", node_id);
|
|
||||||
asset_table.ajax.url(url);
|
|
||||||
asset_table.ajax.reload();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function selectQueryNode() {
|
|
||||||
var query_node_id = $.getUrlParam("node");
|
|
||||||
var cookie_node_id = getCookie('node_selected');
|
|
||||||
var node;
|
|
||||||
var node_id;
|
|
||||||
|
|
||||||
if (query_node_id !== null) {
|
|
||||||
node_id = query_node_id
|
|
||||||
} else if (cookie_node_id !== null) {
|
|
||||||
node_id = cookie_node_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
node = zTree.getNodesByParam("id", node_id, null);
|
|
||||||
if (node){
|
|
||||||
zTree.selectNode(node[0]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function initTree() {
|
|
||||||
var setting = {
|
|
||||||
view: {
|
|
||||||
dblClickExpand: false,
|
|
||||||
showLine: true
|
|
||||||
},
|
|
||||||
data: {
|
|
||||||
simpleData: {
|
|
||||||
enable: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
callback: {
|
|
||||||
onSelected: onSelected
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
$.get("{% url 'api-perms:user-group-nodes-assets-as-tree' pk=object.id %}?show_assets=0", function(data, status) {
|
|
||||||
$.fn.zTree.init($("#assetTree"), setting, data);
|
|
||||||
zTree = $.fn.zTree.getZTreeObj("assetTree");
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
initTree();
|
initTree();
|
||||||
|
|
|
@ -129,7 +129,7 @@ $(document).ready(function() {
|
||||||
swal("{% trans 'UserGroups Delete' %}", msg, "error");
|
swal("{% trans 'UserGroups 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});
|
||||||
jumpserver.checked = false;
|
jumpserver.checked = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue