Unverified Commit eafef9fc authored by BaiJiangJie's avatar BaiJiangJie Committed by GitHub

Dev (#2785)

* [Update]优化用户页面的资产标签过滤功能 (#2781)

* [Update] 优化用户页面的资产标签下拉框选项

* [Update]增加用户页面的过滤资产标签功能

* [Update]优化用户页面的资产标签过滤

* [Update]优化用户页面的资产标签过滤代码

* [Update] 优化用户页面的资产标签过滤

* [Update] 优化用户API,创建用户添加组织关系 (#2776)

* [Update] 优化前端高度显示css (#2749)

* [Update] 修改授权树显示策略 (#2784)

* [Update] 修改授权树显示策略

* [Update] 是否允许用户执行批量命令

* [Update] 优化授权节点构建

* [Update] 修改节点大小判断

* [Update] 修改节点大小判断
parent e5185ebd
...@@ -40,8 +40,10 @@ class Node(OrgModelMixin): ...@@ -40,8 +40,10 @@ class Node(OrgModelMixin):
return self.key == other.key return self.key == other.key
def __gt__(self, other): def __gt__(self, other):
if self.is_root(): if self.is_root() and not other.is_root():
return True return True
elif not self.is_root() and other.is_root():
return False
self_key = [int(k) for k in self.key.split(':')] self_key = [int(k) for k in self.key.split(':')]
other_key = [int(k) for k in other.key.split(':')] other_key = [int(k) for k in other.key.split(':')]
self_parent_key = self_key[:-1] self_parent_key = self_key[:-1]
...@@ -49,6 +51,10 @@ class Node(OrgModelMixin): ...@@ -49,6 +51,10 @@ class Node(OrgModelMixin):
if self_parent_key == other_parent_key: if self_parent_key == other_parent_key:
return self.name > other.name return self.name > other.name
if len(self_parent_key) < len(other_parent_key):
return True
elif len(self_parent_key) > len(other_parent_key):
return False
return self_key > other_key return self_key > other_key
def __lt__(self, other): def __lt__(self, other):
......
...@@ -153,7 +153,11 @@ function initTree() { ...@@ -153,7 +153,11 @@ function initTree() {
$(document).ready(function () { $(document).ready(function () {
initTree(); initTree();
initTable(); initTable();
}) }).on('click', '.labels li', function () {
var val = $(this).text();
$("#user_assets_table_filter input").val(val);
asset_table.search(val).draw();
})
.on('click', '.asset_detail', function() { .on('click', '.asset_detail', function() {
var data = asset_table.ajax.json(); var data = asset_table.ajax.json();
var asset_id = this.getAttribute("asset-id"); var asset_id = this.getAttribute("asset-id");
......
...@@ -79,6 +79,7 @@ class UserAssetListView(LoginRequiredMixin, TemplateView): ...@@ -79,6 +79,7 @@ class UserAssetListView(LoginRequiredMixin, TemplateView):
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = { context = {
'action': _('My assets'), 'action': _('My assets'),
'labels': Label.objects.all().order_by('name'),
'system_users': SystemUser.objects.all(), 'system_users': SystemUser.objects.all(),
} }
kwargs.update(context) kwargs.update(context)
......
...@@ -15,7 +15,8 @@ def jumpserver_processor(request): ...@@ -15,7 +15,8 @@ def jumpserver_processor(request):
'FAVICON_URL': static('img/facio.ico'), 'FAVICON_URL': static('img/facio.ico'),
'JMS_TITLE': 'Jumpserver', 'JMS_TITLE': 'Jumpserver',
'VERSION': settings.VERSION, 'VERSION': settings.VERSION,
'COPYRIGHT': 'FIT2CLOUD 飞致云' + ' © 2014-2019' 'COPYRIGHT': 'FIT2CLOUD 飞致云' + ' © 2014-2019',
'SECURITY_COMMAND_EXECUTION': settings.SECURITY_COMMAND_EXECUTION,
} }
return context return context
......
...@@ -546,6 +546,7 @@ TERMINAL_REPLAY_STORAGE = { ...@@ -546,6 +546,7 @@ TERMINAL_REPLAY_STORAGE = {
SECURITY_MFA_AUTH = False SECURITY_MFA_AUTH = False
SECURITY_COMMAND_EXECUTION = True
SECURITY_LOGIN_LIMIT_COUNT = 7 SECURITY_LOGIN_LIMIT_COUNT = 7
SECURITY_LOGIN_LIMIT_TIME = 30 # Unit: minute SECURITY_LOGIN_LIMIT_TIME = 30 # Unit: minute
SECURITY_MAX_IDLE_TIME = 30 # Unit: minute SECURITY_MAX_IDLE_TIME = 30 # Unit: minute
......
This diff is collapsed.
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
# #
from rest_framework import viewsets from rest_framework import viewsets
from django.db import transaction from django.db import transaction
from django.conf import settings
from common.permissions import IsValidUser from common.permissions import IsValidUser
from ..models import CommandExecution from ..models import CommandExecution
...@@ -18,6 +19,11 @@ class CommandExecutionViewSet(viewsets.ModelViewSet): ...@@ -18,6 +19,11 @@ class CommandExecutionViewSet(viewsets.ModelViewSet):
user_id=str(self.request.user.id) user_id=str(self.request.user.id)
) )
def check_permissions(self, request):
if not settings.SECURITY_COMMAND_EXECUTION:
return self.permission_denied(request, "Command execution disabled")
return super().check_permissions(request)
def perform_create(self, serializer): def perform_create(self, serializer):
instance = serializer.save() instance = serializer.save()
instance.user = self.request.user instance.user = self.request.user
......
...@@ -14,6 +14,7 @@ class AssetsFilterMixin(object): ...@@ -14,6 +14,7 @@ class AssetsFilterMixin(object):
def filter_queryset(self, queryset): def filter_queryset(self, queryset):
queryset = self.search_assets(queryset) queryset = self.search_assets(queryset)
queryset = self.filter_labels(queryset)
queryset = self.sort_assets(queryset) queryset = self.sort_assets(queryset)
return queryset return queryset
...@@ -40,6 +41,31 @@ class AssetsFilterMixin(object): ...@@ -40,6 +41,31 @@ class AssetsFilterMixin(object):
queryset = sort_assets(queryset, order_by=order_by, reverse=reverse) queryset = sort_assets(queryset, order_by=order_by, reverse=reverse)
return queryset return queryset
def filter_labels(self, queryset):
from assets.models import Label
query_keys = self.request.query_params.keys()
all_label_keys = Label.objects.values_list('name', flat=True)
valid_keys = set(all_label_keys) & set(query_keys)
labels_query = {}
for key in valid_keys:
labels_query[key] = self.request.query_params.get(key)
if not labels_query:
return queryset
labels = set()
for k, v in labels_query.items():
label = Label.objects.filter(name=k, value=v).first()
if not label:
continue
labels.add(label)
_queryset = []
for asset in queryset:
_labels = set(asset.labels.all()) & set(labels)
if _labels and len(_labels) == len(set(labels)):
_queryset.append(asset)
return _queryset
class RemoteAppFilterMixin(object): class RemoteAppFilterMixin(object):
""" """
......
...@@ -37,9 +37,21 @@ class GenerateTree: ...@@ -37,9 +37,21 @@ class GenerateTree:
def add_asset(self, asset, system_users): def add_asset(self, asset, system_users):
nodes = asset.nodes.all() nodes = asset.nodes.all()
self.add_nodes(nodes) in_nodes = False
for node in nodes: for node in nodes:
if node not in self.nodes:
continue
self.nodes[node][asset].update(system_users) self.nodes[node][asset].update(system_users)
in_nodes = True
if not in_nodes:
all_nodes = self.nodes.keys()
# 如果没有授权节点,就放到默认的根节点下
if not all_nodes:
root_node = Node.root()
self.add_node(root_node)
else:
root_node = max(all_nodes)
self.nodes[root_node][asset].update(system_users)
def get_nodes(self): def get_nodes(self):
for node in self.nodes: for node in self.nodes:
...@@ -50,6 +62,7 @@ class GenerateTree: ...@@ -50,6 +62,7 @@ class GenerateTree:
node.assets_amount = len(assets) node.assets_amount = len(assets)
return self.nodes return self.nodes
# 添加节点时,追溯到根节点
def add_node(self, node): def add_node(self, node):
if node in self.nodes: if node in self.nodes:
return return
...@@ -62,9 +75,11 @@ class GenerateTree: ...@@ -62,9 +75,11 @@ class GenerateTree:
self.add_node(n) self.add_node(n)
break break
# 添加树节点
def add_nodes(self, nodes): def add_nodes(self, nodes):
for node in nodes: for node in nodes:
self.add_node(node) self.add_node(node)
self.add_nodes(node.get_all_children(with_self=False))
def get_user_permissions(user, include_group=True): def get_user_permissions(user, include_group=True):
...@@ -123,6 +138,7 @@ class AssetPermissionUtil: ...@@ -123,6 +138,7 @@ class AssetPermissionUtil:
self._assets = None self._assets = None
self._filter_id = 'None' # 当通过filter更改 permission是标记 self._filter_id = 'None' # 当通过filter更改 permission是标记
self.cache_policy = cache_policy self.cache_policy = cache_policy
self.tree = GenerateTree()
@classmethod @classmethod
def is_not_using_cache(cls, cache_policy): def is_not_using_cache(cls, cache_policy):
...@@ -181,6 +197,7 @@ class AssetPermissionUtil: ...@@ -181,6 +197,7 @@ class AssetPermissionUtil:
permissions = self.permissions.prefetch_related('nodes', 'system_users') permissions = self.permissions.prefetch_related('nodes', 'system_users')
for perm in permissions: for perm in permissions:
actions = perm.actions.all() actions = perm.actions.all()
self.tree.add_nodes(perm.nodes.all())
for node in perm.nodes.all(): for node in perm.nodes.all():
system_users = perm.system_users.all() system_users = perm.system_users.all()
system_users = self._structured_system_user(system_users, actions) system_users = self._structured_system_user(system_users, actions)
...@@ -275,10 +292,9 @@ class AssetPermissionUtil: ...@@ -275,10 +292,9 @@ class AssetPermissionUtil:
:return: :return:
""" """
assets = self.get_assets_without_cache() assets = self.get_assets_without_cache()
tree = GenerateTree()
for asset, system_users in assets.items(): for asset, system_users in assets.items():
tree.add_asset(asset, system_users) self.tree.add_asset(asset, system_users)
return tree.get_nodes() return self.tree.get_nodes()
def get_nodes_with_assets_from_cache(self): def get_nodes_with_assets_from_cache(self):
cached = cache.get(self.node_key) cached = cache.get(self.node_key)
......
...@@ -180,6 +180,11 @@ class SecuritySettingForm(BaseForm): ...@@ -180,6 +180,11 @@ class SecuritySettingForm(BaseForm):
'authentication (valid for all users, including administrators)' 'authentication (valid for all users, including administrators)'
) )
) )
# Execute commands for user
SECURITY_COMMAND_EXECUTION = forms.BooleanField(
required=False, label=_("Batch execute commands"),
help_text=_("Allow user batch execute commands")
)
# limit login count # limit login count
SECURITY_LOGIN_LIMIT_COUNT = forms.IntegerField( SECURITY_LOGIN_LIMIT_COUNT = forms.IntegerField(
min_value=3, max_value=99999, min_value=3, max_value=99999,
......
...@@ -8118,7 +8118,7 @@ body.md-skin { ...@@ -8118,7 +8118,7 @@ body.md-skin {
#page-wrapper { #page-wrapper {
position: inherit; position: inherit;
margin: 0 0 0 220px; margin: 0 0 0 220px;
min-height: 1200px; /*min-height: 1200px;*/
} }
.navbar-static-side { .navbar-static-side {
z-index: 2001; z-index: 2001;
......
...@@ -16,11 +16,13 @@ ...@@ -16,11 +16,13 @@
</li> </li>
</ul> </ul>
</li> </li>
{% if SECURITY_COMMAND_EXECUTION %}
<li id="ops"> <li id="ops">
<a href="{% url 'ops:command-execution-start' %}"> <a href="{% url 'ops:command-execution-start' %}">
<i class="fa fa-terminal" style="width: 14px"></i> <span class="nav-label">{% trans 'Command execution' %}</span><span class="label label-info pull-right"></span> <i class="fa fa-terminal" style="width: 14px"></i> <span class="nav-label">{% trans 'Command execution' %}</span><span class="label label-info pull-right"></span>
</a> </a>
</li> </li>
{% endif %}
<li id="users"> <li id="users">
<a href="{% url 'users:user-profile' %}"> <a href="{% url 'users:user-profile' %}">
<i class="fa fa-user" style="width: 14px"></i> <span class="nav-label">{% trans 'Profile' %}</span><span class="label label-info pull-right"></span> <i class="fa fa-user" style="width: 14px"></i> <span class="nav-label">{% trans 'Profile' %}</span><span class="label label-info pull-right"></span>
......
...@@ -48,6 +48,9 @@ class UserViewSet(IDInCacheFilterMixin, BulkModelViewSet): ...@@ -48,6 +48,9 @@ class UserViewSet(IDInCacheFilterMixin, BulkModelViewSet):
def perform_create(self, serializer): def perform_create(self, serializer):
users = serializer.save() users = serializer.save()
for user in users:
if current_org and current_org.is_real():
user.orgs.add(current_org.id)
self.send_created_signal(users) self.send_created_signal(users)
def get_queryset(self): def get_queryset(self):
......
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