Unverified Commit 24bdaeca authored by 老广's avatar 老广 Committed by GitHub

Merge pull request #1185 from jumpserver/dev

授权规则优化,支持细颗粒授权
parents d2d10b59 8b3b517b
......@@ -2,4 +2,4 @@
# -*- coding: utf-8 -*-
#
__version__ = "1.0.0"
__version__ = "1.3.0"
......@@ -50,7 +50,9 @@ class AssetViewSet(IDInFilterMixin, LabelFilter, BulkModelViewSet):
if node_id:
node = get_object_or_404(Node, id=node_id)
if not node.is_root():
queryset = queryset.filter(nodes__key__startswith=node.key).distinct()
queryset = queryset.filter(
nodes__key__regex='{}(:[0-9]+)*$'.format(node.key),
).distinct()
return queryset
......
......@@ -30,6 +30,7 @@ from .. import serializers
logger = get_logger(__file__)
__all__ = [
'NodeViewSet', 'NodeChildrenApi',
'NodeAssetsApi', 'NodeWithAssetsApi',
'NodeAddAssetsApi', 'NodeRemoveAssetsApi',
'NodeAddChildrenApi', 'RefreshNodeHardwareInfoApi',
'TestNodeConnectiveApi'
......@@ -47,6 +48,34 @@ class NodeViewSet(BulkModelViewSet):
serializer.save()
class NodeWithAssetsApi(generics.ListAPIView):
permission_classes = (IsSuperUser,)
serializers = serializers.NodeSerializer
def get_node(self):
pk = self.kwargs.get('pk') or self.request.query_params.get('node')
if not pk:
node = Node.root()
else:
node = get_object_or_404(Node, pk)
return node
def get_queryset(self):
queryset = []
node = self.get_node()
children = node.get_children()
assets = node.get_assets()
queryset.extend(list(children))
for asset in assets:
node = Node()
node.id = asset.id
node.parent = node.id
node.value = asset.hostname
queryset.append(node)
return queryset
class NodeChildrenApi(mixins.ListModelMixin, generics.CreateAPIView):
queryset = Node.objects.all()
permission_classes = (IsSuperUser,)
......@@ -69,14 +98,54 @@ class NodeChildrenApi(mixins.ListModelMixin, generics.CreateAPIView):
status=201,
)
def get_object(self):
pk = self.kwargs.get('pk') or self.request.query_params.get('id')
if not pk:
node = Node.root()
else:
node = get_object_or_404(Node, pk=pk)
return node
def get_queryset(self):
queryset = []
query_all = self.request.query_params.get("all")
query_assets = self.request.query_params.get('assets')
node = self.get_object()
if node == Node.root():
queryset.append(node)
if query_all:
children = node.get_all_children()
else:
children = node.get_children()
queryset.extend(list(children))
if query_assets:
assets = node.get_assets()
for asset in assets:
node_fake = Node()
node_fake.id = asset.id
node_fake.parent = node
node_fake.value = asset.hostname
node_fake.is_asset = True
queryset.append(node_fake)
return queryset
def get(self, request, *args, **kwargs):
instance = self.get_object()
if self.request.query_params.get("all"):
children = instance.get_all_children()
return super().list(request, *args, **kwargs)
class NodeAssetsApi(generics.ListAPIView):
permission_classes = (IsSuperUser,)
serializer_class = serializers.AssetSerializer
def get_queryset(self):
node_id = self.kwargs.get('pk')
query_all = self.request.query_params.get('all')
instance = get_object_or_404(Node, pk=node_id)
if query_all:
return instance.get_all_assets()
else:
children = instance.get_children()
response = [{"id": node.id, "key": node.key, "value": node.value} for node in children]
return Response(response, status=200)
return instance.get_assets()
class NodeAddChildrenApi(generics.UpdateAPIView):
......@@ -146,4 +215,3 @@ class TestNodeConnectiveApi(APIView):
task_name = _("测试节点下资产是否可连接: {}".format(node.name))
task = test_asset_connectability_util.delay(assets, task_name=task_name)
return Response({"task": task.id})
......@@ -27,13 +27,16 @@ class AssetCreateForm(forms.ModelForm):
'class': 'select2', 'data-placeholder': _('Admin user')
}),
'labels': forms.SelectMultiple(attrs={
'class': 'select2', 'data-placeholder': _('Labels')
'class': 'select2', 'data-placeholder': _('Label')
}),
'port': forms.TextInput(),
'domain': forms.Select(attrs={
'class': 'select2', 'data-placeholder': _('Domain')
}),
}
labels = {
'nodes': _("Node"),
}
help_texts = {
'hostname': '* required',
'ip': '* required',
......@@ -57,19 +60,22 @@ class AssetUpdateForm(forms.ModelForm):
]
widgets = {
'nodes': forms.SelectMultiple(attrs={
'class': 'select2', 'data-placeholder': _('Nodes')
'class': 'select2', 'data-placeholder': _('Node')
}),
'admin_user': forms.Select(attrs={
'class': 'select2', 'data-placeholder': _('Admin user')
}),
'labels': forms.SelectMultiple(attrs={
'class': 'select2', 'data-placeholder': _('Labels')
'class': 'select2', 'data-placeholder': _('Label')
}),
'port': forms.TextInput(),
'domain': forms.Select(attrs={
'class': 'select2', 'data-placeholder': _('Domain')
}),
}
labels = {
'nodes': _("Node"),
}
help_texts = {
'hostname': '* required',
'ip': '* required',
......@@ -116,10 +122,10 @@ class AssetBulkUpdateForm(forms.ModelForm):
]
widgets = {
'labels': forms.SelectMultiple(
attrs={'class': 'select2', 'data-placeholder': _('Select labels')}
attrs={'class': 'select2', 'data-placeholder': _('Label')}
),
'nodes': forms.SelectMultiple(
attrs={'class': 'select2', 'data-placeholder': _('Select nodes')}
attrs={'class': 'select2', 'data-placeholder': _('Node')}
),
}
......
......@@ -84,7 +84,7 @@ class Asset(models.Model):
comment = models.TextField(max_length=128, default='', blank=True, verbose_name=_('Comment'))
def __str__(self):
return self.hostname
return '{0.hostname}({0.ip})'.format(self)
@property
def is_valid(self):
......@@ -101,6 +101,10 @@ class Asset(models.Model):
else:
return False
def get_nodes(self):
from .node import Node
return self.nodes.all() or [Node.root()]
@property
def hardware_info(self):
if self.cpu_count:
......
......@@ -16,6 +16,8 @@ class Node(models.Model):
child_mark = models.IntegerField(default=0)
date_create = models.DateTimeField(auto_now_add=True)
is_asset = False
def __str__(self):
return self.value
......@@ -73,6 +75,9 @@ class Node(models.Model):
assets = Asset.objects.filter(nodes__in=nodes)
return assets
def has_assets(self):
return self.get_all_assets()
def get_all_active_assets(self):
return self.get_all_assets().filter(is_active=True)
......
......@@ -3,6 +3,7 @@
#
import logging
import uuid
from django.core.cache import cache
from django.db import models
......@@ -100,14 +101,15 @@ class SystemUser(AssetUser):
)
nodes = models.ManyToManyField('assets.Node', blank=True, verbose_name=_("Nodes"))
assets = models.ManyToManyField('assets.Asset', blank=True, verbose_name=_("Assets"))
priority = models.IntegerField(default=10, verbose_name=_("Priority"))
protocol = models.CharField(max_length=16, choices=PROTOCOL_CHOICES, default='ssh', verbose_name=_('Protocol'))
auto_push = models.BooleanField(default=True, verbose_name=_('Auto push'))
sudo = models.TextField(default='/sbin/ifconfig', verbose_name=_('Sudo'))
sudo = models.TextField(default='/bin/whoami', verbose_name=_('Sudo'))
shell = models.CharField(max_length=64, default='/bin/bash', verbose_name=_('Shell'))
def __str__(self):
return self.name
return '{0.name}({0.username})'.format(self)
def to_json(self):
return {
......@@ -119,11 +121,8 @@ class SystemUser(AssetUser):
'auto_push': self.auto_push,
}
@property
def assets(self):
assets = set()
for node in self.nodes.all():
assets.update(set(node.get_all_assets()))
def get_assets(self):
assets = set(self.assets.all())
return assets
@property
......@@ -168,6 +167,3 @@ class SystemUser(AssetUser):
except IntegrityError:
print('Error continue')
continue
......@@ -42,7 +42,7 @@ class NodeSerializer(serializers.ModelSerializer):
class Meta:
model = Node
fields = ['id', 'key', 'value', 'parent', 'assets_amount']
fields = ['id', 'key', 'value', 'parent', 'assets_amount', 'is_asset']
list_serializer_class = BulkListSerializer
@staticmethod
......
......@@ -34,7 +34,7 @@ class SystemUserSerializer(serializers.ModelSerializer):
@staticmethod
def get_assets_amount(obj):
return len(obj.assets)
return len(obj.get_assets())
class SystemUserAuthSerializer(AuthSerializer):
......
# -*- coding: utf-8 -*-
#
from collections import defaultdict
from django.db.models.signals import post_save, m2m_changed
from django.dispatch import receiver
from common.utils import get_logger
from .models import Asset, SystemUser, Node
from .tasks import update_assets_hardware_info_util, \
test_asset_connectability_util, push_system_user_to_node, \
push_node_system_users_to_asset
test_asset_connectability_util, push_system_user_to_assets
logger = get_logger(__file__)
......@@ -31,7 +30,6 @@ def set_asset_root_node(asset):
@receiver(post_save, sender=Asset, dispatch_uid="my_unique_identifier")
def on_asset_created_or_update(sender, instance=None, created=False, **kwargs):
set_asset_root_node(instance)
if created:
logger.info("Asset `{}` create signal received".format(instance))
update_asset_hardware_info_on_created(instance)
......@@ -41,25 +39,39 @@ def on_asset_created_or_update(sender, instance=None, created=False, **kwargs):
@receiver(post_save, sender=SystemUser, dispatch_uid="my_unique_identifier")
def on_system_user_update(sender, instance=None, created=True, **kwargs):
if instance and not created:
for node in instance.nodes.all():
push_system_user_to_node(instance, node)
logger.info("System user `{}` update signal received".format(instance))
assets = instance.assets.all()
push_system_user_to_assets.delay(instance, assets)
@receiver(m2m_changed, sender=SystemUser.nodes.through)
def on_system_user_node_change(sender, instance=None, **kwargs):
def on_system_user_nodes_change(sender, instance=None, **kwargs):
if instance and kwargs["action"] == "post_add":
assets = set()
nodes = kwargs['model'].objects.filter(pk__in=kwargs['pk_set'])
for node in nodes:
assets.update(set(node.get_all_assets()))
instance.assets.add(*tuple(assets))
@receiver(m2m_changed, sender=SystemUser.assets.through)
def on_system_user_assets_change(sender, instance=None, **kwargs):
if instance and kwargs["action"] == "post_add":
for pk in kwargs['pk_set']:
node = kwargs['model'].objects.get(pk=pk)
push_system_user_to_node(instance, node)
assets = kwargs['model'].objects.filter(pk__in=kwargs['pk_set'])
push_system_user_to_assets(instance, assets)
@receiver(m2m_changed, sender=Asset.nodes.through)
def on_asset_node_changed(sender, instance=None, **kwargs):
if isinstance(instance, Asset) and kwargs['action'] == 'post_add':
logger.debug("Asset node change signal received")
for pk in kwargs['pk_set']:
node = kwargs['model'].objects.get(pk=pk)
push_node_system_users_to_asset(node, [instance])
nodes = kwargs['model'].objects.filter(pk__in=kwargs['pk_set'])
system_users_assets = defaultdict(set)
system_users = SystemUser.objects.filter(nodes__in=nodes)
for system_user in system_users:
system_users_assets[system_user].update({instance})
for system_user, assets in system_users_assets.items():
system_user.assets.add(*tuple(assets))
@receiver(m2m_changed, sender=Asset.nodes.through)
......@@ -67,5 +79,6 @@ def on_node_assets_changed(sender, instance=None, **kwargs):
if isinstance(instance, Node) and kwargs['action'] == 'post_add':
logger.debug("Node assets change signal received")
assets = kwargs['model'].objects.filter(pk__in=kwargs['pk_set'])
push_node_system_users_to_asset(instance, assets)
system_users = SystemUser.objects.filter(nodes=instance)
for system_user in system_users:
system_user.assets.add(*tuple(assets))
......@@ -276,7 +276,7 @@ def test_system_user_connectability_util(system_user, task_name):
:return:
"""
from ops.utils import update_or_create_ansible_task
assets = system_user.assets
assets = system_user.get_assets()
hosts = [asset.hostname for asset in assets if asset.is_active and asset.is_unixlike()]
tasks = const.TEST_SYSTEM_USER_CONN_TASKS
if not hosts:
......@@ -386,52 +386,17 @@ def push_system_user_util(system_users, assets, task_name):
return task.run()
def get_node_push_system_user_task_name(system_user, node):
# return _("Push system user to node: {} => {}").format(
return _("推送系统用户到节点资产: {} => {}").format(
system_user.name,
node.value
)
@shared_task
def push_system_user_to_node(system_user, node):
logger.info("Start push system user node: {} => {}".format(system_user.name, node.value))
assets = node.get_all_assets()
task_name = get_node_push_system_user_task_name(system_user, node)
push_system_user_util([system_user], assets, task_name)
@shared_task
def push_system_user_related_nodes(system_user):
if not system_user.is_need_push():
msg = "push system user `{}` passed, may be not auto push or ssh " \
"protocol is not ssh".format(system_user.name)
logger.info(msg)
return
nodes = system_user.nodes.all()
for node in nodes:
push_system_user_to_node(system_user, node)
@shared_task
def push_system_user_to_assets_manual(system_user):
push_system_user_related_nodes(system_user)
assets = system_user.get_assets()
task_name = "推送系统用户到入资产: {}".format(system_user.name)
return push_system_user_util([system_user], assets, task_name=task_name)
def push_node_system_users_to_asset(node, assets):
system_users = []
nodes = node.ancestor_with_node
# 获取该节点所有父节点有的系统用户, 然后推送
for n in nodes:
system_users.extend(list(n.systemuser_set.all()))
if system_users:
# task_name = _("Push system users to node: {}").format(node.value)
task_name = _("推送节点系统用户到新加入资产中: {}").format(node.value)
push_system_user_util.delay(system_users, assets, task_name)
@shared_task
def push_system_user_to_assets(system_user, assets):
task_name = _("推送系统用户到入资产: {}").format(system_user.name)
return push_system_user_util.delay([system_user], assets, task_name)
# @shared_task
......
......@@ -34,7 +34,7 @@
<div class="form-group {% if form.errors.labels %} has-error {% endif %}">
<label for="{{ form.labels.id_for_label }}" class="col-md-2 control-label">{% trans 'Label' %}</label>
<div class="col-md-9">
<select name="labels" class="select2 labels" data-placeholder="{% trans 'Select labels' %}" style="width: 100%" multiple="" tabindex="4" id="{{ form.labels.id_for_label }}">
<select name="labels" class="select2 labels" data-placeholder="{% trans 'Label' %}" style="width: 100%" multiple="" tabindex="4" id="{{ form.labels.id_for_label }}">
{% for name, labels in form.labels.field.queryset|group_labels %}
<optgroup label="{{ name }}">
{% for label in labels %}
......
......@@ -41,9 +41,9 @@
{% block content %}
<div class="wrapper wrapper-content">
<div class="row">
<div class="col-lg-3" id="split-left">
<div class="col-lg-3" id="split-left" style="padding-left: 3px">
<div class="ibox float-e-margins">
<div class="ibox-content mailbox-content" style="padding-top: 0">
<div class="ibox-content mailbox-content" style="padding-top: 0;padding-left: 1px">
<div class="file-manager ">
<div id="assetTree" class="ztree">
</div>
......
......@@ -39,7 +39,7 @@
<div class="form-group">
<label for="{{ form.labels.id_for_label }}" class="col-md-2 control-label">{% trans 'Label' %}</label>
<div class="col-md-9">
<select name="labels" class="select2 labels" data-placeholder="Select labels" style="width: 100%" multiple="" tabindex="4" id="{{ form.labels.id_for_label }}">
<select name="labels" class="select2 labels" data-placeholder="{% trans 'Label' %}" style="width: 100%" multiple="" tabindex="4" id="{{ form.labels.id_for_label }}">
{% for name, labels in form.labels.field.queryset|group_labels %}
<optgroup label="{{ name }}">
{% for label in labels %}
......
{% extends '_base_list.html' %}
{% load i18n %}
{% extends 'base.html' %}
{% load static %}
{% block custom_head_css_js %}
<link href="{% static 'css/plugins/select2/select2.min.css' %}" rel="stylesheet">
<script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script>
{% load i18n %}
{% block custom_head_css_js %}
<link href="{% static 'css/plugins/ztree/awesomeStyle/awesome.css' %}" rel="stylesheet">
<script type="text/javascript" src="{% static 'js/plugins/ztree/jquery.ztree.all.min.js' %}"></script>
<script src="{% static 'js/jquery.form.min.js' %}"></script>
{% endblock %}
{% block content_left_head %}{% endblock %}
{% block table_search %}
{% endblock %}
{% block content %}
<div class="wrapper wrapper-content">
<div class="row">
<div class="col-lg-3" id="split-left" style="padding-left: 3px">
<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>
{% block table_container %}
<table class="table table-striped table-bordered table-hover " id="asset_list_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 'Port' %}</th>
<th class="text-center">{% trans 'Hardware' %}</th>
<th class="text-center">{% trans 'Active' %}</th>
<th class="text-center">{% trans 'Connective' %}</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
<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>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
</div>
</div>
</div>
{% endblock %}
{% block custom_foot_js %}
<script src="{% static 'js/jquery.form.min.js' %}"></script>
<script type="text/javascript">
<script>
var zTree, rMenu, asset_table;
var inited = false;
var url;
function initTable() {
if (inited){
return
} else {
inited = true;
}
var options = {
ele: $('#asset_list_table'),
ele: $('#user_assets_table'),
columnDefs: [
{targets: 1, createdCell: function (td, cellData, rowData) {
{% 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: 5, createdCell: function (td, cellData) {
{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: 6, createdCell: function (td, cellData) {
if (cellData == 'Unknown'){
$(td).html('<i class="fa fa-circle text-warning"></i>')
} else if (!cellData) {
$(td).html('<i class="fa fa-circle text-danger"></i>')
} else {
$(td).html('<i class="fa fa-circle text-navy"></i>')
}
}},
{# {targets: 9, createdCell: function (td, cellData, rowData) {#}
{# var conn_btn = '<a href="{% url "terminal:web-terminal" %}?id={{ DEFAULT_PK }}" class="btn btn-xs btn-info">{% trans "Connect" %}</a>'.replace("{{ DEFAULT_PK }}", cellData);#}
{# $(td).html(conn_btn)#}
{# }}#}
{targets: 4, createdCell: function (td, cellData) {
var users = [];
$.each(cellData, function (id, data) {
users.push(data.name);
});
$(td).html(users.join(', '))
}}
],
ajax_url: '{% url "api-assets:user-asset-list" %}',
ajax_url: url,
columns: [
{data: "id"}, {data: "hostname" }, {data: "ip" }, {data: "port" },
{data: "hardware_info"}, {data: "is_active" }, {data: "is_connective"}
],
op_html: $('#actions').html()
{data: "id"}, {data: "hostname" }, {data: "ip" },
{data: "is_active", orderable: false },
{data: "system_users_granted", orderable: false}
]
};
return jumpserver.initDataTable(options);
asset_table = jumpserver.initDataTable(options);
return asset_table
}
$(document).ready(function(){
function onSelected(event, treeNode) {
console.log("select");
url = '{% url "api-perms:my-node-assets" node_id=DEFAULT_PK %}';
url = url.replace("{{ DEFAULT_PK }}", treeNode.id);
initTable();
});
setCookie('node_selected', treeNode.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
}
};
var zNodes = [];
$.get("{% url 'api-perms:my-nodes' %}", function(data, status){
$.each(data, function (index, value) {
value["pId"] = value["parent"];
if (value["key"] === "0") {
value["open"] = true;
}
value["name"] = value["value"]
});
zNodes = data;
$.fn.zTree.init($("#assetTree"), setting, zNodes);
zTree = $.fn.zTree.getZTreeObj("assetTree");
rMenu = $("#rMenu");
selectQueryNode();
});
}
$(document).ready(function () {
initTree();
});
</script>
{% endblock %}
{% endblock %}
\ No newline at end of file
......@@ -36,7 +36,9 @@ urlpatterns = [
url(r'^v1/system-user/(?P<pk>[0-9a-zA-Z\-]{36})/connective/$',
api.SystemUserTestConnectiveApi.as_view(), name='system-user-connective'),
url(r'^v1/nodes/(?P<pk>[0-9a-zA-Z\-]{36})/children/$', api.NodeChildrenApi.as_view(), name='node-children'),
url(r'^v1/nodes/children/$', api.NodeChildrenApi.as_view(), name='node-children-2'),
url(r'^v1/nodes/(?P<pk>[0-9a-zA-Z\-]{36})/children/add/$', api.NodeAddChildrenApi.as_view(), name='node-add-children'),
url(r'^v1/nodes/(?P<pk>[0-9a-zA-Z\-]{36})/assets/$', api.NodeAssetsApi.as_view(), name='node-assets'),
url(r'^v1/nodes/(?P<pk>[0-9a-zA-Z\-]{36})/assets/add/$', api.NodeAddAssetsApi.as_view(), name='node-add-assets'),
url(r'^v1/nodes/(?P<pk>[0-9a-zA-Z\-]{36})/assets/remove/$', api.NodeRemoveAssetsApi.as_view(), name='node-remove-assets'),
url(r'^v1/nodes/(?P<pk>[0-9a-zA-Z\-]{36})/refresh-hardware-info/$', api.RefreshNodeHardwareInfoApi.as_view(), name='node-refresh-hardware-info'),
......
......@@ -43,3 +43,7 @@ class StringIDField(serializers.Field):
def to_representation(self, value):
return {"pk": value.pk, "name": value.__str__()}
class StringManyToManyField(serializers.RelatedField):
def to_representation(self, value):
return value.__str__()
\ No newline at end of file
......@@ -232,6 +232,14 @@ def setattr_bulk(seq, key, value):
return map(set_attr, seq)
def set_or_append_attr_bulk(seq, key, value):
for obj in seq:
ori = getattr(obj, key, None)
if ori:
value += " " + ori
setattr(obj, key, value)
def content_md5(data):
"""计算data的MD5值,经过Base64编码并返回str类型。
......@@ -350,11 +358,17 @@ def get_short_uuid_str():
return str(uuid.uuid4()).split('-')[-1]
def is_uuid(s):
if UUID_PATTERN.match(s):
return True
def is_uuid(seq):
if isinstance(seq, str):
if UUID_PATTERN.match(seq):
return True
else:
return False
else:
return False
for s in seq:
if not is_uuid(s):
return False
return True
def get_signer():
......@@ -378,3 +392,4 @@ class TeeObj:
def close(self):
self.file_obj.close()
......@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Jumpserver 0.3.3\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-04-06 10:24+0800\n"
"POT-Creation-Date: 2018-04-11 15:13+0800\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: ibuler <ibuler@qq.com>\n"
"Language-Team: Jumpserver team<ibuler@qq.com>\n"
......@@ -17,45 +17,57 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: assets/api/node.py:58
#: assets/api/node.py:87
msgid "New node {}"
msgstr "新节点 {}"
#: assets/api/node.py:133
#: assets/api/node.py:202
msgid "更新节点资产硬件信息: {}"
msgstr ""
#: assets/api/node.py:146
#: assets/api/node.py:215
msgid "测试节点下资产是否可连接: {}"
msgstr ""
#: assets/forms/asset.py:24 assets/forms/asset.py:60 assets/models/asset.py:53
#: assets/models/user.py:102 assets/templates/assets/asset_detail.html:183
#: assets/forms/asset.py:24 assets/models/asset.py:53 assets/models/user.py:103
#: assets/templates/assets/asset_detail.html:183
#: assets/templates/assets/asset_detail.html:191
#: assets/templates/assets/system_user_detail.html:166
#: assets/templates/assets/system_user_detail.html:166 perms/models.py:23
msgid "Nodes"
msgstr "节点管理"
#: assets/forms/asset.py:27 assets/forms/asset.py:63 assets/forms/asset.py:103
#: assets/forms/asset.py:107 assets/models/asset.py:57
#: assets/models/cluster.py:19 assets/models/user.py:71
#: assets/forms/asset.py:27 assets/forms/asset.py:66 assets/forms/asset.py:109
#: assets/forms/asset.py:113 assets/models/asset.py:57
#: assets/models/cluster.py:19 assets/models/user.py:72
#: assets/templates/assets/asset_detail.html:73 templates/_nav.html:25
msgid "Admin user"
msgstr "管理用户"
#: assets/forms/asset.py:30 assets/forms/asset.py:66 assets/models/asset.py:81
#: assets/templates/assets/asset_create.html:33
#: assets/templates/assets/asset_detail.html:220
#: assets/templates/assets/asset_update.html:38 templates/_nav.html:27
msgid "Labels"
msgstr "标签管理"
#: assets/forms/asset.py:30 assets/forms/asset.py:69 assets/forms/asset.py:125
#: assets/templates/assets/asset_create.html:35
#: assets/templates/assets/asset_create.html:37
#: assets/templates/assets/asset_list.html:75
#: assets/templates/assets/asset_update.html:40
#: assets/templates/assets/asset_update.html:42
#: assets/templates/assets/user_asset_list.html:34
msgid "Label"
msgstr "标签"
#: assets/forms/asset.py:34 assets/forms/asset.py:70 assets/models/asset.py:52
#: assets/forms/asset.py:34 assets/forms/asset.py:73 assets/models/asset.py:52
#: assets/models/domain.py:46
msgid "Domain"
msgstr "网域"
#: assets/forms/asset.py:42 assets/forms/asset.py:79
#: assets/forms/asset.py:38 assets/forms/asset.py:63 assets/forms/asset.py:77
#: assets/forms/asset.py:128 assets/templates/assets/asset_create.html:29
#: assets/templates/assets/asset_update.html:34 perms/forms.py:40
#: perms/forms.py:47 perms/models.py:67
#: perms/templates/perms/asset_permission_list.html:57
#: perms/templates/perms/asset_permission_list.html:142
msgid "Node"
msgstr "节点"
#: assets/forms/asset.py:45 assets/forms/asset.py:85
msgid ""
"root or other NOPASSWD sudo privilege user existed in asset,If asset is "
"windows or other set any one, more see admin user left menu"
......@@ -63,46 +75,41 @@ msgstr ""
"root或其他拥有NOPASSWD: ALL权限的用户, 如果是windows或其它硬件可以随意设置一"
"个, 更多信息查看左侧 `管理用户` 菜单"
#: assets/forms/asset.py:45 assets/forms/asset.py:82
#: assets/forms/asset.py:48 assets/forms/asset.py:88
msgid "* required Must set exact system platform, Windows, Linux ..."
msgstr "* required 必须准确设置操作系统平台,如Windows, Linux ..."
#: assets/forms/asset.py:46 assets/forms/asset.py:83
#: assets/forms/asset.py:49 assets/forms/asset.py:89
msgid ""
"If your have some network not connect with each other, you can set domain"
msgstr "如果有多个的互相隔离的网络,设置资产属于的网域,使用网域网关跳转登录"
#: assets/forms/asset.py:90 assets/forms/asset.py:94 assets/forms/domain.py:16
#: assets/forms/asset.py:96 assets/forms/asset.py:100 assets/forms/domain.py:16
#: assets/forms/label.py:15
#: perms/templates/perms/asset_permission_asset.html:88 users/forms.py:272
#: perms/templates/perms/asset_permission_asset.html:88
msgid "Select assets"
msgstr "选择资产"
#: assets/forms/asset.py:99 assets/models/asset.py:51
#: assets/forms/asset.py:105 assets/models/asset.py:51
#: assets/models/domain.py:44 assets/templates/assets/admin_user_assets.html:53
#: assets/templates/assets/asset_detail.html:69
#: assets/templates/assets/domain_gateway_list.html:58
#: assets/templates/assets/system_user_asset.html:51
#: assets/templates/assets/user_asset_list.html:21
msgid "Port"
msgstr "端口"
#: assets/forms/asset.py:119 assets/templates/assets/asset_create.html:37
msgid "Select labels"
msgstr "选择标签"
#: assets/forms/asset.py:122 assets/templates/assets/admin_user_detail.html:91
msgid "Select nodes"
msgstr "选择节点"
#: assets/forms/domain.py:14 assets/forms/label.py:13
#: assets/models/asset.py:165 assets/templates/assets/admin_user_list.html:25
#: assets/models/asset.py:169 assets/templates/assets/admin_user_list.html:25
#: assets/templates/assets/domain_detail.html:60
#: assets/templates/assets/domain_list.html:15
#: assets/templates/assets/label_list.html:16
#: assets/templates/assets/system_user_list.html:29 audits/models.py:11
#: audits/templates/audits/ftp_log_list.html:41
#: audits/templates/audits/ftp_log_list.html:72 perms/models.py:17
#: audits/templates/audits/ftp_log_list.html:72 perms/forms.py:37
#: perms/models.py:22
#: perms/templates/perms/asset_permission_create_update.html:40
#: perms/templates/perms/asset_permission_list.html:56
#: perms/templates/perms/asset_permission_list.html:139
#: terminal/backends/command/models.py:11 terminal/models.py:123
#: terminal/templates/terminal/command_list.html:40
#: terminal/templates/terminal/command_list.html:73
......@@ -125,11 +132,12 @@ msgstr "资产"
#: common/templates/common/terminal_setting.html:67
#: common/templates/common/terminal_setting.html:85 ops/models/adhoc.py:36
#: ops/templates/ops/task_detail.html:59 ops/templates/ops/task_list.html:35
#: perms/models.py:14 perms/templates/perms/asset_permission_detail.html:62
#: perms/models.py:19 perms/templates/perms/asset_permission_detail.html:62
#: perms/templates/perms/asset_permission_list.html:53
#: perms/templates/perms/asset_permission_user.html:54 terminal/models.py:16
#: terminal/models.py:149 terminal/templates/terminal/terminal_detail.html:43
#: terminal/templates/terminal/terminal_list.html:29 users/models/group.py:14
#: users/models/user.py:35 users/templates/users/_select_user_modal.html:13
#: users/models/user.py:40 users/templates/users/_select_user_modal.html:13
#: users/templates/users/user_detail.html:63
#: users/templates/users/user_group_detail.html:55
#: users/templates/users/user_group_list.html:12
......@@ -145,8 +153,8 @@ msgstr "名称"
#: assets/templates/assets/domain_gateway_list.html:60
#: assets/templates/assets/system_user_detail.html:62
#: assets/templates/assets/system_user_list.html:27
#: perms/templates/perms/asset_permission_user.html:55 users/forms.py:14
#: users/models/authentication.py:45 users/models/user.py:34
#: perms/templates/perms/asset_permission_user.html:55 users/forms.py:13
#: users/models/authentication.py:45 users/models/user.py:39
#: users/templates/users/_select_user_modal.html:14
#: users/templates/users/login.html:56
#: users/templates/users/login_log_list.html:49
......@@ -161,7 +169,7 @@ msgid "Password or private key passphrase"
msgstr "密码或密钥密码"
#: assets/forms/user.py:25 assets/models/base.py:22 common/forms.py:113
#: users/forms.py:16 users/forms.py:25 users/templates/users/login.html:59
#: users/forms.py:15 users/forms.py:24 users/templates/users/login.html:59
#: users/templates/users/reset_password.html:52
#: users/templates/users/user_create.html:11
#: users/templates/users/user_password_update.html:40
......@@ -170,7 +178,7 @@ msgstr "密码或密钥密码"
msgid "Password"
msgstr "密码"
#: assets/forms/user.py:28 users/models/user.py:45
#: assets/forms/user.py:28 users/models/user.py:50
msgid "Private key"
msgstr "ssh私钥"
......@@ -199,7 +207,7 @@ msgstr "高优先级的系统用户将会作为默认登录用户"
#: assets/templates/assets/asset_list.html:87
#: assets/templates/assets/domain_gateway_list.html:57
#: assets/templates/assets/system_user_asset.html:50
#: assets/templates/assets/user_asset_list.html:20 common/forms.py:144
#: assets/templates/assets/user_asset_list.html:46 common/forms.py:144
#: perms/templates/perms/asset_permission_asset.html:55
#: users/templates/users/login_log_list.html:52
#: users/templates/users/user_granted_asset.html:45
......@@ -212,7 +220,7 @@ msgstr "IP"
#: assets/templates/assets/asset_detail.html:57
#: assets/templates/assets/asset_list.html:86
#: assets/templates/assets/system_user_asset.html:49
#: assets/templates/assets/user_asset_list.html:19 common/forms.py:143
#: assets/templates/assets/user_asset_list.html:45 common/forms.py:143
#: perms/templates/perms/asset_permission_asset.html:54
#: users/templates/users/user_granted_asset.html:44
#: users/templates/users/user_group_granted_asset.html:44
......@@ -221,7 +229,6 @@ msgstr "主机名"
#: assets/models/asset.py:54 assets/models/domain.py:48
#: assets/models/label.py:20 assets/templates/assets/asset_detail.html:105
#: perms/templates/perms/asset_permission_list.html:70
msgid "Is active"
msgstr "激活"
......@@ -289,15 +296,21 @@ msgstr "系统架构"
msgid "Hostname raw"
msgstr "主机名原始"
#: assets/models/asset.py:81 assets/templates/assets/asset_create.html:33
#: assets/templates/assets/asset_detail.html:220
#: assets/templates/assets/asset_update.html:38 templates/_nav.html:27
msgid "Labels"
msgstr "标签管理"
#: assets/models/asset.py:82 assets/models/base.py:28
#: assets/models/cluster.py:28 assets/models/group.py:21
#: assets/templates/assets/admin_user_detail.html:68
#: assets/templates/assets/asset_detail.html:117
#: assets/templates/assets/domain_detail.html:72
#: assets/templates/assets/system_user_detail.html:96
#: ops/templates/ops/adhoc_detail.html:86 perms/models.py:22 perms/models.py:79
#: perms/templates/perms/asset_permission_detail.html:94
#: users/models/user.py:50 users/templates/users/user_detail.html:99
#: ops/templates/ops/adhoc_detail.html:86 perms/models.py:28 perms/models.py:72
#: perms/templates/perms/asset_permission_detail.html:98
#: users/models/user.py:55 users/templates/users/user_detail.html:99
msgid "Created by"
msgstr "创建者"
......@@ -307,8 +320,8 @@ msgstr "创建者"
#: assets/templates/assets/domain_detail.html:68
#: assets/templates/assets/system_user_detail.html:92
#: ops/templates/ops/adhoc_detail.html:90 ops/templates/ops/task_detail.html:63
#: perms/models.py:23 perms/models.py:80
#: perms/templates/perms/asset_permission_detail.html:90
#: perms/models.py:29 perms/models.py:73
#: perms/templates/perms/asset_permission_detail.html:94
#: terminal/templates/terminal/terminal_detail.html:59 users/models/group.py:17
#: users/templates/users/user_group_detail.html:63
msgid "Date created"
......@@ -325,10 +338,10 @@ msgstr "创建日期"
#: assets/templates/assets/domain_list.html:17
#: assets/templates/assets/system_user_detail.html:100
#: assets/templates/assets/system_user_list.html:33 common/models.py:30
#: ops/models/adhoc.py:42 perms/models.py:24 perms/models.py:81
#: perms/templates/perms/asset_permission_detail.html:98 terminal/models.py:26
#: ops/models/adhoc.py:42 perms/models.py:30 perms/models.py:74
#: perms/templates/perms/asset_permission_detail.html:102 terminal/models.py:26
#: terminal/templates/terminal/terminal_detail.html:63 users/models/group.py:15
#: users/models/user.py:47 users/templates/users/user_detail.html:111
#: users/models/user.py:52 users/templates/users/user_detail.html:111
#: users/templates/users/user_group_detail.html:67
#: users/templates/users/user_group_list.html:14
#: users/templates/users/user_profile.html:114
......@@ -351,7 +364,7 @@ msgstr "带宽"
msgid "Contact"
msgstr "联系人"
#: assets/models/cluster.py:22 users/models/user.py:41
#: assets/models/cluster.py:22 users/models/user.py:46
#: users/templates/users/user_detail.html:76
msgid "Phone"
msgstr "手机"
......@@ -377,7 +390,7 @@ msgid "Default"
msgstr "默认"
#: assets/models/cluster.py:36 assets/models/label.py:13
#: users/models/user.py:266
#: users/models/user.py:285
msgid "System"
msgstr "系统"
......@@ -389,14 +402,14 @@ msgstr "默认Cluster"
msgid "Cluster"
msgstr "集群"
#: assets/models/domain.py:45 assets/models/user.py:104
#: assets/models/domain.py:45 assets/models/user.py:106
#: assets/templates/assets/domain_gateway_list.html:59
#: assets/templates/assets/system_user_detail.html:66
#: assets/templates/assets/system_user_list.html:28
msgid "Protocol"
msgstr "协议"
#: assets/models/group.py:30 perms/models.py:18
#: assets/models/group.py:30
msgid "Asset group"
msgstr "资产组"
......@@ -406,15 +419,19 @@ msgstr "默认资产组"
#: assets/models/label.py:14 audits/models.py:9
#: audits/templates/audits/ftp_log_list.html:33
#: audits/templates/audits/ftp_log_list.html:71 perms/models.py:15
#: audits/templates/audits/ftp_log_list.html:71 perms/forms.py:14
#: perms/forms.py:31 perms/models.py:20
#: perms/templates/perms/asset_permission_create_update.html:36
#: perms/templates/perms/asset_permission_list.html:54
#: perms/templates/perms/asset_permission_list.html:133
#: terminal/backends/command/models.py:10 terminal/models.py:122
#: terminal/templates/terminal/command_list.html:32
#: terminal/templates/terminal/command_list.html:72
#: terminal/templates/terminal/session_list.html:33
#: terminal/templates/terminal/session_list.html:71 users/forms.py:220
#: users/models/user.py:30 users/models/user.py:254
#: terminal/templates/terminal/session_list.html:71 users/forms.py:219
#: users/models/user.py:30 users/models/user.py:273
#: users/templates/users/user_group_detail.html:78
#: users/templates/users/user_group_list.html:13 users/views/user.py:334
#: users/templates/users/user_group_list.html:13 users/views/user.py:335
msgid "User"
msgstr "用户"
......@@ -431,30 +448,49 @@ msgstr "分类"
msgid "Key"
msgstr ""
#: assets/models/user.py:103
#: assets/models/user.py:104
#: assets/templates/assets/_asset_group_bulk_update_modal.html:11
#: assets/templates/assets/system_user_asset.html:21
#: assets/views/admin_user.py:29 assets/views/admin_user.py:47
#: assets/views/admin_user.py:63 assets/views/admin_user.py:78
#: assets/views/admin_user.py:102 assets/views/asset.py:49
#: assets/views/asset.py:95 assets/views/asset.py:155 assets/views/asset.py:172
#: assets/views/asset.py:196 assets/views/domain.py:29
#: assets/views/domain.py:45 assets/views/domain.py:61
#: assets/views/domain.py:74 assets/views/domain.py:98
#: assets/views/domain.py:126 assets/views/domain.py:150
#: assets/views/label.py:26 assets/views/label.py:42 assets/views/label.py:58
#: assets/views/system_user.py:28 assets/views/system_user.py:44
#: assets/views/system_user.py:60 assets/views/system_user.py:74
#: templates/_nav.html:20
msgid "Assets"
msgstr "资产管理"
#: assets/models/user.py:105
msgid "Priority"
msgstr "优先级"
#: assets/models/user.py:105 assets/templates/assets/_system_user.html:58
#: assets/models/user.py:107 assets/templates/assets/_system_user.html:58
#: assets/templates/assets/system_user_detail.html:118
#: assets/templates/assets/system_user_update.html:11
msgid "Auto push"
msgstr "自动推送"
#: assets/models/user.py:106 assets/templates/assets/system_user_detail.html:70
#: assets/models/user.py:108 assets/templates/assets/system_user_detail.html:70
msgid "Sudo"
msgstr "Sudo"
#: assets/models/user.py:107 assets/templates/assets/system_user_detail.html:75
#: assets/models/user.py:109 assets/templates/assets/system_user_detail.html:75
msgid "Shell"
msgstr "Shell"
#: assets/models/user.py:150 audits/models.py:12
#: assets/models/user.py:149 audits/models.py:12
#: audits/templates/audits/ftp_log_list.html:49
#: audits/templates/audits/ftp_log_list.html:73 perms/forms.py:25
#: perms/models.py:19 perms/models.py:76
#: perms/templates/perms/asset_permission_detail.html:136
#: perms/templates/perms/asset_permission_list.html:69 templates/_nav.html:26
#: audits/templates/audits/ftp_log_list.html:73 perms/forms.py:43
#: perms/models.py:24 perms/models.py:69
#: perms/templates/perms/asset_permission_detail.html:140
#: perms/templates/perms/asset_permission_list.html:58
#: perms/templates/perms/asset_permission_list.html:145 templates/_nav.html:26
#: terminal/backends/command/models.py:12 terminal/models.py:124
#: terminal/templates/terminal/command_list.html:48
#: terminal/templates/terminal/command_list.html:74
......@@ -496,12 +532,8 @@ msgstr "测试系统用户可连接性: {}"
msgid "定期测试系统用户可连接性: {}"
msgstr ""
#: assets/tasks.py:392
msgid "推送系统用户到节点资产: {} => {}"
msgstr ""
#: assets/tasks.py:433
msgid "推送节点系统用户到新加入资产中: {}"
#: assets/tasks.py:398
msgid "推送系统用户到入资产: {}"
msgstr ""
#: assets/templates/assets/_asset_group_bulk_update_modal.html:5
......@@ -512,28 +544,13 @@ msgstr "更新用户组"
msgid "Hint: only change the field you want to update."
msgstr "仅修改你需要更新的字段"
#: assets/templates/assets/_asset_group_bulk_update_modal.html:11
#: assets/templates/assets/system_user_asset.html:21
#: assets/views/admin_user.py:29 assets/views/admin_user.py:47
#: assets/views/admin_user.py:63 assets/views/admin_user.py:78
#: assets/views/admin_user.py:102 assets/views/asset.py:49
#: assets/views/asset.py:95 assets/views/asset.py:155 assets/views/asset.py:172
#: assets/views/asset.py:196 assets/views/domain.py:29
#: assets/views/domain.py:45 assets/views/domain.py:61
#: assets/views/domain.py:74 assets/views/domain.py:98
#: assets/views/domain.py:126 assets/views/domain.py:150
#: assets/views/label.py:26 assets/views/label.py:42 assets/views/label.py:58
#: assets/views/system_user.py:28 assets/views/system_user.py:44
#: assets/views/system_user.py:60 assets/views/system_user.py:74
#: templates/_nav.html:20
msgid "Assets"
msgstr "资产管理"
#: assets/templates/assets/_asset_group_bulk_update_modal.html:13
msgid "Select Asset"
msgstr "选择资产"
#: assets/templates/assets/_asset_group_bulk_update_modal.html:21
#: assets/templates/assets/user_asset_list.html:48
#: users/templates/users/user_granted_asset.html:47
msgid "System users"
msgstr "系统用户"
......@@ -569,17 +586,17 @@ msgstr "如果设置了id,则会使用该行信息更新该id的资产"
#: assets/templates/assets/_asset_list_modal.html:22
#: assets/templates/assets/asset_list.html:88
#: assets/templates/assets/user_asset_list.html:22
msgid "Hardware"
msgstr "硬件"
#: assets/templates/assets/_asset_list_modal.html:23
#: assets/templates/assets/asset_detail.html:143
#: assets/templates/assets/asset_list.html:89
#: assets/templates/assets/user_asset_list.html:23 perms/models.py:20
#: perms/models.py:77
#: perms/templates/perms/asset_permission_create_update.html:51
#: perms/templates/perms/asset_permission_detail.html:116
#: assets/templates/assets/user_asset_list.html:47 perms/models.py:25
#: perms/models.py:70
#: perms/templates/perms/asset_permission_create_update.html:47
#: perms/templates/perms/asset_permission_detail.html:120
#: perms/templates/perms/asset_permission_list.html:59
#: terminal/templates/terminal/terminal_list.html:34
#: users/templates/users/_select_user_modal.html:18
#: users/templates/users/user_detail.html:128
......@@ -596,7 +613,6 @@ msgstr "激活中"
#: assets/templates/assets/asset_list.html:90
#: assets/templates/assets/system_user_asset.html:52
#: assets/templates/assets/system_user_list.html:30
#: users/templates/users/user_granted_asset.html:47
#: users/templates/users/user_group_granted_asset.html:47
msgid "Reachable"
msgstr "可连接"
......@@ -610,7 +626,7 @@ msgstr "可连接"
#: assets/templates/assets/system_user_list.html:34
#: ops/templates/ops/adhoc_history.html:59 ops/templates/ops/task_adhoc.html:64
#: ops/templates/ops/task_history.html:65 ops/templates/ops/task_list.html:42
#: perms/templates/perms/asset_permission_list.html:72
#: perms/templates/perms/asset_permission_list.html:60
#: terminal/templates/terminal/session_list.html:80
#: terminal/templates/terminal/terminal_list.html:36
#: users/templates/users/user_group_list.html:15
......@@ -656,7 +672,7 @@ msgstr "激活所选"
#: common/templates/common/email_setting.html:60
#: common/templates/common/ldap_setting.html:60
#: common/templates/common/terminal_setting.html:103
#: perms/templates/perms/asset_permission_create_update.html:72
#: perms/templates/perms/asset_permission_create_update.html:70
#: terminal/templates/terminal/session_list.html:120
#: terminal/templates/terminal/terminal_update.html:48
#: users/templates/users/_user.html:44
......@@ -683,7 +699,7 @@ msgstr "提交"
#: assets/templates/assets/system_user_detail.html:26
#: assets/templates/assets/system_user_list.html:88
#: perms/templates/perms/asset_permission_detail.html:30
#: perms/templates/perms/asset_permission_list.html:121
#: perms/templates/perms/asset_permission_list.html:191
#: terminal/templates/terminal/terminal_detail.html:16
#: terminal/templates/terminal/terminal_list.html:71
#: users/templates/users/user_detail.html:25
......@@ -709,7 +725,7 @@ msgstr "更新"
#: assets/templates/assets/system_user_list.html:89
#: ops/templates/ops/task_list.html:72
#: perms/templates/perms/asset_permission_detail.html:34
#: perms/templates/perms/asset_permission_list.html:122
#: perms/templates/perms/asset_permission_list.html:192
#: terminal/templates/terminal/terminal_list.html:73
#: users/templates/users/user_detail.html:30
#: users/templates/users/user_group_detail.html:32
......@@ -723,7 +739,7 @@ msgstr "删除"
#: assets/templates/assets/asset_create.html:16
#: assets/templates/assets/asset_update.html:21
#: assets/templates/assets/gateway_create_update.html:37
#: perms/templates/perms/asset_permission_create_update.html:38
#: perms/templates/perms/asset_permission_create_update.html:33
msgid "Basic"
msgstr "基本"
......@@ -745,7 +761,7 @@ msgstr "自动生成密钥"
#: assets/templates/assets/asset_create.html:59
#: assets/templates/assets/asset_update.html:63
#: assets/templates/assets/gateway_create_update.html:53
#: perms/templates/perms/asset_permission_create_update.html:49
#: perms/templates/perms/asset_permission_create_update.html:45
#: terminal/templates/terminal/terminal_update.html:42
msgid "Other"
msgstr "其它"
......@@ -762,7 +778,7 @@ msgstr "其它"
#: common/templates/common/email_setting.html:59
#: common/templates/common/ldap_setting.html:59
#: common/templates/common/terminal_setting.html:101
#: perms/templates/perms/asset_permission_create_update.html:71
#: perms/templates/perms/asset_permission_create_update.html:69
#: terminal/templates/terminal/terminal_update.html:47
#: users/templates/users/_user.html:43
#: users/templates/users/user_bulk_update.html:23
......@@ -802,7 +818,7 @@ msgstr "资产列表"
#: assets/templates/assets/admin_user_assets.html:66
#: assets/templates/assets/system_user_asset.html:64
#: assets/templates/assets/system_user_detail.html:112
#: perms/templates/perms/asset_permission_detail.html:110
#: perms/templates/perms/asset_permission_detail.html:114
msgid "Quick update"
msgstr "快速更新"
......@@ -822,9 +838,14 @@ msgstr "测试"
msgid "Replace node assets admin user with this"
msgstr "替换资产的管理员"
#: assets/templates/assets/admin_user_detail.html:91
#: perms/templates/perms/asset_permission_asset.html:116
msgid "Select nodes"
msgstr "选择节点"
#: assets/templates/assets/admin_user_detail.html:100
#: assets/templates/assets/asset_detail.html:200
#: assets/templates/assets/asset_list.html:600
#: assets/templates/assets/asset_list.html:603
#: assets/templates/assets/system_user_detail.html:183
#: assets/templates/assets/system_user_list.html:138 templates/_modal.html:16
#: terminal/templates/terminal/session_detail.html:108
......@@ -855,19 +876,6 @@ msgstr "不可达"
msgid "Ratio"
msgstr "比例"
#: assets/templates/assets/asset_create.html:29
#: assets/templates/assets/asset_update.html:34 perms/models.py:74
#: perms/templates/perms/asset_permission_create_update.html:40
#: perms/templates/perms/asset_permission_list.html:67
msgid "Node"
msgstr "节点"
#: assets/templates/assets/asset_create.html:35
#: assets/templates/assets/asset_list.html:75
#: assets/templates/assets/asset_update.html:40
msgid "Label"
msgstr "标签"
#: assets/templates/assets/asset_detail.html:20 assets/views/asset.py:197
msgid "Asset detail"
msgstr "资产详情"
......@@ -949,15 +957,15 @@ msgstr "重命名节点"
msgid "Delete node"
msgstr "删除节点"
#: assets/templates/assets/asset_list.html:203
#: assets/templates/assets/asset_list.html:202
msgid "Create node failed"
msgstr "创建节点失败"
#: assets/templates/assets/asset_list.html:216
#: assets/templates/assets/asset_list.html:215
msgid "Have child node, cancel"
msgstr "存在子节点,不能删除"
#: assets/templates/assets/asset_list.html:595
#: assets/templates/assets/asset_list.html:598
#: assets/templates/assets/system_user_list.html:133
#: users/templates/users/user_detail.html:334
#: users/templates/users/user_detail.html:359
......@@ -966,20 +974,20 @@ msgstr "存在子节点,不能删除"
msgid "Are you sure?"
msgstr "你确认吗?"
#: assets/templates/assets/asset_list.html:596
#: assets/templates/assets/asset_list.html:599
msgid "This will delete the selected assets !!!"
msgstr "删除选择资产"
#: assets/templates/assets/asset_list.html:604
#: assets/templates/assets/asset_list.html:607
msgid "Asset Deleted."
msgstr "已被删除"
#: assets/templates/assets/asset_list.html:605
#: assets/templates/assets/asset_list.html:610
#: assets/templates/assets/asset_list.html:608
#: assets/templates/assets/asset_list.html:613
msgid "Asset Delete"
msgstr "删除"
#: assets/templates/assets/asset_list.html:609
#: assets/templates/assets/asset_list.html:612
msgid "Asset Deleting failed."
msgstr "删除失败"
......@@ -1088,10 +1096,6 @@ msgstr "删除系统用户"
msgid "System Users Deleting failed."
msgstr "系统用户删除失败"
#: assets/templates/assets/user_asset_list.html:24
msgid "Connective"
msgstr "连接性"
#: assets/views/admin_user.py:30
msgid "Admin user list"
msgstr "管理用户列表"
......@@ -1120,7 +1124,7 @@ msgstr "批量更新资产"
msgid "Update asset"
msgstr "更新资产"
#: assets/views/asset.py:300
#: assets/views/asset.py:308
msgid "already exists"
msgstr "已经存在"
......@@ -1182,18 +1186,33 @@ msgstr "远端地址"
msgid "Operate"
msgstr "操作"
#: audits/models.py:14 audits/templates/audits/ftp_log_list.html:76
#: audits/models.py:14 audits/templates/audits/ftp_log_list.html:56
#: audits/templates/audits/ftp_log_list.html:76
msgid "Filename"
msgstr "文件名"
#: audits/templates/audits/ftp_log_list.html:77
#: audits/models.py:15 audits/templates/audits/ftp_log_list.html:77
#: ops/templates/ops/task_list.html:39
msgid "Success"
msgstr "成功"
#: audits/templates/audits/ftp_log_list.html:78
#: ops/templates/ops/adhoc_history.html:52
#: ops/templates/ops/adhoc_history_detail.html:61
#: ops/templates/ops/task_history.html:58 terminal/models.py:132
#: ops/templates/ops/task_history.html:58 perms/models.py:26
#: perms/templates/perms/asset_permission_detail.html:86 terminal/models.py:132
#: terminal/templates/terminal/session_list.html:77
msgid "Date start"
msgstr "开始日期"
#: audits/views.py:50 templates/_nav.html:64
msgid "Audits"
msgstr "日志审计"
#: audits/views.py:51 templates/_nav.html:67
msgid "FTP log"
msgstr "FTP日志"
#: common/api.py:18
msgid "Test mail sent to {}, please check"
msgstr "邮件已经发送{}, 请检查"
......@@ -1655,10 +1674,6 @@ msgstr "搜索"
msgid "Versions"
msgstr "版本"
#: ops/templates/ops/task_list.html:39
msgid "Success"
msgstr "成功"
#: ops/templates/ops/task_list.html:40
#: users/templates/users/login_log_list.html:54
msgid "Date"
......@@ -1685,24 +1700,37 @@ msgstr "任务列表"
msgid "Task run history"
msgstr "执行历史"
#: perms/forms.py:22 perms/models.py:16 perms/models.py:75
#: perms/templates/perms/asset_permission_list.html:68 templates/_nav.html:14
#: users/models/group.py:25 users/models/user.py:37
#: perms/forms.py:18 users/forms.py:176 users/forms.py:181 users/forms.py:193
#: users/forms.py:223
msgid "Select users"
msgstr "选择用户"
#: perms/forms.py:34 perms/models.py:21 perms/models.py:68
#: perms/templates/perms/asset_permission_list.html:55
#: perms/templates/perms/asset_permission_list.html:136 templates/_nav.html:14
#: users/models/group.py:25 users/models/user.py:42
#: users/templates/users/_select_user_modal.html:16
#: users/templates/users/user_detail.html:179
#: users/templates/users/user_list.html:26
msgid "User group"
msgstr "用户组"
#: perms/models.py:21 perms/models.py:78
#: perms/templates/perms/asset_permission_detail.html:86
#: perms/templates/perms/asset_permission_list.html:71 users/models/user.py:49
#: users/templates/users/user_detail.html:95
#: perms/forms.py:56
msgid "User or group at least one required"
msgstr ""
#: perms/forms.py:65
msgid "Asset or group at least one required"
msgstr ""
#: perms/models.py:27 perms/models.py:71
#: perms/templates/perms/asset_permission_detail.html:90
#: users/models/user.py:54 users/templates/users/user_detail.html:95
#: users/templates/users/user_profile.html:96
msgid "Date expired"
msgstr "失效日期"
#: perms/models.py:88 templates/_nav.html:34
#: perms/models.py:81 templates/_nav.html:34
msgid "Asset permission"
msgstr "资产授权"
......@@ -1723,7 +1751,7 @@ msgid "Add asset to this permission"
msgstr "添加资产"
#: perms/templates/perms/asset_permission_asset.html:97
#: perms/templates/perms/asset_permission_detail.html:153
#: perms/templates/perms/asset_permission_detail.html:157
#: perms/templates/perms/asset_permission_user.html:97
#: perms/templates/perms/asset_permission_user.html:125
#: users/templates/users/user_group_detail.html:95
......@@ -1731,12 +1759,8 @@ msgid "Add"
msgstr "添加"
#: perms/templates/perms/asset_permission_asset.html:108
msgid "Add asset group to this permission"
msgstr "添加资产组"
#: perms/templates/perms/asset_permission_asset.html:116 users/forms.py:275
msgid "Select asset groups"
msgstr "选择资产组"
msgid "Add node to this permission"
msgstr "添加节点"
#: perms/templates/perms/asset_permission_asset.html:125
#: users/templates/users/user_detail.html:196
......@@ -1756,18 +1780,18 @@ msgid "Asset count"
msgstr "资产数量"
#: perms/templates/perms/asset_permission_detail.html:78
msgid "Asset group count"
msgstr "资产组数量"
msgid "Node count"
msgstr "节点数量"
#: perms/templates/perms/asset_permission_detail.html:82
msgid "System user count"
msgstr "系统用户数量"
#: perms/templates/perms/asset_permission_detail.html:144 users/forms.py:278
#: perms/templates/perms/asset_permission_detail.html:148
msgid "Select system users"
msgstr "选择系统用户"
#: perms/templates/perms/asset_permission_list.html:58
#: perms/templates/perms/asset_permission_list.html:46
msgid "Create permission"
msgstr "创建授权规则"
......@@ -1792,22 +1816,31 @@ msgstr "添加用户组"
msgid "Select user groups"
msgstr "选择用户组"
#: perms/views.py:23 perms/views.py:47 perms/views.py:67 templates/_nav.html:31
#: perms/views.py:25 perms/views.py:55 perms/views.py:70 perms/views.py:85
#: perms/views.py:120 perms/views.py:151 templates/_nav.html:31
msgid "Perms"
msgstr "权限管理"
#: perms/views.py:24
#: perms/views.py:26
msgid "Asset permission list"
msgstr "资产授权列表"
#: perms/views.py:48
#: perms/views.py:56
msgid "Create asset permission"
msgstr "创建权限规则"
#: perms/views.py:68
#: perms/views.py:71 perms/views.py:86
msgid "Update asset permission"
msgstr "更新资产授权"
#: perms/views.py:121
msgid "Asset permission user list"
msgstr "资产授权用户列表"
#: perms/views.py:152
msgid "Asset permission asset list"
msgstr "资产授权资产列表"
#: templates/_header_bar.html:18
msgid "Supports"
msgstr "商业支持"
......@@ -1816,13 +1849,13 @@ msgstr "商业支持"
msgid "Docs"
msgstr "文档"
#: templates/_header_bar.html:37 templates/_nav_user.html:9 users/forms.py:94
#: templates/_header_bar.html:37 templates/_nav_user.html:9 users/forms.py:93
#: users/templates/users/_user.html:36
#: users/templates/users/user_password_update.html:37
#: users/templates/users/user_profile.html:17
#: users/templates/users/user_profile_update.html:37
#: users/templates/users/user_profile_update.html:57
#: users/templates/users/user_pubkey_update.html:37 users/views/user.py:317
#: users/templates/users/user_pubkey_update.html:37 users/views/user.py:318
msgid "Profile"
msgstr "个人信息"
......@@ -1879,9 +1912,9 @@ msgstr "关闭"
#: templates/_nav.html:10 users/views/group.py:28 users/views/group.py:44
#: users/views/group.py:62 users/views/group.py:79 users/views/group.py:95
#: users/views/login.py:209 users/views/login.py:258 users/views/user.py:60
#: users/views/user.py:75 users/views/user.py:94 users/views/user.py:150
#: users/views/user.py:305 users/views/user.py:352 users/views/user.py:374
#: users/views/login.py:205 users/views/login.py:254 users/views/user.py:60
#: users/views/user.py:75 users/views/user.py:95 users/views/user.py:151
#: users/views/user.py:306 users/views/user.py:353 users/views/user.py:375
msgid "Users"
msgstr "用户管理"
......@@ -1925,14 +1958,6 @@ msgstr "终端管理"
msgid "Job Center"
msgstr "作业中心"
#: templates/_nav.html:64
msgid "Audits"
msgstr "日志审计"
#: templates/_nav.html:67
msgid "FTP log"
msgstr "FTP日志"
#: templates/captcha/image.html:3
msgid "Play CAPTCHA as audio file"
msgstr "语言播放验证码"
......@@ -2199,7 +2224,7 @@ msgstr ""
msgid "Invalid token or cache refreshed."
msgstr ""
#: users/forms.py:28 users/models/user.py:38
#: users/forms.py:27 users/models/user.py:43
#: users/templates/users/_select_user_modal.html:15
#: users/templates/users/user_detail.html:87
#: users/templates/users/user_list.html:25
......@@ -2207,55 +2232,55 @@ msgstr ""
msgid "Role"
msgstr "角色"
#: users/forms.py:30 users/forms.py:140
#: users/forms.py:29 users/forms.py:139
msgid "ssh public key"
msgstr "ssh公钥"
#: users/forms.py:31 users/forms.py:141
#: users/forms.py:30 users/forms.py:140
msgid "ssh-rsa AAAA..."
msgstr ""
#: users/forms.py:32
#: users/forms.py:31
msgid "Paste user id_rsa.pub here."
msgstr "复制用户公钥到这里"
#: users/forms.py:50 users/templates/users/user_detail.html:187
#: users/forms.py:49 users/templates/users/user_detail.html:187
msgid "Join user groups"
msgstr "添加到用户组"
#: users/forms.py:60 users/forms.py:155
#: users/forms.py:59 users/forms.py:154
msgid "Public key should not be the same as your old one."
msgstr "不能和原来的密钥相同"
#: users/forms.py:64 users/forms.py:159 users/serializers.py:42
#: users/forms.py:63 users/forms.py:158 users/serializers.py:42
msgid "Not a valid ssh public key"
msgstr "ssh密钥不合法"
#: users/forms.py:100
#: users/forms.py:99
msgid "Old password"
msgstr "原来密码"
#: users/forms.py:105
#: users/forms.py:104
msgid "New password"
msgstr "新密码"
#: users/forms.py:110
#: users/forms.py:109
msgid "Confirm password"
msgstr "确认密码"
#: users/forms.py:120
#: users/forms.py:119
msgid "Old password error"
msgstr "原来密码错误"
#: users/forms.py:128
#: users/forms.py:127
msgid "Password does not match"
msgstr "密码不一致"
#: users/forms.py:142
#: users/forms.py:141
msgid "Paste your id_rsa.pub here."
msgstr "复制你的公钥到这里"
#: users/forms.py:170 users/models/user.py:46
#: users/forms.py:169 users/models/user.py:51
#: users/templates/users/user_password_update.html:43
#: users/templates/users/user_profile.html:71
#: users/templates/users/user_profile_update.html:43
......@@ -2263,10 +2288,6 @@ msgstr "复制你的公钥到这里"
msgid "Public key"
msgstr "ssh公钥"
#: users/forms.py:177 users/forms.py:182 users/forms.py:194 users/forms.py:224
msgid "Select users"
msgstr "选择用户"
#: users/models/authentication.py:36
msgid "Private Token"
msgstr "ssh密钥"
......@@ -2291,7 +2312,7 @@ msgstr "Agent"
msgid "Date login"
msgstr "登录日期"
#: users/models/user.py:29 users/models/user.py:262
#: users/models/user.py:29 users/models/user.py:281
msgid "Administrator"
msgstr "管理员"
......@@ -2299,24 +2320,36 @@ msgstr "管理员"
msgid "Application"
msgstr "应用程序"
#: users/models/user.py:36 users/templates/users/user_detail.html:71
#: users/models/user.py:34
msgid "Disable"
msgstr "禁用"
#: users/models/user.py:35
msgid "Enable"
msgstr "启用"
#: users/models/user.py:36
msgid "Force enable"
msgstr "强制启用"
#: users/models/user.py:41 users/templates/users/user_detail.html:71
#: users/templates/users/user_profile.html:59
msgid "Email"
msgstr "邮件"
#: users/models/user.py:39
#: users/models/user.py:44
msgid "Avatar"
msgstr "头像"
#: users/models/user.py:40 users/templates/users/user_detail.html:82
#: users/models/user.py:45 users/templates/users/user_detail.html:82
msgid "Wechat"
msgstr "微信"
#: users/models/user.py:42
#: users/models/user.py:47
msgid "Enable OTP"
msgstr "二次验证"
#: users/models/user.py:265
#: users/models/user.py:284
msgid "Administrator is the super user of system"
msgstr "Administrator是初始的超级管理员"
......@@ -2431,7 +2464,7 @@ msgid "Reset link will be generated and sent to the user. "
msgstr "生成重置密码连接,通过邮件发送给用户"
#: users/templates/users/user_detail.html:19
#: users/templates/users/user_granted_asset.html:18 users/views/user.py:151
#: users/templates/users/user_granted_asset.html:18 users/views/user.py:152
msgid "User detail"
msgstr "用户详情"
......@@ -2550,8 +2583,8 @@ msgstr "用户删除失败"
msgid "OTP"
msgstr ""
#: users/templates/users/user_profile.html:100 users/views/user.py:180
#: users/views/user.py:234
#: users/templates/users/user_profile.html:100 users/views/user.py:181
#: users/views/user.py:235
msgid "User groups"
msgstr "用户组"
......@@ -2587,7 +2620,7 @@ msgstr "更新密钥"
msgid "Or reset by server"
msgstr "或者重置并下载密钥"
#: users/templates/users/user_update.html:4 users/views/user.py:94
#: users/templates/users/user_update.html:4 users/views/user.py:95
msgid "Update user"
msgstr "更新用户"
......@@ -2733,78 +2766,76 @@ msgstr "更新用户组"
msgid "User group granted asset"
msgstr "用户组授权资产"
#: users/views/login.py:57
#: users/views/login.py:55
msgid "Please enable cookies and try again."
msgstr "设置你的浏览器支持cookie"
#: users/views/login.py:99
#: users/views/login.py:97
msgid "Logout success"
msgstr "退出登录成功"
#: users/views/login.py:100
#: users/views/login.py:98
msgid "Logout success, return login page"
msgstr "退出登录成功,返回到登录页面"
#: users/views/login.py:116
#: users/views/login.py:114
msgid "Email address invalid, please input again"
msgstr "邮箱地址错误,重新输入"
#: users/views/login.py:129
#: users/views/login.py:127
msgid "Send reset password message"
msgstr "发送重置密码邮件"
#: users/views/login.py:130
#: users/views/login.py:128
msgid "Send reset password mail success, login your mail box and follow it "
msgstr ""
"发送重置邮件成功, 请登录邮箱查看, 按照提示操作 (如果没收到,请等待3-5分钟)"
#: users/views/login.py:144
#: users/views/login.py:142
msgid "Reset password success"
msgstr "重置密码成功"
#: users/views/login.py:145
#: users/views/login.py:143
msgid "Reset password success, return to login page"
msgstr "重置密码成功,返回到登录页面"
#: users/views/login.py:162 users/views/login.py:175
#: users/views/login.py:160 users/views/login.py:173
msgid "Token invalid or expired"
msgstr "Token错误或失效"
#: users/views/login.py:171
#: users/views/login.py:169
msgid "Password not same"
msgstr "密码不一致"
#: users/views/login.py:209
#: users/views/login.py:205
msgid "First login"
msgstr "首次登陆"
#: users/views/login.py:259
#: users/views/login.py:255
msgid "Login log list"
msgstr "登录日志"
#: users/views/user.py:104
#: users/views/user.py:105
msgid "Bulk update user success"
msgstr "批量更新用户成功"
#: users/views/user.py:209
#: users/views/user.py:210
msgid "Invalid file."
msgstr "文件不合法"
#: users/views/user.py:306
#: users/views/user.py:307
msgid "User granted assets"
msgstr "用户授权资产"
#: users/views/user.py:335
#: users/views/user.py:336
msgid "Profile setting"
msgstr "个人信息设置"
#: users/views/user.py:353
#: users/views/user.py:354
msgid "Password update"
msgstr "密码更新"
#: users/views/user.py:375
#: users/views/user.py:376
msgid "Public key update"
msgstr "密钥更新"
#~ msgid "Task has been send, seen left asset status"
#~ msgstr "任务已下发,查看左侧资产状态"
......@@ -3,13 +3,14 @@
from django.shortcuts import get_object_or_404
from rest_framework.views import APIView, Response
from rest_framework.generics import ListAPIView, get_object_or_404
from rest_framework.generics import ListAPIView, get_object_or_404, RetrieveUpdateAPIView
from rest_framework import viewsets
from common.utils import set_or_append_attr_bulk
from users.permissions import IsValidUser, IsSuperUser, IsSuperUserOrAppUser
from .utils import NodePermissionUtil
from .models import NodePermission
from .hands import AssetGrantedSerializer, User, UserGroup, Asset, \
from .utils import AssetPermissionUtil
from .models import AssetPermission
from .hands import AssetGrantedSerializer, User, UserGroup, Asset, Node, \
NodeGrantedSerializer, SystemUser, NodeSerializer
from . import serializers
......@@ -18,7 +19,7 @@ class AssetPermissionViewSet(viewsets.ModelViewSet):
"""
资产授权列表的增删改查api
"""
queryset = NodePermission.objects.all()
queryset = AssetPermission.objects.all()
serializer_class = serializers.AssetPermissionCreateUpdateSerializer
permission_classes = (IsSuperUser,)
......@@ -29,12 +30,28 @@ class AssetPermissionViewSet(viewsets.ModelViewSet):
def get_queryset(self):
queryset = super().get_queryset()
node_id = self.request.query_params.get('node_id')
asset_id = self.request.query_params.get('asset')
node_id = self.request.query_params.get('node')
inherit_nodes = set()
if not asset_id and not node_id:
return queryset
if node_id:
queryset = queryset.filter(node__id=node_id)
permissions = set()
if asset_id:
asset = get_object_or_404(Asset, pk=asset_id)
permissions = set(queryset.filter(assets=asset))
for node in asset.nodes.all():
inherit_nodes.update(set(node.ancestor_with_node))
elif node_id:
node = get_object_or_404(Node, pk=node_id)
permissions = set(queryset.filter(nodes=node))
inherit_nodes = node.ancestor
return queryset
for n in inherit_nodes:
_permissions = queryset.filter(nodes=n)
set_or_append_attr_bulk(_permissions, "inherit", n.value)
permissions.update(_permissions)
return permissions
class UserGrantedAssetsApi(ListAPIView):
......@@ -53,7 +70,7 @@ class UserGrantedAssetsApi(ListAPIView):
else:
user = self.request.user
for k, v in NodePermissionUtil.get_user_assets(user).items():
for k, v in AssetPermissionUtil.get_user_assets(user).items():
if k.is_unixlike():
system_users_granted = [s for s in v if s.protocol == 'ssh']
else:
......@@ -78,38 +95,11 @@ class UserGrantedNodesApi(ListAPIView):
user = get_object_or_404(User, id=user_id)
else:
user = self.request.user
nodes = NodePermissionUtil.get_user_nodes(user)
nodes = AssetPermissionUtil.get_user_nodes_with_assets(user)
return nodes.keys()
class UserGrantedNodesWithAssetsApi(ListAPIView):
"""
授权用户的资产组,注:这里的资产组并非是授权列表中授权的,
而是把所有资产取出来,然后反查出所有资产组,然后合并得到,
结果里也包含资产组下授权的资产
数据结构如下:
[
{
"id": 1,
"value": "node",
... 其它属性
"assets_granted": [
{
"id": 1,
"hostname": "testserver",
"ip": "192.168.1.1",
"port": 22,
"system_users_granted": [
"id": 1,
"name": "web",
"username": "web",
"protocol": "ssh",
]
}
]
}
]
"""
permission_classes = (IsSuperUserOrAppUser,)
serializer_class = NodeGrantedSerializer
......@@ -121,18 +111,16 @@ class UserGrantedNodesWithAssetsApi(ListAPIView):
else:
user = get_object_or_404(User, id=user_id)
nodes = NodePermissionUtil.get_user_nodes_with_assets(user)
assets = {}
for k, v in NodePermissionUtil.get_user_assets(user).items():
if k.is_unixlike():
system_users_granted = [s for s in v if s.protocol == 'ssh']
else:
system_users_granted = [s for s in v if s.protocol == 'rdp']
assets[k] = system_users_granted
for node, v in nodes.items():
for asset in v['assets']:
asset.system_users_granted = assets[asset]
node.assets_granted = v['assets']
nodes = AssetPermissionUtil.get_user_nodes_with_assets(user)
for node, _assets in nodes.items():
assets = _assets.keys()
for k, v in _assets.items():
if k.is_unixlike():
system_users_granted = [s for s in v if s.protocol == 'ssh']
else:
system_users_granted = [s for s in v if s.protocol == 'rdp']
k.system_users_granted = system_users_granted
node.assets_granted = assets
queryset.append(node)
return queryset
......@@ -142,6 +130,31 @@ class UserGrantedNodesWithAssetsApi(ListAPIView):
return super().get_permissions()
class UserGrantedNodeAssetsApi(ListAPIView):
permission_classes = (IsSuperUserOrAppUser,)
serializer_class = AssetGrantedSerializer
def get_queryset(self):
user_id = self.kwargs.get('pk', '')
node_id = self.kwargs.get('node_id')
if user_id:
user = get_object_or_404(User, id=user_id)
else:
user = self.request.user
node = get_object_or_404(Node, id=node_id)
nodes = AssetPermissionUtil.get_user_nodes_with_assets(user)
assets = nodes.get(node, [])
for asset, system_users in assets.items():
asset.system_users_granted = system_users
return assets
def get_permissions(self):
if self.kwargs.get('pk') is None:
self.permission_classes = (IsValidUser,)
return super().get_permissions()
class UserGroupGrantedAssetsApi(ListAPIView):
permission_classes = (IsSuperUser,)
serializer_class = AssetGrantedSerializer
......@@ -154,7 +167,7 @@ class UserGroupGrantedAssetsApi(ListAPIView):
return queryset
user_group = get_object_or_404(UserGroup, id=user_group_id)
assets = NodePermissionUtil.get_user_group_assets(user_group)
assets = AssetPermissionUtil.get_user_group_assets(user_group)
for k, v in assets.items():
k.system_users_granted = v
queryset.append(k)
......@@ -171,8 +184,8 @@ class UserGroupGrantedNodesApi(ListAPIView):
if group_id:
group = get_object_or_404(UserGroup, id=group_id)
nodes = NodePermissionUtil.get_user_group_nodes(group)
queryset = nodes.keys()
nodes = AssetPermissionUtil.get_user_group_nodes_with_assets(group)
return nodes.keys()
return queryset
......@@ -188,15 +201,33 @@ class UserGroupGrantedNodesWithAssetsApi(ListAPIView):
return queryset
user_group = get_object_or_404(UserGroup, id=user_group_id)
nodes = NodePermissionUtil.get_user_group_nodes_with_assets(user_group)
for node, v in nodes.items():
for asset in v['assets']:
asset.system_users_granted = v['system_users']
node.assets_granted = v['assets']
nodes = AssetPermissionUtil.get_user_group_nodes_with_assets(user_group)
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 UserGroupGrantedNodeAssetsApi(ListAPIView):
permission_classes = (IsSuperUserOrAppUser,)
serializer_class = AssetGrantedSerializer
def get_queryset(self):
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)
node = get_object_or_404(Node, id=node_id)
nodes = AssetPermissionUtil.get_user_group_nodes_with_assets(user_group)
assets = nodes.get(node, [])
for asset, system_users in assets.items():
asset.system_users_granted = system_users
return assets
class ValidateUserAssetPermissionView(APIView):
permission_classes = (IsSuperUserOrAppUser,)
......@@ -210,8 +241,82 @@ class ValidateUserAssetPermissionView(APIView):
asset = get_object_or_404(Asset, id=asset_id)
system_user = get_object_or_404(SystemUser, id=system_id)
assets_granted = NodePermissionUtil.get_user_assets(user)
assets_granted = AssetPermissionUtil.get_user_assets(user)
if system_user in assets_granted.get(asset, []):
return Response({'msg': True}, status=200)
else:
return Response({'msg': False}, status=403)
class AssetPermissionRemoveUserApi(RetrieveUpdateAPIView):
"""
将用户从授权中移除,Detail页面会调用
"""
permission_classes = (IsSuperUser,)
serializer_class = serializers.AssetPermissionUpdateUserSerializer
queryset = AssetPermission.objects.all()
def update(self, request, *args, **kwargs):
perm = self.get_object()
serializer = self.serializer_class(data=request.data)
if serializer.is_valid():
users = serializer.validated_data.get('users')
if users:
perm.users.remove(*tuple(users))
return Response({"msg": "ok"})
else:
return Response({"error": serializer.errors})
class AssetPermissionAddUserApi(RetrieveUpdateAPIView):
permission_classes = (IsSuperUser,)
serializer_class = serializers.AssetPermissionUpdateUserSerializer
queryset = AssetPermission.objects.all()
def update(self, request, *args, **kwargs):
perm = self.get_object()
serializer = self.serializer_class(data=request.data)
if serializer.is_valid():
users = serializer.validated_data.get('users')
if users:
perm.users.add(*tuple(users))
return Response({"msg": "ok"})
else:
return Response({"error": serializer.errors})
class AssetPermissionRemoveAssetApi(RetrieveUpdateAPIView):
"""
将用户从授权中移除,Detail页面会调用
"""
permission_classes = (IsSuperUser,)
serializer_class = serializers.AssetPermissionUpdateAssetSerializer
queryset = AssetPermission.objects.all()
def update(self, request, *args, **kwargs):
perm = self.get_object()
serializer = self.serializer_class(data=request.data)
if serializer.is_valid():
assets = serializer.validated_data.get('assets')
if assets:
perm.assets.remove(*tuple(assets))
return Response({"msg": "ok"})
else:
return Response({"error": serializer.errors})
class AssetPermissionAddAssetApi(RetrieveUpdateAPIView):
permission_classes = (IsSuperUser,)
serializer_class = serializers.AssetPermissionUpdateAssetSerializer
queryset = AssetPermission.objects.all()
def update(self, request, *args, **kwargs):
perm = self.get_object()
serializer = self.serializer_class(data=request.data)
if serializer.is_valid():
assets = serializer.validated_data.get('assets')
if assets:
perm.assets.add(*tuple(assets))
return Response({"msg": "ok"})
else:
return Response({"error": serializer.errors})
......@@ -4,27 +4,64 @@ from __future__ import absolute_import, unicode_literals
from django import forms
from django.utils.translation import ugettext_lazy as _
from .models import NodePermission
from .hands import User
from .models import AssetPermission
class AssetPermissionForm(forms.ModelForm):
users = forms.ModelMultipleChoiceField(
queryset=User.objects.exclude(role=User.ROLE_APP),
label=_("User"),
widget=forms.SelectMultiple(
attrs={
'class': 'select2',
'data-placeholder': _('Select users')
}
),
required=False,
)
class Meta:
model = NodePermission
fields = [
'node', 'user_group', 'system_user', 'is_active',
'date_expired', 'comment',
]
model = AssetPermission
exclude = (
'id', 'date_created', 'created_by'
)
widgets = {
'node': forms.Select(
attrs={'style': 'display:none'}
'users': forms.SelectMultiple(
attrs={'class': 'select2', 'data-placeholder': _("User")}
),
'user_group': forms.Select(
'user_groups': forms.SelectMultiple(
attrs={'class': 'select2', 'data-placeholder': _("User group")}
),
'system_user': forms.Select(
'assets': forms.SelectMultiple(
attrs={'class': 'select2', 'data-placeholder': _("Asset")}
),
'nodes': forms.SelectMultiple(
attrs={'class': 'select2', 'data-placeholder': _("Node")}
),
'system_users': forms.SelectMultiple(
attrs={'class': 'select2', 'data-placeholder': _('System user')}
),
}
labels = {
'nodes': _("Node"),
}
def clean_user_groups(self):
users = self.cleaned_data.get('users')
user_groups = self.cleaned_data.get('user_groups')
if not users and not user_groups:
raise forms.ValidationError(
_("User or group at least one required"))
return self.cleaned_data["user_groups"]
def clean_asset_groups(self):
assets = self.cleaned_data.get('assets')
asset_groups = self.cleaned_data.get('asset_groups')
if not assets and not asset_groups:
raise forms.ValidationError(
_("Asset or group at least one required"))
def clean_system_user(self):
return self.cleaned_data['system_user']
return self.cleaned_data["asset_groups"]
......@@ -4,70 +4,63 @@ from django.db import models
from django.utils.translation import ugettext_lazy as _
from django.utils import timezone
from common.utils import date_expired_default
from common.utils import date_expired_default, set_or_append_attr_bulk
class ValidManager(models.Manager):
def get_queryset(self):
return super().get_queryset().filter(is_active=True) \
.filter(date_start__lt=timezone.now())\
.filter(date_expired__gt=timezone.now())
class AssetPermission(models.Model):
from users.models import User, UserGroup
from assets.models import Asset, AssetGroup, SystemUser, Cluster
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
name = models.CharField(max_length=128, unique=True, verbose_name=_('Name'))
users = models.ManyToManyField(User, related_name='asset_permissions', blank=True, verbose_name=_("User"))
user_groups = models.ManyToManyField(UserGroup, related_name='asset_permissions', blank=True, verbose_name=_("User group"))
assets = models.ManyToManyField(Asset, related_name='granted_by_permissions', blank=True, verbose_name=_("Asset"))
asset_groups = models.ManyToManyField(AssetGroup, related_name='granted_by_permissions', blank=True, verbose_name=_("Asset group"))
system_users = models.ManyToManyField(SystemUser, related_name='granted_by_permissions', verbose_name=_("System user"))
users = models.ManyToManyField('users.User', related_name='asset_permissions', blank=True, verbose_name=_("User"))
user_groups = models.ManyToManyField('users.UserGroup', related_name='asset_permissions', blank=True, verbose_name=_("User group"))
assets = models.ManyToManyField('assets.Asset', related_name='granted_by_permissions', blank=True, verbose_name=_("Asset"))
nodes = models.ManyToManyField('assets.Node', related_name='granted_by_permissions', blank=True, verbose_name=_("Nodes"))
system_users = models.ManyToManyField('assets.SystemUser', related_name='granted_by_permissions', verbose_name=_("System user"))
is_active = models.BooleanField(default=True, verbose_name=_('Active'))
date_start = models.DateTimeField(default=timezone.now, verbose_name=_("Date start"))
date_expired = models.DateTimeField(default=date_expired_default, verbose_name=_('Date expired'))
created_by = models.CharField(max_length=128, blank=True, verbose_name=_('Created by'))
date_created = models.DateTimeField(auto_now_add=True, verbose_name=_('Date created'))
comment = models.TextField(verbose_name=_('Comment'), blank=True)
objects = models.Manager()
valid = ValidManager()
def __str__(self):
return self.name
@property
def id_str(self):
return str(self.id)
@property
def is_valid(self):
if self.date_expired > timezone.now() and self.is_active:
if self.date_expired > timezone.now() > self.date_start and self.is_active:
return True
return False
def get_granted_users(self):
return list(set(self.users.all()) | self.get_granted_user_groups_member())
def get_granted_user_groups_member(self):
users = set()
for user_group in self.user_groups.all():
for user in user_group.users.all():
setattr(user, 'is_inherit_from_user_groups', True)
setattr(user, 'inherit_from_user_groups',
getattr(user, 'inherit_from_user_groups', set()).add(user_group))
users.add(user)
def get_all_users(self):
users = set(self.users.all())
for group in self.user_groups.all():
_users = group.users.all()
set_or_append_attr_bulk(_users, 'inherit', group.name)
users.update(set(_users))
return users
def get_granted_assets(self):
return list(set(self.assets.all()) | self.get_granted_asset_groups_member())
def get_granted_asset_groups_member(self):
assets = set()
for asset_group in self.asset_groups.all():
for asset in asset_group.assets.all():
setattr(asset, 'is_inherit_from_asset_groups', True)
setattr(asset, 'inherit_from_asset_groups',
getattr(asset, 'inherit_from_user_groups', set()).add(asset_group))
assets.add(asset)
def get_all_assets(self):
assets = set(self.assets.all())
for node in self.nodes.all():
_assets = node.get_all_assets()
set_or_append_attr_bulk(_assets, 'inherit', node.value)
assets.update(set(_assets))
return assets
def check_system_user_in_assets(self):
errors = {}
assets = self.get_granted_assets()
clusters = set([asset.cluster for asset in assets])
for system_user in self.system_users.all():
cluster_remain = clusters - set(system_user.cluster.all())
if cluster_remain:
errors[system_user] = cluster_remain
return errors
class NodePermission(models.Model):
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
......
# -*- coding: utf-8 -*-
#
from django.utils.translation import ugettext_lazy as _
from rest_framework import serializers
from common.utils import get_object_or_none
from common.fields import StringIDField
from .models import AssetPermission, NodePermission
from .models import AssetPermission
from common.fields import StringManyToManyField
class AssetPermissionCreateUpdateSerializer(serializers.ModelSerializer):
class Meta:
model = NodePermission
fields = [
'id', 'node', 'user_group', 'system_user',
'is_active', 'date_expired'
]
model = AssetPermission
exclude = ('id', 'created_by', 'date_created')
class AssetPermissionListSerializer(serializers.ModelSerializer):
node = StringIDField(read_only=True)
user_group = StringIDField(read_only=True)
system_user = StringIDField(read_only=True)
users = StringManyToManyField(many=True, read_only=True)
user_groups = StringManyToManyField(many=True, read_only=True)
assets = StringManyToManyField(many=True, read_only=True)
nodes = StringManyToManyField(many=True, read_only=True)
system_users = StringManyToManyField(many=True, read_only=True)
inherit = serializers.SerializerMethodField()
class Meta:
model = NodePermission
model = AssetPermission
fields = '__all__'
@staticmethod
def get_inherit(obj):
if hasattr(obj, 'inherit'):
return obj.inherit
else:
return None
class AssetPermissionUpdateUserSerializer(serializers.ModelSerializer):
......@@ -40,14 +45,3 @@ class AssetPermissionUpdateAssetSerializer(serializers.ModelSerializer):
model = AssetPermission
fields = ['id', 'assets']
class UserAssetPermissionCreateUpdateSerializer(AssetPermissionCreateUpdateSerializer):
is_inherited = serializers.SerializerMethodField()
@staticmethod
def get_is_inherited(obj):
if getattr(obj, 'inherited', ''):
return True
else:
return False
# -*- coding: utf-8 -*-
#
from django.db.models.signals import post_save, post_delete
from django.db.models.signals import m2m_changed
from django.dispatch import receiver
from common.utils import get_logger
from .models import NodePermission
from .models import AssetPermission
logger = get_logger(__file__)
@receiver(post_save, sender=NodePermission, dispatch_uid="my_unique_identifier")
def on_asset_permission_create_or_update(sender, instance=None, **kwargs):
if instance and instance.node and instance.system_user:
instance.system_user.nodes.add(instance.node)
@receiver(m2m_changed, sender=AssetPermission.nodes.through)
def on_permission_nodes_changed(sender, instance=None, **kwargs):
if isinstance(instance, AssetPermission) and kwargs['action'] == 'post_add':
logger.debug("Asset permission nodes change signal received")
nodes = kwargs['model'].objects.filter(pk__in=kwargs['pk_set'])
system_users = instance.system_users.all()
for system_user in system_users:
system_user.nodes.add(*tuple(nodes))
@receiver(m2m_changed, sender=AssetPermission.assets.through)
def on_permission_assets_changed(sender, instance=None, **kwargs):
if isinstance(instance, AssetPermission) and kwargs['action'] == 'post_add':
logger.debug("Asset permission assets change signal received")
assets = kwargs['model'].objects.filter(pk__in=kwargs['pk_set'])
system_users = instance.system_users.all()
for system_user in system_users:
system_user.assets.add(*tuple(assets))
@receiver(m2m_changed, sender=AssetPermission.system_users.through)
def on_permission_system_users_changed(sender, instance=None, **kwargs):
if isinstance(instance, AssetPermission) and kwargs['action'] == 'post_add':
logger.debug("Asset permission system_users change signal received")
system_users = kwargs['model'].objects.filter(pk__in=kwargs['pk_set'])
assets = instance.assets.all()
nodes = instance.nodes.all()
for system_user in system_users:
system_user.nodes.add(*tuple(nodes))
system_user.assets.add(*tuple(assets))
......@@ -57,12 +57,12 @@
</tr>
</thead>
<tbody>
{% for asset in page_obj %}
{% for asset in object_list %}
<tr>
<td>{{ asset.hostname }}</td>
<td>{{ asset.ip }}</td>
<td>
<button title="{{ asset.inherit_from_asset_groups }}" data-gid="{{ asset.id }}" class="btn btn-danger btn-xs btn-remove-asset {% if asset.is_inherit_from_asset_groups %} disabled {% endif %}" type="button" style="float: right;"><i class="fa fa-minus"></i></button>
<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 %}
......@@ -105,7 +105,7 @@
<div class="panel panel-info">
<div class="panel-heading">
<i class="fa fa-info-circle"></i> {% trans 'Add asset group to this permission' %}
<i class="fa fa-info-circle"></i> {% trans 'Add node to this permission' %}
</div>
<div class="panel-body">
<table class="table group_edit">
......@@ -113,25 +113,25 @@
<form>
<tr>
<td colspan="2" class="no-borders">
<select data-placeholder="{% trans 'Select asset groups' %}" class="select2 group" style="width: 100%" multiple="" tabindex="4">
{% for asset_group in asset_groups_remain %}
<option value="{{ asset_group.id }}" id="opt_{{ asset_group.id }}">{{ asset_group.name }}</option>
<select data-placeholder="{% trans 'Select nodes' %}" class="select2 group" style="width: 100%" multiple="" tabindex="4">
{% for node in nodes_remain %}
<option value="{{ node.id }}" id="opt_{{ node.id }}">{{ node.value }}</option>
{% endfor %}
</select>
</td>
</tr>
<tr>
<td colspan="2" class="no-borders">
<button type="button" class="btn btn-info btn-sm" id="btn-add-group">{% trans 'Join' %}</button>
<button type="button" class="btn btn-info btn-sm" id="btn-add-node">{% trans 'Join' %}</button>
</td>
</tr>
</form>
{% for asset_group in asset_groups %}
{% for node in asset_permission.nodes.all %}
<tr>
<td ><b class="bdg_user_group" data-gid={{ asset_group.id }}>{{ asset_group.name }}</b></td>
<td ><b class="bdg_user_group" data-gid={{ node.id }}>{{ node.value }}</b></td>
<td>
<button class="btn btn-danger btn-xs btn-remove-group" type="button" style="float: right;"><i class="fa fa-minus"></i></button>
<button class="btn btn-danger btn-xs btn-remove-node" type="button" style="float: right;"><i class="fa fa-minus"></i></button>
</td>
</tr>
{% endfor %}
......@@ -179,10 +179,10 @@ function removeAssets(assets) {
});
}
function updateGroup(groups) {
function updateNodes(nodes) {
var the_url = "{% url 'api-perms:asset-permission-detail' pk=asset_permission.id %}";
var body = {
asset_groups: groups
nodes: nodes
};
APIUpdateAttr({
url: the_url,
......@@ -231,17 +231,17 @@ $(document).ready(function () {
var assets = [asset_id];
removeAssets(assets)
})
.on('click', '#btn-add-group', function () {
.on('click', '#btn-add-node', function () {
if (Object.keys(jumpserver.nodes_selected).length === 0) {
return false;
}
var groups = $('.bdg_group').map(function() {
var nodes = $('.bdg_group').map(function() {
return $(this).data('gid');
}).get();
$.map(jumpserver.nodes_selected, function(group_name, index) {
groups.push(index);
nodes.push(index);
$('#opt_' + index).remove();
$('.group_edit tbody').append(
'<tr>' +
......@@ -251,17 +251,17 @@ $(document).ready(function () {
)
});
updateGroup(groups);
updateNodes(nodes);
})
.on('click', '.btn-remove-group', function () {
.on('click', '.btn-remove-node', function () {
var $this = $(this);
var $tr = $this.closest('tr');
var groups = $('.bdg_group').map(function() {
var nodes = $('.bdg_group').map(function() {
if ($(this).data('gid') !== $this.data('gid')){
return $(this).data('gid');
}
}).get();
updateGroup(groups);
updateNodes(nodes);
$tr.remove()
})
</script>
......
......@@ -28,23 +28,19 @@
</div>
</div>
<div class="ibox-content">
{% if form.non_field_errors %}
<div class="alert alert-danger">
{{ form.non_field_errors }}
</div>
{% endif %}
<form method="post" class="form-horizontal" action="" >
{% csrf_token %}
<h3>{% trans 'Basic' %}</h3>
<div class="form-group">
<label class="col-md-2 control-label" for="id_name">{% trans 'Node' %}</label>
<div class="col-md-9">
<input type="text" class="form-control" readonly value="{{ form.node.initial }}">
</div>
</div>
{{ form.node }}
{% bootstrap_field form.user_group layout="horizontal" %}
{% bootstrap_field form.system_user layout="horizontal" %}
{% bootstrap_field form.name layout="horizontal" %}
<div class="hr-line-dashed"></div>
<h3>{% trans 'User' %}</h3>
{% bootstrap_field form.users layout="horizontal" %}
{% bootstrap_field form.user_groups layout="horizontal" %}
<div class="hr-line-dashed"></div>
<h3>{% trans 'Asset' %}</h3>
{% bootstrap_field form.assets layout="horizontal" %}
{% bootstrap_field form.nodes layout="horizontal" %}
{% bootstrap_field form.system_users layout="horizontal" %}
<div class="hr-line-dashed"></div>
<h3>{% trans 'Other' %}</h3>
<div class="form-group">
......@@ -53,17 +49,19 @@
{{ form.is_active }}
</div>
</div>
<div class="form-group {% if form.date_expired.errors %} has-error {% endif %}" id="date_5">
<div class="form-group {% if form.date_expired.errors or form.date_start.errors %} has-error {% endif %}" id="date_5">
<label for="{{ form.date_expired.id_for_label }}" class="col-sm-2 control-label">{{ form.date_expired.label }}</label>
<div class="col-sm-9">
<div class="input-group date">
<div class="input-daterange input-group" id="datepicker">
<span class="input-group-addon"><i class="fa fa-calendar"></i></span>
<input id="{{ form.date_expired.id_for_label }}" name="{{ form.date_expired.html_name }}" type="text" class="form-control" value="{{ form.date_expired.value|date:'Y-m-d'|default:form.date_expired.value }}">
<input type="text" class="input-sm form-control" name="date_start" value="{{ form.date_start.value|date:'Y-m-d' }}">
<span class="input-group-addon">to</span>
<input type="text" class="input-sm form-control" name="date_expired" value="{{ form.date_expired.value|date:'Y-m-d' }}">
</div>
<span class="help-block ">{{ form.date_expired.errors }}</span>
<span class="help-block ">{{ form.date_start.errors }}</span>
</div>
</div>
</div>
{% bootstrap_field form.comment layout="horizontal" %}
<div class="form-group">
......@@ -84,15 +82,14 @@
<script>
$(document).ready(function () {
$('.select2').select2();
$('.input-group.date').datepicker({
$('#datepicker').datepicker({
format: "yyyy-mm-dd",
todayBtn: "linked",
keyboardNavigation: false,
forceParse: false,
calendarWeeks: true,
autoclose: true
})
});
})
</script>
{% endblock %}
\ No newline at end of file
......@@ -15,19 +15,19 @@
<div class="panel-options">
<ul class="nav nav-tabs">
<li class="active">
<a href="{% url 'perms:asset-permission-detail' pk=asset_permission.id %}" class="text-center"><i class="fa fa-laptop"></i> {% trans 'Detail' %} </a>
<a href="{% url 'perms:asset-permission-detail' pk=object.id %}" class="text-center"><i class="fa fa-laptop"></i> {% trans 'Detail' %} </a>
</li>
<li>
<a href="{% url 'perms:asset-permission-user-list' pk=asset_permission.id %}" class="text-center">
<a href="{% url 'perms:asset-permission-user-list' pk=object.id %}" class="text-center">
<i class="fa fa-bar-chart-o"></i> {% trans 'Users and user groups' %}
</a>
</li>
<li>
<a href="{% url 'perms:asset-permission-asset-list' pk=asset_permission.id %}" class="text-center">
<a href="{% url 'perms:asset-permission-asset-list' pk=object.id %}" class="text-center">
<i class="fa fa-bar-chart-o"></i> {% trans 'Assets and asset groups' %}</a>
</li>
<li class="pull-right">
<a class="btn btn-outline btn-default" href="{% url 'perms:asset-permission-update' pk=asset_permission.id %}"><i class="fa fa-edit"></i>{% trans 'Update' %}</a>
<a class="btn btn-outline btn-default" href="{% url 'perms:asset-permission-update' pk=object.id %}"><i class="fa fa-edit"></i>{% trans 'Update' %}</a>
</li>
<li class="pull-right">
<a class="btn btn-outline btn-danger btn-delete-perm">
......@@ -40,7 +40,7 @@
<div class="col-sm-7" style="padding-left: 0;">
<div class="ibox float-e-margins">
<div class="ibox-title">
<span class="label"><b>{{ asset_permission.name }}</b></span>
<span class="label"><b>{{ object.name }}</b></span>
<div class="ibox-tools">
<a class="collapse-link">
<i class="fa fa-chevron-up"></i>
......@@ -60,43 +60,47 @@
<tbody>
<tr class="no-borders-tr">
<td>{% trans 'Name' %}:</td>
<td><b>{{ asset_permission.name }}</b></td>
<td><b>{{ object.name }}</b></td>
</tr>
<tr>
<td>{% trans 'User count' %}:</td>
<td><b>{{ asset_permission.users.count }}</b></td>
<td><b>{{ object.users.count }}</b></td>
</tr>
<tr>
<td>{% trans 'User group count' %}:</td>
<td><b>{{ asset_permission.users.count }}</b></td>
<td><b>{{ object.users.count }}</b></td>
</tr>
<tr>
<td>{% trans 'Asset count' %}:</td>
<td><b>{{ asset_permission.assets.count }}</b></td>
<td><b>{{ object.assets.count }}</b></td>
</tr>
<tr>
<td>{% trans 'Asset group count' %}:</td>
<td><b>{{ asset_permission.asset_groups.count }}</b></td>
<td>{% trans 'Node count' %}:</td>
<td><b>{{ object.nodes.count }}</b></td>
</tr>
<tr>
<td>{% trans 'System user count' %}:</td>
<td><b>{{ asset_permission.system_users.count }}</b></td>
<td><b>{{ object.system_users.count }}</b></td>
</tr>
<tr>
<td>{% trans 'Date start' %}:</td>
<td><b>{{ object.date_start }}</b></td>
</tr>
<tr>
<td>{% trans 'Date expired' %}:</td>
<td><b>{{ asset_permission.date_expired }}</b></td>
<td><b>{{ object.date_expired }}</b></td>
</tr>
<tr>
<td>{% trans 'Date created' %}:</td>
<td><b>{{ asset_permission.date_created }}</b></td>
<td><b>{{ object.date_created }}</b></td>
</tr>
<tr>
<td>{% trans 'Created by' %}:</td>
<td><b>{{ asset_permission.created_by }}</b></td>
<td><b>{{ object.created_by }}</b></td>
</tr>
<tr>
<td>{% trans 'Comment' %}:</td>
<td><b>{{ asset_permission.comment }}</b></td>
<td><b>{{ object.comment }}</b></td>
</tr>
</tbody>
</table>
......@@ -117,7 +121,7 @@
<td><span style="float: right">
<div class="switch">
<div class="onoffswitch">
<input type="checkbox" {% if asset_permission.is_active %} checked {% endif %} class="onoffswitch-checkbox" id="is_active">
<input type="checkbox" {% if object.is_active %} checked {% endif %} class="onoffswitch-checkbox" id="is_active">
<label class="onoffswitch-label" for="is_active">
<span class="onoffswitch-inner"></span>
<span class="onoffswitch-switch"></span>
......@@ -155,7 +159,7 @@
</tr>
</form>
{% for system_user in system_users %}
{% for system_user in object.system_users.all %}
<tr {% if forloop.counter == 1 %} class="no-borders-tr" {% endif %} >
<td ><b class="bdg-system-user" data-uid={{ system_user.id }}>{{ system_user.name }}</b></td>
<td>
......@@ -179,7 +183,7 @@
jumpserver.system_users_selected = {};
function updateSystemUser(system_users) {
var the_url = "{% url 'api-perms:asset-permission-detail' pk=asset_permission.id %}";
var the_url = "{% url 'api-perms:asset-permission-detail' pk=object.id %}";
var body = {
system_users: Object.assign([], system_users)
};
......@@ -203,7 +207,7 @@ $(document).ready(function () {
.on('click', '.btn-delete-perm', function () {
var $this = $(this);
var name = "{{ asset_permission.name }}";
var uid = "{{ asset_permission.id }}";
var uid = "{{ object.id }}";
var the_url = '{% url "api-perms:asset-permission-detail" pk=DEFAULT_PK %}'.replace('{{ DEFAULT_PK }}', uid);
var redirect_url = "{% url 'perms:asset-permission-list' %}";
objectDelete($this, name, the_url, redirect_url);
......@@ -238,7 +242,7 @@ $(document).ready(function () {
updateSystemUser(system_users);
$tr.remove()
}).on('click', '#is_active', function () {
var the_url = '{% url "api-perms:asset-permission-detail" pk=asset_permission.id %}';
var the_url = '{% url "api-perms:asset-permission-detail" pk=object.id %}';
var checked = $(this).prop('checked');
var body = {
'is_active': checked
......
......@@ -3,41 +3,29 @@
{% load i18n %}
{% block custom_head_css_js %}
<link href="{% static "css/plugins/footable/footable.core.css" %}" rel="stylesheet">
<link href="{% static 'css/plugins/datepicker/datepicker3.css' %}" rel="stylesheet">
<link href="{% static 'css/plugins/select2/select2.min.css' %}" rel="stylesheet">
<script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script>
<link href="{% static 'css/plugins/jstree/style.min.css' %}" rel="stylesheet">
<link href="{% static 'css/plugins/ztree/awesomeStyle/awesome.css' %}" rel="stylesheet">
<script type="text/javascript" src="{% static 'js/plugins/ztree/jquery.ztree.all.min.js' %}"></script>
<style type="text/css">
div#rMenu {
position:absolute;
visibility:hidden;
text-align: left;
top: 100%;
left: 0;
z-index: 1000;
float: left;
padding: 5px 0;
margin: 2px 0 0;
list-style: none;
background-clip: padding-box;
<style>
.toggle {
cursor: pointer;
}
div#rMenu li{
margin: 1px 0;
cursor: pointer;
{#list-style: none outside none;#}
.detail-key {
width: 70px;
}
.dropdown a:hover {
background-color: #f1f1f1
}
</style>
</style>
{% endblock %}
{% block content %}
<div class="wrapper wrapper-content">
<div class="row">
<div class="col-lg-3" id="split-left">
<div class="col-lg-3" id="split-left" style="padding-left: 3px">
<div class="ibox float-e-margins">
<div class="ibox-content mailbox-content" style="padding-top: 0">
<div class="ibox-content mailbox-content" style="padding-top: 0;padding-left: 1px">
<div class="file-manager ">
<div id="assetTree" class="ztree">
</div>
......@@ -61,16 +49,16 @@
<table class="table table-striped table-bordered table-hover" id="permission_list_table" style="width: 100%">
<thead>
<tr>
<th class="text-center">
<input type="checkbox" id="check_all" class="ipt_check_all" >
</th>
<th class="text-center">{% trans 'Node' %}</th>
<th class="text-center">{% trans 'User group' %}</th>
<th class="text-center">{% trans 'System user' %}</th>
<th class="text-center">{% trans 'Is active' %}</th>
<th class="text-center">{% trans 'Date expired' %}</th>
<th class="text-center">{% trans 'Action' %}</th>
</tr>
<th></th>
<th>{% trans 'Name' %}</th>
<th class="text-center">{% trans 'User' %}</th>
<th class="text-center">{% trans 'User group' %}</th>
<th class="text-center">{% trans 'Asset' %}</th>
<th class="text-center">{% trans 'Node'%}</th>
<th class="text-center">{% trans 'System user' %}</th>
<th class="text-center">{% trans 'Active' %}</th>
<th class="text-center" >{% trans 'Action' %}</th>
</tr>
</thead>
<tbody>
</tbody>
......@@ -82,54 +70,142 @@
{% endblock %}
{% block custom_foot_js %}
<script src="{% static "js/plugins/footable/footable.all.min.js" %}"></script>
<script src="{% static 'js/plugins/datepicker/bootstrap-datepicker.js' %}"></script>
<script>
var zTree, rMenu, table, show = 0;
var zTree, table, show = 0;
function onSelected(event, treeNode) {
setCookie('node_selected', treeNode.id);
var url = table.ajax.url();
if (treeNode.is_asset) {
url = setUrlParam(url, 'node', "");
url = setUrlParam(url, 'asset', treeNode.id)
} else {
url = setUrlParam(url, 'asset', "");
url = setUrlParam(url, 'node', treeNode.id)
}
setCookie('node_selected', treeNode.id);
table.ajax.url(url);
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]);
node.open = true;
}
}
function filter(treeId, parentNode, childNodes) {
$.each(childNodes, function (index, value) {
value["pId"] = value["parent"];
value["name"] = value["value"];
value["isParent"] = value["assets_amount"] !== 0;
value["iconSkin"] = value["is_asset"] ? "file" : null;
});
return childNodes;
}
function beforeAsync(treeId, treeNode) {
return true;
}
function makeLabel(data) {
return "<label class='detail-key'><b>" + data[0] + ": </b></label>" + data[1] + "</br>"
}
function format(d) {
var data = "";
if (d.users.length > 0 ) {
data += makeLabel(["{% trans 'User' %}", d.users.join(", ")])
}
if (d.user_groups.length > 0) {
data += makeLabel(["{% trans 'User group' %}", d.user_groups.join(", ")])
}
if (d.assets.length > 0) {
data += makeLabel(["{% trans 'Asset' %}", d.assets.join(", ")])
}
if (d.nodes.length > 0) {
data += makeLabel(["{% trans 'Node' %}", d.nodes.join(", ")])
}
if (d.system_users.length > 0) {
data += makeLabel(["{% trans 'System user' %}", d.system_users.join(", ")])
}
return data
}
function initTable() {
var options = {
ele: $('#permission_list_table'),
toggle: true,
columnDefs: [
{targets: 1, createdCell: function (td, cellData) {
var html = '<a href="{% url 'assets:asset-list' %}?node=99899">' + cellData.name + '</a>';
$(td).html(html.replace("99899", cellData.pk));
{targets: 0, createdCell: function (td, cellData, rowData) {
$(td).addClass("toggle");
$(td).html("<i class='fa fa-angle-right'></i>");
}},
{targets: 1, createdCell: function (td, cellData, rowData) {
var detail_btn = '<a href="{% url "perms:asset-permission-detail" pk=DEFAULT_PK %}">' + cellData + '</a>';
$(td).html(detail_btn.replace('{{ DEFAULT_PK }}', rowData.id));
}},
{targets: 2, createdCell: function (td, cellData) {
var html = '<a href="{% url "users:user-group-detail" pk=DEFAULT_PK %}">' + cellData.name + '</a>';
$(td).html(html.replace("{{ DEFAULT_PK }}", cellData.pk))
var num = cellData.length;
$(td).html(num);
}},
{targets: 3, createdCell: function (td, cellData) {
var html = '<a href="{% url 'assets:system-user-detail' pk=DEFAULT_PK %}">' + cellData.name + '</a>';
$(td).html(html.replace("{{ DEFAULT_PK }}", cellData.pk));
var num = cellData.length;
$(td).html(num);
}},
{targets: 4, createdCell: function (td, cellData) {
var num = cellData.length;
$(td).html(num);
}},
{targets: 5, createdCell: function (td, cellData) {
var num = cellData.length;
$(td).html(num);
}},
{targets: 6, createdCell: function (td, cellData) {
var num = cellData.length;
$(td).html(num);
}},
{targets: 7, 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: 5, createdCell: function (td, cellData) {
var date_expired = cellData.split(" ");
if (date_expired && date_expired.length === 3) {
$(td).html(date_expired[0]);
} else {
$(td).html(cellData);
}
}},
{targets: 6, createdCell: function (td, cellData, rowData) {
var name = rowData.user_group.name + "=>" + rowData.system_user.name + "=>" + rowData.node.name;
{targets: 8, createdCell: function (td, cellData, rowData) {
var update_btn = '<a href="{% url "perms:asset-permission-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-del" data-uid="{{ DEFAULT_PK }}" data-name="99991938">{% trans "Delete" %}</a>'
var del_btn = '<a class="btn btn-xs btn-danger m-l-xs btn-del" data-uid="{{ DEFAULT_PK }}" mark=1 data-name="99991938">{% trans "Delete" %}</a>'
.replace('{{ DEFAULT_PK }}', cellData)
.replace('99991938', name);
.replace('99991938', rowData.name);
if (rowData.inherit) {
del_btn = del_btn.replace("mark", "disabled")
}
$(td).html(update_btn + del_btn);
}}
],
ajax_url: '{% url "api-perms:asset-permission-list" %}',
columns: [
{data: "id"}, {data: "node"}, {data: "user_group" }, {data: "system_user"},
{data: "is_active"}, {data: "date_expired"}, {data: "id"}
{data: "id"}, {data: "name"}, {data: "users"},
{data: "user_groups"}, {data: "assets"},
{data: "nodes"}, {data: "system_users"},
{data: "is_active", orderable: false}, {data: "id", orderable: false}
],
select: {},
op_html: $('#actions').html()
};
table = jumpserver.initDataTable(options);
......@@ -137,32 +213,6 @@ function initTable() {
}
function onSelected(event, treeNode) {
var url = table.ajax.url();
url = setUrlParam(url, "node_id", treeNode.id);
setCookie('node_selected', treeNode.id);
table.ajax.url(url);
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: {
......@@ -174,25 +224,32 @@ function initTree() {
enable: true
}
},
async: {
enable: true,
url: "{% url 'api-assets:node-children-2' %}?assets=1",
autoParam:["id", "name=n", "level=lv"],
dataFilter: filter,
type: 'get'
},
callback: {
onSelected: onSelected
onSelected: onSelected,
beforeAsync: beforeAsync
}
};
var zNodes = [];
$.get("{% url 'api-assets:node-list' %}", function(data, status){
$.get("{% url 'api-assets:node-children-2' %}", function(data, status){
$.each(data, function (index, value) {
value["pId"] = value["parent"];
{#if (value["key"] === "0") {#}
value["open"] = true;
{# }#}
value["name"] = value["value"]
value["isParent"] = value["assets_amount"] !== 0;
value["name"] = value["value"];
value["open"] = value["key"] === "0";
});
zNodes = data;
{#$.fn.zTree.init($("#assetTree"), setting);#}
$.fn.zTree.init($("#assetTree"), setting, zNodes);
zTree = $.fn.zTree.getZTreeObj("assetTree");
rMenu = $("#rMenu");
selectQueryNode();
selectQueryNode();
});
}
......@@ -226,12 +283,41 @@ $(document).ready(function(){
.on('click', '.btn-create-permission', function () {
var url = "{% url 'perms:asset-permission-create' %}";
var nodes = zTree.getSelectedNodes();
var current_node;
if (nodes && nodes.length ===1 ){
current_node = nodes[0];
url += "?node_id=" + current_node.id;
}
var _nodes = [];
var _assets = [];
$.each(nodes, function (id, node) {
if (node.is_asset) {
_assets.push(node.id)
} else {
_nodes.push(node.id)
}
});
url += "?assets=" + _assets.join(",") + "&nodes=" + _nodes.join(",");
window.open(url, '_self');
}).on('click', '.toggle', function (e) {
e.preventDefault();
var detailRows = [];
var tr = $(this).closest('tr');
var row = table.row(tr);
var idx = $.inArray(tr.attr('id'), detailRows);
if (row.child.isShown()) {
tr.removeClass('details');
row.child.hide();
// Remove from the 'open' array
detailRows.splice(idx, 1);
}
else {
tr.addClass('details');
$('.toggle i').removeClass('fa-angle-right').addClass('fa-angle-down');
row.child(format(row.data())).show();
// Add to the 'open' array
if ( idx === -1 ) {
detailRows.push(tr.attr('id'));
}
}
})
</script>
......
......@@ -57,12 +57,12 @@
</tr>
</thead>
<tbody>
{% for user in page_obj %}
{% for user in object_list %}
<tr>
<td>{{ user.name }}</td>
<td>{{ user.username }}</td>
<td>
<button class="btn btn-danger btn-xs btn-remove-user {% if user.is_inherit_from_user_groups %} disabled {% endif %}" data-gid="{{ user.id }}" type="button" style="float: right;"><i class="fa fa-minus"></i></button>
<button class="btn btn-danger btn-xs btn-remove-user {% if user.inherit %} disabled {% endif %}" data-gid="{{ user.id }}" type="button" style="float: right;"><i class="fa fa-minus"></i></button>
</td>
</tr>
{% endfor %}
......@@ -127,7 +127,7 @@
</tr>
</form>
{% for user_group in user_groups %}
{% for user_group in asset_permission.user_groups.all %}
<tr>
<td ><b class="bdg_group" data-gid={{ user_group.id }}>{{ user_group.name }}</b></td>
<td>
......
......@@ -11,17 +11,50 @@ router.register('v1/asset-permissions', api.AssetPermissionViewSet, 'asset-permi
urlpatterns = [
# 查询某个用户授权的资产和资产组
url(r'^v1/user/(?P<pk>[0-9a-zA-Z\-]{36})/assets/$', api.UserGrantedAssetsApi.as_view(), name='user-assets'),
url(r'^v1/user/assets/$', api.UserGrantedAssetsApi.as_view(), name='my-assets'),
url(r'^v1/user/(?P<pk>[0-9a-zA-Z\-]{36})/nodes/$', api.UserGrantedNodesApi.as_view(), name='user-nodes'),
url(r'^v1/user/nodes/$', api.UserGrantedNodesApi.as_view(), name='my-nodes'),
url(r'^v1/user/(?P<pk>[0-9a-zA-Z\-]{36})/nodes-assets/$', api.UserGrantedNodesWithAssetsApi.as_view(), name='user-nodes-assets'),
url(r'^v1/user/nodes-assets/$', api.UserGrantedNodesWithAssetsApi.as_view(), name='my-nodes-assets'),
url(r'^v1/user/(?P<pk>[0-9a-zA-Z\-]{36})/assets/$',
api.UserGrantedAssetsApi.as_view(), name='user-assets'),
url(r'^v1/user/assets/$', api.UserGrantedAssetsApi.as_view(),
name='my-assets'),
url(r'^v1/user/(?P<pk>[0-9a-zA-Z\-]{36})/nodes/$',
api.UserGrantedNodesApi.as_view(), name='user-nodes'),
url(r'^v1/user/nodes/$', api.UserGrantedNodesApi.as_view(),
name='my-nodes'),
url(
r'^v1/user/(?P<pk>[0-9a-zA-Z\-]{36})/nodes/(?P<node_id>[0-9a-zA-Z\-]{36})/assets/$',
api.UserGrantedNodeAssetsApi.as_view(), name='user-node-assets'),
url(r'^v1/user/nodes/(?P<node_id>[0-9a-zA-Z\-]{36})/assets/$',
api.UserGrantedNodeAssetsApi.as_view(), name='my-node-assets'),
url(r'^v1/user/(?P<pk>[0-9a-zA-Z\-]{36})/nodes-assets/$',
api.UserGrantedNodesWithAssetsApi.as_view(), name='user-nodes-assets'),
url(r'^v1/user/nodes-assets/$', api.UserGrantedNodesWithAssetsApi.as_view(),
name='my-nodes-assets'),
# 查询某个用户组授权的资产和资产组
url(r'^v1/user-group/(?P<pk>[0-9a-zA-Z\-]{36})/assets/$', api.UserGroupGrantedAssetsApi.as_view(), name='user-group-assets'),
url(r'^v1/user-group/(?P<pk>[0-9a-zA-Z\-]{36})/nodes/$', api.UserGroupGrantedNodesApi.as_view(), name='user-group-nodes'),
url(r'^v1/user-group/(?P<pk>[0-9a-zA-Z\-]{36})/nodes-assets/$', api.UserGroupGrantedNodesWithAssetsApi.as_view(), name='user-group-nodes-assets'),
url(r'^v1/user-group/(?P<pk>[0-9a-zA-Z\-]{36})/assets/$',
api.UserGroupGrantedAssetsApi.as_view(), name='user-group-assets'),
url(r'^v1/user-group/(?P<pk>[0-9a-zA-Z\-]{36})/nodes/$',
api.UserGroupGrantedNodesApi.as_view(), name='user-group-nodes'),
url(r'^v1/user-group/(?P<pk>[0-9a-zA-Z\-]{36})/nodes-assets/$',
api.UserGroupGrantedNodesWithAssetsApi.as_view(),
name='user-group-nodes-assets'),
url(
r'^v1/user-group/(?P<pk>[0-9a-zA-Z\-]{36})/nodes/(?P<node_id>[0-9a-zA-Z\-]{36})/assets/$',
api.UserGroupGrantedNodeAssetsApi.as_view(),
name='user-group-node-assets'),
# 用户和资产授权变更
url(r'^v1/asset-permissions/(?P<pk>[0-9a-zA-Z\-]{36})/user/remove/$',
api.AssetPermissionRemoveUserApi.as_view(),
name='asset-permission-remove-user'),
url(r'^v1/asset-permissions/(?P<pk>[0-9a-zA-Z\-]{36})/user/add/$',
api.AssetPermissionAddUserApi.as_view(),
name='asset-permission-add-user'),
url(r'^v1/asset-permissions/(?P<pk>[0-9a-zA-Z\-]{36})/asset/remove/$',
api.AssetPermissionRemoveAssetApi.as_view(),
name='asset-permission-remove-asset'),
url(r'^v1/asset-permissions/(?P<pk>[0-9a-zA-Z\-]{36})/asset/add/$',
api.AssetPermissionAddAssetApi.as_view(),
name='asset-permission-add-asset'),
# 验证用户是否有某个资产和系统用户的权限
url(r'v1/asset-permission/user/validate/$', api.ValidateUserAssetPermissionView.as_view(), name='validate-user-asset-permission'),
......
......@@ -9,10 +9,10 @@ urlpatterns = [
url(r'^asset-permission$', views.AssetPermissionListView.as_view(), name='asset-permission-list'),
url(r'^asset-permission/create$', views.AssetPermissionCreateView.as_view(), name='asset-permission-create'),
url(r'^asset-permission/(?P<pk>[0-9a-zA-Z\-]{36})/update$', views.AssetPermissionUpdateView.as_view(), name='asset-permission-update'),
# url(r'^asset-permission/(?P<pk>[0-9a-zA-Z\-]{36})$', views.AssetPermissionDetailView.as_view(),name='asset-permission-detail'),
url(r'^asset-permission/(?P<pk>[0-9a-zA-Z\-]{36})$', views.AssetPermissionDetailView.as_view(),name='asset-permission-detail'),
url(r'^asset-permission/(?P<pk>[0-9a-zA-Z\-]{36})/delete$', views.AssetPermissionDeleteView.as_view(), name='asset-permission-delete'),
# url(r'^asset-permission/(?P<pk>[0-9a-zA-Z\-]{36})/user$', views.AssetPermissionUserView.as_view(), name='asset-permission-user-list'),
# url(r'^asset-permission/(?P<pk>[0-9a-zA-Z\-]{36})/asset$', views.AssetPermissionAssetView.as_view(), name='asset-permission-asset-list'),
url(r'^asset-permission/(?P<pk>[0-9a-zA-Z\-]{36})/user$', views.AssetPermissionUserView.as_view(), name='asset-permission-user-list'),
url(r'^asset-permission/(?P<pk>[0-9a-zA-Z\-]{36})/asset$', views.AssetPermissionAssetView.as_view(), name='asset-permission-asset-list'),
]
......@@ -2,17 +2,204 @@
from __future__ import absolute_import, unicode_literals
import collections
from collections import defaultdict
from django.utils import timezone
from django.utils.translation import ugettext as _
import copy
from common.utils import setattr_bulk, get_logger
from .models import NodePermission
from common.utils import set_or_append_attr_bulk, get_logger
from .models import AssetPermission
logger = get_logger(__file__)
class AssetPermissionUtil:
@staticmethod
def get_user_permissions(user):
return AssetPermission.valid.all().filter(users=user)
@staticmethod
def get_user_group_permissions(user_group):
return AssetPermission.valid.all().filter(user_groups=user_group)
@staticmethod
def get_asset_permissions(asset):
return AssetPermission.valid.all().filter(assets=asset)
@staticmethod
def get_node_permissions(node):
return AssetPermission.valid.all().filter(nodes=node)
@staticmethod
def get_system_user_permissions(system_user):
return AssetPermission.objects.all().filter(system_users=system_user)
@classmethod
def get_user_group_nodes(cls, group):
nodes = defaultdict(set)
permissions = cls.get_user_group_permissions(group)
for perm in permissions:
_nodes = perm.nodes.all()
_system_users = perm.system_users.all()
set_or_append_attr_bulk(_nodes, 'permission', perm.id)
for node in _nodes:
nodes[node].update(set(_system_users))
return nodes
@classmethod
def get_user_group_assets_direct(cls, group):
assets = defaultdict(set)
permissions = cls.get_user_group_permissions(group)
for perm in permissions:
_assets = perm.assets.all()
_system_users = perm.system_users.all()
set_or_append_attr_bulk(_assets, 'permission', perm.id)
for asset in _assets:
assets[asset].update(set(_system_users))
return assets
@classmethod
def get_user_group_nodes_assets(cls, group):
assets = defaultdict(set)
nodes = cls.get_user_group_nodes(group)
for node, _system_users in nodes.items():
_assets = node.get_all_assets()
set_or_append_attr_bulk(_assets, 'inherit_node', node.id)
set_or_append_attr_bulk(_assets, 'permission', getattr(node, 'permission', None))
for asset in _assets:
assets[asset].update(set(_system_users))
return assets
@classmethod
def get_user_group_assets(cls, group):
assets = defaultdict(set)
_assets = cls.get_user_group_assets_direct(group)
_nodes_assets = cls.get_user_group_nodes_assets(group)
for asset, _system_users in _assets.items():
assets[asset].update(set(_system_users))
for asset, _system_users in _nodes_assets.items():
assets[asset].update(set(_system_users))
return assets
@classmethod
def get_user_group_nodes_with_assets(cls, user):
"""
:param user:
:return: {node: {asset: set(su1, su2)}}
"""
nodes = defaultdict(dict)
_assets = cls.get_user_group_assets(user)
for asset, _system_users in _assets.items():
_nodes = asset.get_nodes()
for node in _nodes:
if asset in nodes[node]:
nodes[node][asset].update(_system_users)
else:
nodes[node][asset] = _system_users
return nodes
@classmethod
def get_user_assets_direct(cls, user):
assets = defaultdict(set)
permissions = list(cls.get_user_permissions(user))
for perm in permissions:
_assets = perm.assets.all()
_system_users = perm.system_users.all()
set_or_append_attr_bulk(_assets, 'permission', perm.id)
for asset in _assets:
assets[asset].update(set(_system_users))
return assets
@classmethod
def get_user_nodes_direct(cls, user):
nodes = defaultdict(set)
permissions = cls.get_user_permissions(user)
for perm in permissions:
_nodes = perm.nodes.all()
_system_users = perm.system_users.all()
set_or_append_attr_bulk(_nodes, 'permission', perm.id)
for node in _nodes:
nodes[node].update(set(_system_users))
return nodes
@classmethod
def get_user_nodes_assets_direct(cls, user):
assets = defaultdict(set)
nodes = cls.get_user_nodes_direct(user)
for node, _system_users in nodes.items():
_assets = node.get_all_assets()
set_or_append_attr_bulk(_assets, 'inherit_node', node.id)
set_or_append_attr_bulk(_assets, 'permission', getattr(node, 'permission', None))
for asset in _assets:
assets[asset].update(set(_system_users))
return assets
@classmethod
def get_user_assets_inherit_group(cls, user):
assets = defaultdict(set)
for group in user.groups.all():
_assets = cls.get_user_group_assets(group)
set_or_append_attr_bulk(_assets, 'inherit_group', group.id)
for asset, _system_users in _assets.items():
assets[asset].update(_system_users)
return assets
@classmethod
def get_user_assets(cls, user):
assets = defaultdict(set)
_assets_direct = cls.get_user_assets_direct(user)
_nodes_assets_direct = cls.get_user_nodes_assets_direct(user)
_assets_inherit_group = cls.get_user_assets_inherit_group(user)
for asset, _system_users in _assets_direct.items():
assets[asset].update(_system_users)
for asset, _system_users in _nodes_assets_direct.items():
assets[asset].update(_system_users)
for asset, _system_users in _assets_inherit_group.items():
assets[asset].update(_system_users)
return assets
@classmethod
def get_user_nodes_with_assets(cls, user):
"""
:param user:
:return: {node: {asset: set(su1, su2)}}
"""
nodes = defaultdict(dict)
_assets = cls.get_user_assets(user)
for asset, _system_users in _assets.items():
_nodes = asset.get_nodes()
for node in _nodes:
if asset in nodes[node]:
nodes[node][asset].update(_system_users)
else:
nodes[node][asset] = _system_users
return nodes
@classmethod
def get_system_user_assets(cls, system_user):
assets = set()
permissions = cls.get_system_user_permissions(system_user)
for perm in permissions:
assets.update(set(perm.assets.all()))
nodes = perm.nodes.all()
for node in nodes:
assets.update(set(node.get_all_assets()))
return assets
@classmethod
def get_node_system_users(cls, node):
system_users = set()
permissions = cls.get_node_permissions(node)
for perm in permissions:
system_users.update(perm.system_users.all())
return system_users
# Abandon
class NodePermissionUtil:
"""
"""
@staticmethod
def get_user_group_permissions(user_group):
......
......@@ -3,20 +3,22 @@
from __future__ import unicode_literals, absolute_import
from django.utils.translation import ugettext as _
from django.views.generic import ListView, CreateView, UpdateView
from django.views.generic.edit import DeleteView
from django.views.generic import ListView, CreateView, UpdateView, DetailView
from django.views.generic.edit import DeleteView, SingleObjectMixin
from django.urls import reverse_lazy
from django.conf import settings
from common.utils import get_object_or_none
from .hands import AdminUserRequiredMixin, Node
from .models import AssetPermission, NodePermission
from common.mixins import AdminUserRequiredMixin
from .hands import Node, Asset, SystemUser, User, UserGroup
from .models import AssetPermission
from .forms import AssetPermissionForm
class AssetPermissionListView(AdminUserRequiredMixin, ListView):
model = NodePermission
context_object_name = 'asset_permission_list'
model = AssetPermission
template_name = 'perms/asset_permission_list.html'
paginate_by = settings.DISPLAY_PER_PAGE
user = user_group = asset = node = system_user = q = ""
def get_context_data(self, **kwargs):
context = {
......@@ -28,18 +30,24 @@ class AssetPermissionListView(AdminUserRequiredMixin, ListView):
class AssetPermissionCreateView(AdminUserRequiredMixin, CreateView):
model = NodePermission
model = AssetPermission
form_class = AssetPermissionForm
template_name = 'perms/asset_permission_create_update.html'
success_url = reverse_lazy('perms:asset-permission-list')
def get_form(self, form_class=None):
form = super().get_form(form_class=form_class)
node_id = self.request.GET.get("node_id")
node = get_object_or_none(Node, id=node_id)
if not node:
node = Node.root()
form['node'].initial = node
nodes_id = self.request.GET.get("nodes")
assets_id = self.request.GET.get("assets")
if nodes_id:
nodes_id = nodes_id.split(",")
nodes = Node.objects.filter(id__in=nodes_id)
form['nodes'].initial = nodes
if assets_id:
assets_id = assets_id.split(",")
assets = Asset.objects.filter(id__in=assets_id)
form['assets'].initial = assets
return form
def get_context_data(self, **kwargs):
......@@ -52,16 +60,11 @@ class AssetPermissionCreateView(AdminUserRequiredMixin, CreateView):
class AssetPermissionUpdateView(AdminUserRequiredMixin, UpdateView):
model = NodePermission
model = AssetPermission
form_class = AssetPermissionForm
template_name = 'perms/asset_permission_create_update.html'
success_url = reverse_lazy("perms:asset-permission-list")
def get_form(self, form_class=None):
form = super().get_form(form_class=form_class)
form['node'].initial = form.instance.node
return form
def get_context_data(self, **kwargs):
context = {
'app': _('Perms'),
......@@ -71,9 +74,84 @@ class AssetPermissionUpdateView(AdminUserRequiredMixin, UpdateView):
return super().get_context_data(**kwargs)
class AssetPermissionDetailView(AdminUserRequiredMixin, DetailView):
model = AssetPermission
form_class = AssetPermissionForm
template_name = 'perms/asset_permission_detail.html'
success_url = reverse_lazy("perms:asset-permission-list")
def get_context_data(self, **kwargs):
context = {
'app': _('Perms'),
'action': _('Update asset permission'),
'system_users_remain': SystemUser.objects.exclude(
granted_by_permissions=self.object
),
}
kwargs.update(context)
return super().get_context_data(**kwargs)
class AssetPermissionDeleteView(AdminUserRequiredMixin, DeleteView):
model = AssetPermission
template_name = 'delete_confirm.html'
success_url = reverse_lazy('perms:asset-permission-list')
class AssetPermissionUserView(AdminUserRequiredMixin,
SingleObjectMixin,
ListView):
template_name = 'perms/asset_permission_user.html'
context_object_name = 'asset_permission'
paginate_by = settings.CONFIG.DISPLAY_PER_PAGE
object = None
def get(self, request, *args, **kwargs):
self.object = self.get_object(queryset=AssetPermission.objects.all())
return super().get(request, *args, **kwargs)
def get_queryset(self):
queryset = self.object.get_all_users()
return queryset
def get_context_data(self, **kwargs):
context = {
'app': _('Perms'),
'action': _('Asset permission user list'),
'users_remain': User.objects.exclude(asset_permissions=self.object)
.exclude(role=User.ROLE_APP),
'user_groups_remain': UserGroup.objects.exclude(
asset_permissions=self.object
)
}
kwargs.update(context)
return super().get_context_data(**kwargs)
class AssetPermissionAssetView(AdminUserRequiredMixin,
SingleObjectMixin,
ListView):
template_name = 'perms/asset_permission_asset.html'
context_object_name = 'asset_permission'
paginate_by = settings.CONFIG.DISPLAY_PER_PAGE
object = None
def get(self, request, *args, **kwargs):
self.object = self.get_object(queryset=AssetPermission.objects.all())
return super().get(request, *args, **kwargs)
def get_queryset(self):
queryset = self.object.get_all_assets()
return queryset
def get_context_data(self, **kwargs):
assets_granted = self.get_queryset()
context = {
'app': _('Perms'),
'action': _('Asset permission asset list'),
'assets_remain': Asset.objects.exclude(id__in=[a.id for a in assets_granted]),
'nodes_remain': Node.objects.exclude(granted_by_permissions=self.object),
}
kwargs.update(context)
return super().get_context_data(**kwargs)
\ No newline at end of file
......@@ -212,49 +212,49 @@ website: http://code.google.com/p/jquerytree/
height: 20px;
}
.ztree li span.button.root_open::before {
content: "\f078";
content: "\f107";
padding-top: 10px;
padding-left: 2px;
display: inline-block;
}
.ztree li span.button.root_close::before {
content: "\f054";
content: "\f105";
padding-top: 10px;
padding-left: 2px;
display: inline-block;
}
.ztree li span.button.roots_open::before {
content: "\f078";
content: "\f107";
padding-top: 10px;
padding-left: 2px;
display: inline-block;
}
.ztree li span.button.roots_close::before {
content: "\f054";
content: "\f105";
padding-top: 10px;
padding-left: 2px;
display: inline-block;
}
.ztree li span.button.center_open::before {
content: "\f078";
content: "\f107";
padding-top: 10px;
padding-left: 2px;
display: inline-block;
}
.ztree li span.button.center_close::before {
content: "\f054";
content: "\f105";
padding-top: 10px;
padding-left: 2px;
display: inline-block;
}
.ztree li span.button.bottom_open::before {
content: "\f078";
content: "\f107";
padding-top: 10px;
padding-left: 2px;
display: inline-block;
}
.ztree li span.button.bottom_close::before {
content: "\f054";
content: "\f105";
padding-top: 10px;
padding-left: 2px;
display: inline-block;
......@@ -300,7 +300,31 @@ website: http://code.google.com/p/jquerytree/
color: #676a6c;
}
.ztree li span.button.ico_docu::before {
content: "\f114";
content: "\f07b";
font-family: FontAwesome;
padding-top: 10px;
padding-left: 2px;
display: inline-block;
color: #676a6c;
}
.ztree li span.button.file_ico_docu::before {
content: "\f022";
font-family: FontAwesome;
padding-top: 10px;
padding-left: 2px;
display: inline-block;
color: #676a6c;
}
.ztree li span.button.linux_ico_docu::before {
content: "\f17c";
font-family: FontAwesome;
padding-top: 10px;
padding-left: 2px;
display: inline-block;
color: #676a6c;
}
.ztree li span.button.windows_ico_docu::before {
content: "\f17a";
font-family: FontAwesome;
padding-top: 10px;
padding-left: 2px;
......
......@@ -39,11 +39,11 @@ website: http://code.google.com/p/jquerytree/
margin:0; padding:5px; color:@color-normal; background-color: @color-bg;
li {
padding:0; margin:0; list-style:none; line-height:17px; text-align:left; white-space:nowrap; outline:0;
ul {
ul {
margin: 0px; padding:0 0 0 18px;
}
ul.line { }
a {padding-right:3px; margin:0; cursor:pointer; height:@h; color:@color-normal; background-color: transparent;
a {padding-right:3px; margin:0; cursor:pointer; height:@h; color:@color-normal; background-color: transparent;
text-decoration:none; vertical-align:top; display: inline-block;
input.rename {height:14px; width:80px; padding:0; margin:0;
color: @color-bg; background-color: @color-normal;
......@@ -64,11 +64,11 @@ website: http://code.google.com/p/jquerytree/
span.button {line-height:0; margin:0; padding: 0; width:@w; height:@h; display: inline-block; vertical-align:top;
border:0px solid; cursor: pointer;outline:none;
background-color:transparent; background-repeat:no-repeat; background-attachment: scroll;
&::before{color: @color-normal; font-family: FontAwesome; padding-top:@pad-top;}
&.chk { margin:0px; cursor: auto; width: 12px;
display: inline-block;padding-top:@pad-top;padding-left:@pad-left;
&.checkbox_false_full::before {content: @fa-square-o;}
&.checkbox_false_full_focus::before {content: @fa-square-o; color:@color-highlight;}
&.checkbox_false_part::before {content: @fa-square-o;color: @color-partial;}
......@@ -82,7 +82,7 @@ website: http://code.google.com/p/jquerytree/
&.checkbox_true_part::before {content: @fa-check-square-o;color: @color-partial}
&.checkbox_true_part_focus::before {content: @fa-check-square-o;color: @color-partfocus;}
&.checkbox_true_disable::before {content: @fa-check-square-o;color: @color-disabled}
&.radio_false_full::before {content: @fa-circle-o;}
&.radio_false_full_focus::before {content: @fa-circle-o;color: @color-highlight}
&.radio_false_part::before {content: @fa-circle-o;color: @color-partial}
......@@ -93,17 +93,17 @@ website: http://code.google.com/p/jquerytree/
&.radio_true_part::before {content: @fa-dot-circle-o;color: @color-partial}
&.radio_true_part_focus::before {content: @fa-dot-circle-o;color: @color-partial;}
&.radio_true_disable::before {content: @fa-circle-thin;color: @color-disabled}
}
&.switch {width:@w; height:@h}
&.root_open::before{content: @fa-chevron-down;padding-top:@pad-top;padding-left:@pad-left;display: inline-block;}
&.root_close::before{content: @fa-chevron-right;padding-top:@pad-top;padding-left:@pad-left;display: inline-block;}
&.roots_open::before{content: @fa-chevron-down;padding-top:@pad-top;padding-left:@pad-left;display: inline-block;}
&.roots_close::before{content: @fa-chevron-right;padding-top:@pad-top;padding-left:@pad-left;display: inline-block;}
&.center_open::before{content: @fa-chevron-down;padding-top:@pad-top;padding-left:@pad-left;display: inline-block;}
&.center_close::before{content: @fa-chevron-right;padding-top:@pad-top;padding-left:@pad-left;display: inline-block;}
&.bottom_open::before{content: @fa-chevron-down;padding-top:@pad-top;padding-left:@pad-left;display: inline-block;}
&.bottom_close::before{content: @fa-chevron-right;padding-top:@pad-top;padding-left:@pad-left;display: inline-block;}
&.root_open::before{content: @fa-angle-down;padding-top:@pad-top;padding-left:@pad-left;display: inline-block;}
&.root_close::before{content: @fa-angle-right;padding-top:@pad-top;padding-left:@pad-left;display: inline-block;}
&.roots_open::before{content: @fa-angle-down;padding-top:@pad-top;padding-left:@pad-left;display: inline-block;}
&.roots_close::before{content: @fa-angle-right;padding-top:@pad-top;padding-left:@pad-left;display: inline-block;}
&.center_open::before{content: @fa-angle-down;padding-top:@pad-top;padding-left:@pad-left;display: inline-block;}
&.center_close::before{content: @fa-angle-right;padding-top:@pad-top;padding-left:@pad-left;display: inline-block;}
&.bottom_open::before{content: @fa-angle-down;padding-top:@pad-top;padding-left:@pad-left;display: inline-block;}
&.bottom_close::before{content: @fa-angle-right;padding-top:@pad-top;padding-left:@pad-left;display: inline-block;}
&.noline_open{}
&.noline_close{}
&.root_docu{ background:none;}
......@@ -111,11 +111,15 @@ website: http://code.google.com/p/jquerytree/
&.center_docu::before{padding-top:@pad-top;padding-left:@pad-left;display: inline-block;color:@color-normal;}
&.bottom_docu::before{padding-top:@pad-top;padding-left:@pad-left;display: inline-block;color:@color-normal;}
&.noline_docu{ background:none;}
&.ico_open::before {content: @fa-folder-open;font-family: FontAwesome;padding-top:@pad-top;padding-left:@pad-left;display: inline-block;color:@color-normal;}
&.ico_close::before {content: @fa-folder;font-family: FontAwesome;padding-top:@pad-top;padding-left:@pad-left;display: inline-block;color:@color-normal;}
&.ico_docu::before{content: @fa-folder-o;font-family: FontAwesome;padding-top:@pad-top;padding-left:@pad-left;display: inline-block;color:@color-normal;}
&.ico_docu::before{content: @fa-folder;font-family: FontAwesome;padding-top:@pad-top;padding-left:@pad-left;display: inline-block;color:@color-normal;}
&.file_ico_docu::before{content: @fa-list-alt;font-family: FontAwesome;padding-top:@pad-top;padding-left:@pad-left;display: inline-block;color:@color-normal;}
&.linux_ico_docu::before{content: @fa-linux;font-family: FontAwesome;padding-top:@pad-top;padding-left:@pad-left;display: inline-block;color:@color-normal;}
&.windows_ico_docu::before{content: @fa-windows;font-family: FontAwesome;padding-top:@pad-top;padding-left:@pad-left;display: inline-block;color:@color-normal;}
&.edit {margin-left:4px; margin-right: -1px; vertical-align:top; *vertical-align:middle;padding-top:@pad-top;}
&.edit::before{content: @fa-pencil-square-o;font-family: FontAwesome;}
......
......@@ -62,7 +62,6 @@ function GetTableDataBox() {
}
}
for (i in id_list) {
console.log(tabProduct);
tableData.push(GetRowData(tabProduct.rows[id_list[i]]));
}
......@@ -240,6 +239,13 @@ $.fn.serializeObject = function()
});
return o;
};
function makeLabel(data) {
return "<label class='detail-key'><b>" + data[0] + ": </b></label>" + data[1] + "</br>"
}
var jumpserver = {};
jumpserver.checked = false;
jumpserver.selected = {};
......@@ -281,7 +287,7 @@ jumpserver.initDataTable = function (options) {
buttons: [],
columnDefs: columnDefs,
ajax: {
url: options.ajax_url ,
url: options.ajax_url,
dataSrc: ""
},
columns: options.columns || [],
......
......@@ -18,7 +18,7 @@
{% endblock %}
{% block table_search %}
<form id="search_form" method="get" action="" class="pull-right form-inline">
<form id="search_form" method="get" action="" class="pull-right form-inline" style="padding-bottom: 8px">
<div class="form-group" id="date">
<div class="input-daterange input-group" id="datepicker">
<span class="input-group-addon"><i class="fa fa-calendar"></i></span>
......@@ -64,7 +64,7 @@
</form>
{% endblock %}
{% block table_container %}
<table class="footable table table-stripped toggle-arrow-tiny" data-page="false">
<table class="footable table table-stripped table-bordered toggle-arrow-tiny" data-page="false" >
<thead>
<tr>
<th data-toggle="true">ID</th>
......
......@@ -26,11 +26,15 @@ logger = get_logger(__name__)
class UserViewSet(IDInFilterMixin, BulkModelViewSet):
queryset = User.objects.exclude(role="App")
# queryset = User.objects.all().exclude(role="App").order_by("date_joined")
serializer_class = UserSerializer
permission_classes = (IsSuperUserOrAppUser, IsAuthenticated)
permission_classes = (IsSuperUser,)
filter_fields = ('username', 'email', 'name', 'id')
def get_permissions(self):
if self.action == "retrieve":
self.permission_classes = (IsSuperUserOrAppUser,)
return super().get_permissions()
class ChangeUserPasswordApi(generics.RetrieveUpdateAPIView):
permission_classes = (IsSuperUser,)
......@@ -57,7 +61,6 @@ class UserResetPasswordApi(generics.UpdateAPIView):
def perform_update(self, serializer):
# Note: we are not updating the user object here.
# We just do the reset-password stuff.
import uuid
from .utils import send_reset_password_mail
user = self.get_object()
user.password_raw = str(uuid.uuid4())
......@@ -68,6 +71,7 @@ class UserResetPasswordApi(generics.UpdateAPIView):
class UserResetPKApi(generics.UpdateAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
permission_classes = (IsAuthenticated,)
def perform_update(self, serializer):
from .utils import send_reset_ssh_key_mail
......@@ -91,6 +95,7 @@ class UserUpdatePKApi(generics.UpdateAPIView):
class UserGroupViewSet(IDInFilterMixin, BulkModelViewSet):
queryset = UserGroup.objects.all()
serializer_class = UserGroupSerializer
permission_classes = (IsSuperUser,)
class UserGroupUpdateUserApi(generics.RetrieveUpdateAPIView):
......
......@@ -6,7 +6,6 @@ from django.utils.translation import gettext_lazy as _
from captcha.fields import CaptchaField
from common.utils import validate_ssh_public_key
from perms.models import AssetPermission
from .models import User, UserGroup
......@@ -253,30 +252,30 @@ class UserGroupForm(forms.ModelForm):
}
class UserGroupPrivateAssetPermissionForm(forms.ModelForm):
def save(self, commit=True):
self.instance = super(UserGroupPrivateAssetPermissionForm, self)\
.save(commit=commit)
self.instance.user_groups = [self.user_group]
self.instance.save()
return self.instance
class Meta:
model = AssetPermission
fields = [
'assets', 'asset_groups', 'system_users', 'name',
]
widgets = {
'assets': forms.SelectMultiple(
attrs={'class': 'select2',
'data-placeholder': _('Select assets')}),
'asset_groups': forms.SelectMultiple(
attrs={'class': 'select2',
'data-placeholder': _('Select asset groups')}),
'system_users': forms.SelectMultiple(
attrs={'class': 'select2',
'data-placeholder': _('Select system users')}),
}
# class UserGroupPrivateAssetPermissionForm(forms.ModelForm):
# def save(self, commit=True):
# self.instance = super(UserGroupPrivateAssetPermissionForm, self)\
# .save(commit=commit)
# self.instance.user_groups = [self.user_group]
# self.instance.save()
# return self.instance
#
# class Meta:
# model = AssetPermission
# fields = [
# 'assets', 'asset_groups', 'system_users', 'name',
# ]
# widgets = {
# 'assets': forms.SelectMultiple(
# attrs={'class': 'select2',
# 'data-placeholder': _('Select assets')}),
# 'asset_groups': forms.SelectMultiple(
# attrs={'class': 'select2',
# 'data-placeholder': _('Select asset groups')}),
# 'system_users': forms.SelectMultiple(
# attrs={'class': 'select2',
# 'data-placeholder': _('Select system users')}),
# }
class FileForm(forms.Form):
......
......@@ -30,6 +30,11 @@ class User(AbstractUser):
(ROLE_USER, _('User')),
(ROLE_APP, _('Application'))
)
OTP_LEVEL_CHOICES = (
(0, _('Disable')),
(1, _('Enable')),
(2, _("Force enable")),
)
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'))
......@@ -39,8 +44,8 @@ class User(AbstractUser):
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'))
enable_otp = models.BooleanField(default=False, verbose_name=_('Enable OTP'))
secret_key_otp = models.CharField(max_length=16, blank=True)
otp_level = models.SmallIntegerField(default=0, choices=OTP_LEVEL_CHOICES, verbose_name=_('Enable OTP'))
otp_secret_key = models.CharField(max_length=16, blank=True)
# Todo: Auto generate key, let user download
_private_key = models.CharField(max_length=5000, blank=True, verbose_name=_('Private key'))
_public_key = models.CharField(max_length=5000, blank=True, verbose_name=_('Public key'))
......@@ -50,7 +55,7 @@ class User(AbstractUser):
created_by = models.CharField(max_length=30, default='', verbose_name=_('Created by'))
def __str__(self):
return self.username
return '{0.name}({0.username})'.format(self)
@property
def password_raw(self):
......@@ -202,6 +207,20 @@ class User(AbstractUser):
def generate_reset_token(self):
return signer.sign_t({'reset': str(self.id), 'email': self.email}, expires_in=3600)
@property
def otp_enabled(self):
return self.otp_level > 0
def enabled_otp(self):
self.otp_level = 1
def force_enable_otp(self):
self.otp_level = 2
@property
def otp_force_enabled(self):
return self.otp_level == 2
def to_json(self):
return OrderedDict({
'id': self.id,
......@@ -222,7 +241,7 @@ class User(AbstractUser):
def create_app_user(cls, name, comment):
app = cls.objects.create(
username=name, name=name, email='{}@local.domain'.format(name),
is_active=False, role='App', enable_otp=False, comment=comment,
is_active=False, role='App', comment=comment,
is_first_login=False, created_by='System'
)
access_key = app.create_access_key()
......
......@@ -2,19 +2,29 @@
#
from django.dispatch import receiver
from django.db.models.signals import post_save
# from django.db.models.signals import post_save
from common.utils import get_logger
from .models import User
from .signals import post_user_create
# from .models import User
logger = get_logger(__file__)
@receiver(post_save, sender=User)
def on_user_created(sender, instance=None, created=False, **kwargs):
if created:
logger.debug("Receive user `{}` create signal".format(instance.name))
from .utils import send_user_created_mail
logger.info(" - Sending welcome mail ...".format(instance.name))
if instance.email:
send_user_created_mail(instance)
\ No newline at end of file
# @receiver(post_save, sender=User)
# def on_user_created(sender, instance=None, created=False, **kwargs):
# if created:
# logger.debug("Receive user `{}` create signal".format(instance.name))
# from .utils import send_user_created_mail
# logger.info(" - Sending welcome mail ...".format(instance.name))
# if instance.email:
# send_user_created_mail(instance)
@receiver(post_user_create)
def on_user_create(sender, user=None, **kwargs):
logger.debug("Receive user `{}` create signal".format(user.name))
from .utils import send_user_created_mail
logger.info(" - Sending welcome mail ...".format(user.name))
if user.email:
send_user_created_mail(user)
......@@ -44,7 +44,7 @@
<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>
<th class="text-center">{% trans 'System users' %}</th>
</tr>
</thead>
<tbody>
......@@ -63,6 +63,8 @@
<script>
var zTree;
var inited = false;
var url;
var asset_table;
function initTable() {
if (inited){
......@@ -86,31 +88,28 @@ function initTable() {
}
}},
{targets: 4, createdCell: function (td, cellData) {
if (cellData === 'Unknown'){
$(td).html('<i class="fa fa-circle text-warning"></i>')
} else if (!cellData) {
$(td).html('<i class="fa fa-circle text-danger"></i>')
} else {
$(td).html('<i class="fa fa-circle text-navy"></i>')
}
var users = [];
$.each(cellData, function (id, data) {
users.push(data.name);
});
$(td).html(users.join(', '))
}}
],
ajax_url: '{% url "api-assets:asset-list" %}',
ajax_url: url,
columns: [
{data: "id"}, {data: "hostname" }, {data: "ip" },
{data: "is_active", orderable: false },
{data: "is_connective", orderable: false}
{data: "system_users_granted", orderable: false}
]
};
asset_table = jumpserver.initServerSideDataTable(options);
return asset_table
return jumpserver.initDataTable(options);
}
function onSelected(event, treeNode) {
initTable();
var url = asset_table.ajax.url();
url = setUrlParam(url, "node_id", treeNode.id);
url = '{% url "api-perms:user-node-assets" pk=object.id node_id=DEFAULT_PK %}';
url = url.replace("{{ DEFAULT_PK }}", treeNode.id);
setCookie('node_selected', treeNode.id);
asset_table = initTable();
asset_table.ajax.url(url);
asset_table.ajax.reload();
}
......
......@@ -63,6 +63,7 @@
<script>
var zTree;
var inited = false;
var url;
function initTable() {
if (inited){
......@@ -86,31 +87,29 @@ function initTable() {
}
}},
{targets: 4, createdCell: function (td, cellData) {
if (cellData === 'Unknown'){
$(td).html('<i class="fa fa-circle text-warning"></i>')
} else if (!cellData) {
$(td).html('<i class="fa fa-circle text-danger"></i>')
} else {
$(td).html('<i class="fa fa-circle text-navy"></i>')
}
var users = [];
$.each(cellData, function (id, data) {
users.push(data.name);
});
$(td).html(users.join(', '))
}}
],
ajax_url: '{% url "api-assets:asset-list" %}',
ajax_url: url,
columns: [
{data: "id"}, {data: "hostname" }, {data: "ip" },
{data: "is_active", orderable: false },
{data: "is_connective", orderable: false}
{data: "system_users_granted", orderable: false}
]
};
asset_table = jumpserver.initServerSideDataTable(options);
asset_table = jumpserver.initDataTable(options);
return asset_table
}
function onSelected(event, treeNode) {
initTable();
var url = asset_table.ajax.url();
url = setUrlParam(url, "node_id", treeNode.id);
url = '{% url "api-perms:user-group-node-assets" pk=object.id node_id=DEFAULT_PK %}';
url = url.replace("{{ DEFAULT_PK }}", treeNode.id);
setCookie('node_selected', treeNode.id);
asset_table = initTable();
asset_table.ajax.url(url);
asset_table.ajax.reload();
}
......
......@@ -65,7 +65,7 @@
</tr>
<tr>
<td class="text-navy">{% trans 'OTP' %}</td>
<td>{{ user.enable_otp|yesno:"Yes,No,Unkown" }}</td>
<td>{{ user.otp_enabled|yesno:"Yes,No,Unkown" }}</td>
</tr>
<tr>
<td class="text-navy">{% trans 'Public key' %}</td>
......
......@@ -2,7 +2,6 @@
from __future__ import unicode_literals
import os
from django import forms
from django.shortcuts import render
from django.contrib.auth import login as auth_login, logout as auth_logout
from django.contrib.auth.mixins import LoginRequiredMixin
......@@ -20,10 +19,9 @@ from django.views.generic.base import TemplateView
from django.views.generic.edit import FormView
from formtools.wizard.views import SessionWizardView
from django.conf import settings
from django.utils import timezone
from common.utils import get_object_or_none
from common.mixins import DatetimeSearchMixin
from common.mixins import DatetimeSearchMixin, AdminUserRequiredMixin
from ..models import User, LoginLog
from ..utils import send_reset_password_mail
from ..tasks import write_login_log_async
......@@ -194,8 +192,6 @@ class UserFirstLoginView(LoginRequiredMixin, SessionWizardView):
for field in form:
if field.value():
setattr(user, field.name, field.value())
if field.name == 'enable_otp':
user.enable_otp = field.value()
user.is_first_login = False
user.is_public_key_valid = True
user.save()
......@@ -228,7 +224,7 @@ class UserFirstLoginView(LoginRequiredMixin, SessionWizardView):
return form
class LoginLogListView(DatetimeSearchMixin, ListView):
class LoginLogListView(AdminUserRequiredMixin, DatetimeSearchMixin, ListView):
template_name = 'users/login_log_list.html'
model = LoginLog
paginate_by = settings.DISPLAY_PER_PAGE
......
......@@ -79,6 +79,7 @@ class UserCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateView):
user = form.save(commit=False)
user.created_by = self.request.user.username or 'System'
user.save()
post_user_create.send(self.__class__, user=user)
return super().form_valid(form)
......
#!/bin/bash
#
python ../apps/manage.py shell << EOF
from perms.models import *
for old in NodePermission.objects.all():
perm = asset_perm_model.objects.using(db_alias).create(
name="{}-{}-{}".format(
old.node.value,
old.user_group.name,
old.system_user.name
),
is_active=old.is_active,
date_expired=old.date_expired,
created_by=old.date_expired,
date_created=old.date_created,
comment=old.comment,
)
perm.user_groups.add(old.user_group)
perm.nodes.add(old.node)
perm.system_users.add(old.system_user)
EOF
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment