Unverified Commit 176052e8 authored by 老广's avatar 老广 Committed by GitHub

[Update] 权限页面增加过滤规则 (#2349)

parent d026b31c
...@@ -229,7 +229,8 @@ def test_admin_user_connectivity_period(): ...@@ -229,7 +229,8 @@ def test_admin_user_connectivity_period():
@shared_task @shared_task
def test_admin_user_connectivity_manual(admin_user): def test_admin_user_connectivity_manual(admin_user):
task_name = _("Test admin user connectivity: {}").format(admin_user.name) task_name = _("Test admin user connectivity: {}").format(admin_user.name)
return test_admin_user_connectivity_util(admin_user, task_name) test_admin_user_connectivity_util(admin_user, task_name)
return True
## System user connective ## ## System user connective ##
......
This diff is collapsed.
# coding: utf-8 # coding: utf-8
import os import os
import subprocess
from django.conf import settings
from celery import shared_task, subtask from celery import shared_task, subtask
from django.utils import timezone from django.utils import timezone
...@@ -59,8 +61,9 @@ def clean_tasks_adhoc_period(): ...@@ -59,8 +61,9 @@ def clean_tasks_adhoc_period():
@after_app_shutdown_clean_periodic @after_app_shutdown_clean_periodic
@register_as_period_task(interval=3600*24) @register_as_period_task(interval=3600*24)
def clean_celery_tasks_period(): def clean_celery_tasks_period():
expire_days = 30
logger.debug("Start clean celery task history") logger.debug("Start clean celery task history")
one_month_ago = timezone.now() - timezone.timedelta(days=30) one_month_ago = timezone.now() - timezone.timedelta(days=expire_days)
tasks = CeleryTask.objects.filter(date_start__lt=one_month_ago) tasks = CeleryTask.objects.filter(date_start__lt=one_month_ago)
for task in tasks: for task in tasks:
if os.path.isfile(task.full_log_path): if os.path.isfile(task.full_log_path):
...@@ -71,6 +74,10 @@ def clean_celery_tasks_period(): ...@@ -71,6 +74,10 @@ def clean_celery_tasks_period():
task.delete() task.delete()
tasks = CeleryTask.objects.filter(date_start__isnull=True) tasks = CeleryTask.objects.filter(date_start__isnull=True)
tasks.delete() tasks.delete()
command = "find %s -mtime +%s -name '*.log' -type f -exec rm -f {} \\;" % (
settings.CELERY_LOG_DIR, expire_days
)
subprocess.call(command, shell=True)
@shared_task @shared_task
......
...@@ -66,3 +66,4 @@ class OrgMembershipUsersViewSet(OrgMembershipModelViewSetMixin, BulkModelViewSet ...@@ -66,3 +66,4 @@ class OrgMembershipUsersViewSet(OrgMembershipModelViewSetMixin, BulkModelViewSet
serializer_class = OrgMembershipUserSerializer serializer_class = OrgMembershipUserSerializer
membership_class = Organization.users.through membership_class = Organization.users.through
permission_classes = (IsSuperUserOrAppUser, ) permission_classes = (IsSuperUserOrAppUser, )
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
from werkzeug.local import Local from werkzeug.local import Local
from django.db import models from django.db import models
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.shortcuts import redirect from django.shortcuts import redirect, get_object_or_404
from django.forms import ModelForm from django.forms import ModelForm
from django.http.response import HttpResponseForbidden from django.http.response import HttpResponseForbidden
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
...@@ -191,7 +191,7 @@ class OrgMembershipModelViewSetMixin: ...@@ -191,7 +191,7 @@ class OrgMembershipModelViewSetMixin:
http_method_names = ['get', 'post', 'delete', 'head', 'options'] http_method_names = ['get', 'post', 'delete', 'head', 'options']
def dispatch(self, request, *args, **kwargs): def dispatch(self, request, *args, **kwargs):
self.org = Organization.objects.get(pk=kwargs.get('org_id')) self.org = get_object_or_404(Organization, pk=kwargs.get('org_id'))
return super().dispatch(request, *args, **kwargs) return super().dispatch(request, *args, **kwargs)
def get_serializer_context(self): def get_serializer_context(self):
...@@ -200,4 +200,5 @@ class OrgMembershipModelViewSetMixin: ...@@ -200,4 +200,5 @@ class OrgMembershipModelViewSetMixin:
return context return context
def get_queryset(self): def get_queryset(self):
return self.membership_class.objects.filter(organization=self.org) queryset = self.membership_class.objects.filter(organization=self.org)
return queryset
...@@ -9,11 +9,16 @@ from .. import api ...@@ -9,11 +9,16 @@ from .. import api
app_name = 'orgs' app_name = 'orgs'
router = DefaultRouter() router = DefaultRouter()
# 将会删除
router.register(r'org/(?P<org_id>[0-9a-zA-Z\-]{36})/membership/admins', router.register(r'org/(?P<org_id>[0-9a-zA-Z\-]{36})/membership/admins',
api.OrgMembershipAdminsViewSet, 'membership-admins') api.OrgMembershipAdminsViewSet, 'membership-admins')
router.register(r'org/(?P<org_id>[0-9a-zA-Z\-]{36})/membership/users', router.register(r'org/(?P<org_id>[0-9a-zA-Z\-]{36})/membership/users',
api.OrgMembershipUsersViewSet, 'membership-users'), api.OrgMembershipUsersViewSet, 'membership-users'),
# 替换为这个
router.register(r'orgs/(?P<org_id>[0-9a-zA-Z\-]{36})/membership/admins',
api.OrgMembershipAdminsViewSet, 'membership-admins-2')
router.register(r'orgs/(?P<org_id>[0-9a-zA-Z\-]{36})/membership/users',
api.OrgMembershipUsersViewSet, 'membership-users-2'),
router.register(r'orgs', api.OrgViewSet, 'org') router.register(r'orgs', api.OrgViewSet, 'org')
......
...@@ -2,21 +2,26 @@ ...@@ -2,21 +2,26 @@
# #
from django.shortcuts import get_object_or_404 from django.shortcuts import get_object_or_404
from django.utils import timezone
from django.db.models import Q
from rest_framework.views import APIView, Response from rest_framework.views import APIView, Response
from rest_framework.generics import ListAPIView, get_object_or_404, \ from rest_framework.generics import (
RetrieveUpdateAPIView ListAPIView, get_object_or_404, RetrieveUpdateAPIView
)
from rest_framework import viewsets from rest_framework import viewsets
from rest_framework.pagination import LimitOffsetPagination from rest_framework.pagination import LimitOffsetPagination
from common.utils import set_or_append_attr_bulk
from common.permissions import IsValidUser, IsOrgAdmin, IsOrgAdminOrAppUser from common.permissions import IsValidUser, IsOrgAdmin, IsOrgAdminOrAppUser
from common.tree import TreeNode, TreeNodeSerializer from common.tree import TreeNode, TreeNodeSerializer
from common.utils import get_object_or_none
from orgs.mixins import RootOrgViewMixin from orgs.mixins import RootOrgViewMixin
from orgs.utils import set_to_root_org from orgs.utils import set_to_root_org
from .utils import AssetPermissionUtil from .utils import AssetPermissionUtil
from .models import AssetPermission from .models import AssetPermission
from .hands import AssetGrantedSerializer, User, UserGroup, Asset, Node, \ from .hands import (
AssetGrantedSerializer, User, UserGroup, Asset, Node,
SystemUser, NodeSerializer SystemUser, NodeSerializer
)
from . import serializers from . import serializers
from .mixins import AssetsFilterMixin from .mixins import AssetsFilterMixin
...@@ -38,6 +43,7 @@ class AssetPermissionViewSet(viewsets.ModelViewSet): ...@@ -38,6 +43,7 @@ class AssetPermissionViewSet(viewsets.ModelViewSet):
queryset = AssetPermission.objects.all() queryset = AssetPermission.objects.all()
serializer_class = serializers.AssetPermissionCreateUpdateSerializer serializer_class = serializers.AssetPermissionCreateUpdateSerializer
pagination_class = LimitOffsetPagination pagination_class = LimitOffsetPagination
filter_fields = ['name']
permission_classes = (IsOrgAdmin,) permission_classes = (IsOrgAdmin,)
def get_serializer_class(self): def get_serializer_class(self):
...@@ -45,36 +51,122 @@ class AssetPermissionViewSet(viewsets.ModelViewSet): ...@@ -45,36 +51,122 @@ class AssetPermissionViewSet(viewsets.ModelViewSet):
return serializers.AssetPermissionListSerializer return serializers.AssetPermissionListSerializer
return self.serializer_class return self.serializer_class
def get_queryset(self): def filter_valid(self, queryset):
queryset = super().get_queryset().all() valid = self.request.query_params.get('is_valid', None)
search = self.request.query_params.get('search') if valid is None:
asset_id = self.request.query_params.get('asset') return queryset
node_id = self.request.query_params.get('node') if valid in ['0', 'N', 'false', 'False']:
inherit_nodes = set() valid = False
else:
valid = True
now = timezone.now()
if valid:
queryset = queryset.filter(is_active=True).filter(
date_start__lt=now, date_expired__gt=now,
)
else:
queryset = queryset.filter(
Q(is_active=False) |
Q(date_start__gt=now) |
Q(date_expired__lt=now)
)
return queryset
if search: def filter_system_user(self, queryset):
queryset = queryset.filter(name__icontains=search) system_user_id = self.request.query_params.get('system_user_id')
system_user_name = self.request.query_params.get('system_user')
if system_user_id:
system_user = get_object_or_none(SystemUser, pk=system_user_id)
elif system_user_name:
system_user = get_object_or_none(SystemUser, name=system_user_name)
else:
return queryset
if not system_user:
return queryset.none()
queryset = queryset.filter(system_users=system_user)
return queryset
if not asset_id and not node_id: def filter_node(self, queryset):
node_id = self.request.query_params.get('node_id')
node_name = self.request.query_params.get('node')
if node_id:
node = get_object_or_none(Node, pk=node_id)
elif node_name:
node = get_object_or_none(Node, name=node_name)
else:
return queryset return queryset
if not node:
return queryset.none()
nodes = node.get_ancestor(with_self=True)
queryset = queryset.filter(nodes__in=nodes)
return queryset
permissions = set() def filter_asset(self, queryset):
asset_id = self.request.query_params.get('asset_id')
hostname = self.request.query_params.get('hostname')
ip = self.request.query_params.get('ip')
if asset_id: if asset_id:
asset = get_object_or_404(Asset, pk=asset_id) assets = Asset.objects.filter(pk=asset_id)
permissions = set(queryset.filter(assets=asset)) elif hostname:
assets = Asset.objects.filter(hostname=hostname)
elif ip:
assets = Asset.objects.filter(ip=ip)
else:
return queryset
if not assets:
return queryset.none()
inherit_nodes = set()
for asset in assets:
for node in asset.nodes.all(): for node in asset.nodes.all():
inherit_nodes.update(set(node.get_ancestor(with_self=True))) inherit_nodes.update(set(node.get_ancestor(with_self=True)))
elif node_id: queryset = queryset.filter(Q(assets__in=assets) | Q(nodes__in=inherit_nodes))
node = get_object_or_404(Node, pk=node_id) return queryset
permissions = set(queryset.filter(nodes=node))
inherit_nodes = node.get_ancestor() def filter_user(self, queryset):
user_id = self.request.query_params.get('user_id')
username = self.request.query_params.get('username')
if user_id:
user = get_object_or_none(User, pk=user_id)
elif username:
user = get_object_or_none(User, username=username)
else:
return queryset
if not user:
return queryset.none()
def filter_user_group(self, queryset):
user_group_id = self.request.query_params.get('user_group_id')
user_group_name = self.request.query_params.get('user_group')
if user_group_id:
group = get_object_or_none(UserGroup, pk=user_group_id)
elif user_group_name:
group = get_object_or_none(UserGroup, name=user_group_name)
else:
return queryset
if not group:
return queryset.none()
queryset = queryset.filter(user_groups=group)
return queryset
for n in inherit_nodes: def filter_keyword(self, queryset):
_permissions = queryset.filter(nodes=n) keyword = self.request.query_params.get('search')
set_or_append_attr_bulk(_permissions, "inherit", n.value) if not keyword:
permissions.update(_permissions) return queryset
queryset = queryset.filter(name__icontains=keyword)
return queryset
return list(permissions) def filter_queryset(self, queryset):
queryset = super().filter_queryset(queryset)
queryset = self.filter_valid(queryset)
queryset = self.filter_keyword(queryset)
queryset = self.filter_asset(queryset)
queryset = self.filter_node(queryset)
queryset = self.filter_system_user(queryset)
queryset = self.filter_user_group(queryset)
return queryset
def get_queryset(self):
return self.queryset.all()
class UserGrantedAssetsApi(AssetsFilterMixin, ListAPIView): class UserGrantedAssetsApi(AssetsFilterMixin, ListAPIView):
......
...@@ -51,9 +51,15 @@ class AssetPermission(OrgModelMixin): ...@@ -51,9 +51,15 @@ class AssetPermission(OrgModelMixin):
def id_str(self): def id_str(self):
return str(self.id) return str(self.id)
@property
def is_expired(self):
if self.date_expired > timezone.now() > self.date_start:
return False
return True
@property @property
def is_valid(self): def is_valid(self):
if self.date_expired > timezone.now() > self.date_start and self.is_active: if not self.is_expired and self.is_active:
return True return True
return False return False
......
...@@ -28,19 +28,13 @@ class AssetPermissionListSerializer(serializers.ModelSerializer): ...@@ -28,19 +28,13 @@ class AssetPermissionListSerializer(serializers.ModelSerializer):
assets = StringManyToManyField(many=True, read_only=True) assets = StringManyToManyField(many=True, read_only=True)
nodes = StringManyToManyField(many=True, read_only=True) nodes = StringManyToManyField(many=True, read_only=True)
system_users = StringManyToManyField(many=True, read_only=True) system_users = StringManyToManyField(many=True, read_only=True)
inherit = serializers.SerializerMethodField() is_valid = serializers.BooleanField()
is_expired = serializers.BooleanField()
class Meta: class Meta:
model = AssetPermission model = AssetPermission
fields = '__all__' fields = '__all__'
@staticmethod
def get_inherit(obj):
if hasattr(obj, 'inherit'):
return obj.inherit
else:
return None
class AssetPermissionUpdateUserSerializer(serializers.ModelSerializer): class AssetPermissionUpdateUserSerializer(serializers.ModelSerializer):
......
...@@ -56,7 +56,7 @@ ...@@ -56,7 +56,7 @@
<th class="text-center">{% trans 'Asset' %}</th> <th class="text-center">{% trans 'Asset' %}</th>
<th class="text-center">{% trans 'Node'%}</th> <th class="text-center">{% trans 'Node'%}</th>
<th class="text-center">{% trans 'System user' %}</th> <th class="text-center">{% trans 'System user' %}</th>
<th class="text-center">{% trans 'Active' %}</th> <th class="text-center">{% trans 'Validity' %}</th>
<th class="text-center" >{% trans 'Action' %}</th> <th class="text-center" >{% trans 'Action' %}</th>
</tr> </tr>
</thead> </thead>
...@@ -67,6 +67,17 @@ ...@@ -67,6 +67,17 @@
</div> </div>
</div> </div>
</div> </div>
<ul class="dropdown-menu search-help">
<li><a class="search-item" data-value="name">{% trans 'Name' %}</a></li>
<li><a class="search-item" data-value="is_valid">{% trans 'Validity' %}</a></li>
<li><a class="search-item" data-value="username">{% trans 'Username' %}</a></li>
<li><a class="search-item" data-value="user_group">{% trans 'User group' %}</a></li>
<li><a class="search-item" data-value="ip">IP</a></li>
<li><a class="search-item" data-value="hostname">{% trans 'Hostname' %}</a></li>
<li><a class="search-item" data-value="node">{% trans 'Node' %}</a></li>
<li><a class="search-item" data-value="system_user">{% trans 'System user' %}</a></li>
</ul>
{% endblock %} {% endblock %}
{% block custom_foot_js %} {% block custom_foot_js %}
...@@ -79,11 +90,11 @@ function onSelected(event, treeNode) { ...@@ -79,11 +90,11 @@ function onSelected(event, treeNode) {
setCookie('node_selected', treeNode.id); setCookie('node_selected', treeNode.id);
var url = table.ajax.url(); var url = table.ajax.url();
if (treeNode.meta.type === 'node') { if (treeNode.meta.type === 'node') {
url = setUrlParam(url, 'asset', ""); url = setUrlParam(url, 'asset_id', "");
url = setUrlParam(url, 'node', treeNode.meta.node.id) url = setUrlParam(url, 'node_id', treeNode.meta.node.id)
} else { } else {
url = setUrlParam(url, 'node', ""); url = setUrlParam(url, 'node_id', "");
url = setUrlParam(url, 'asset', treeNode.meta.asset.id) url = setUrlParam(url, 'asset_id', treeNode.meta.asset.id)
} }
setCookie('node_selected', treeNode.node_id); setCookie('node_selected', treeNode.node_id);
table.ajax.url(url); table.ajax.url(url);
...@@ -178,7 +189,7 @@ function initTable() { ...@@ -178,7 +189,7 @@ function initTable() {
{data: "id"}, {data: "name"}, {data: "users"}, {data: "id"}, {data: "name"}, {data: "users"},
{data: "user_groups"}, {data: "assets"}, {data: "user_groups"}, {data: "assets"},
{data: "nodes"}, {data: "system_users"}, {data: "nodes"}, {data: "system_users"},
{data: "is_active", orderable: false}, {data: "id", orderable: false} {data: "is_valid", orderable: false}, {data: "id", orderable: false}
], ],
select: {}, select: {},
op_html: $('#actions').html() op_html: $('#actions').html()
...@@ -231,6 +242,7 @@ function toggle() { ...@@ -231,6 +242,7 @@ function toggle() {
$(document).ready(function(){ $(document).ready(function(){
initTable(); initTable();
initTree(); initTree();
}) })
.on('click', '.btn-del', function () { .on('click', '.btn-del', function () {
var $this = $(this); var $this = $(this);
...@@ -279,6 +291,28 @@ $(document).ready(function(){ ...@@ -279,6 +291,28 @@ $(document).ready(function(){
} }
} }
}).on('click', '#permission_list_table_filter input', function (e) {
e.preventDefault();
e.stopPropagation();
var position = $('#permission_list_table_filter input').offset();
var y = position['top'];
var x = position['left'];
x -= 220;
y += 30;
$('.search-help').css({"top":y+"px", "left":x+"px", "position": "absolute"});
$('.dropdown-menu.search-help').show();
}).on('click', '.search-item', function (e) {
e.preventDefault();
e.stopPropagation();
var value = $(this).data('value');
var old_value = $('#permission_list_table_filter input').val();
var new_value = old_value + ' ' + value + ':';
$('#permission_list_table_filter input').val(new_value.trim());
$('.dropdown-menu.search-help').hide();
$('#permission_list_table_filter input').focus()
}).on('click', 'body', function (e) {
$('.dropdown-menu.search-help').hide()
}) })
</script> </script>
......
...@@ -478,7 +478,7 @@ jumpserver.initServerSideDataTable = function (options) { ...@@ -478,7 +478,7 @@ jumpserver.initServerSideDataTable = function (options) {
url: options.ajax_url , url: options.ajax_url ,
data: function (data) { data: function (data) {
delete data.columns; delete data.columns;
if (data.length !== null ){ if (data.length !== null){
data.limit = data.length; data.limit = data.length;
delete data.length; delete data.length;
} }
...@@ -525,7 +525,7 @@ jumpserver.initServerSideDataTable = function (options) { ...@@ -525,7 +525,7 @@ jumpserver.initServerSideDataTable = function (options) {
columns: options.columns || [], columns: options.columns || [],
select: options.select || select, select: options.select || select,
language: jumpserver.language, language: jumpserver.language,
lengthMenu: [[10, 15, 25, 50], [10, 15, 25, 50]] lengthMenu: [[15, 25, 50, 9999], [15, 25, 50, 'All']]
}); });
table.selected = []; table.selected = [];
table.selected_rows = []; table.selected_rows = [];
......
...@@ -17,13 +17,13 @@ class UserCreation: ...@@ -17,13 +17,13 @@ class UserCreation:
self.domain = domain self.domain = domain
def auth(self): def auth(self):
url = "{}/api/users/v1/token/".format(self.domain) url = "{}/api/users/v1/auth/".format(self.domain)
data = {"username": self.username, "password": self.password} data = {"username": self.username, "password": self.password}
resp = requests.post(url, data=data) resp = requests.post(url, data=data)
if resp.status_code == 200: if resp.status_code == 200:
data = resp.json() data = resp.json()
self.headers.update({ self.headers.update({
'Authorization': '{} {}'.format(data['Keyword'], data['Token']) 'Authorization': '{} {}'.format('Bearer', data['token'])
}) })
else: else:
print("用户名 或 密码 或 地址 不对") print("用户名 或 密码 或 地址 不对")
......
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