diff --git a/apps/assets/models/asset.py b/apps/assets/models/asset.py index 905cee012..1bbc2ddd7 100644 --- a/apps/assets/models/asset.py +++ b/apps/assets/models/asset.py @@ -87,7 +87,7 @@ class Asset(models.Model): comment = models.TextField(max_length=128, default='', blank=True, verbose_name=_('Comment')) def __str__(self): - return '%s <%s: %s>' % (self.hostname, self.ip, self.port) + return self.hostname @property def is_valid(self): diff --git a/apps/assets/models/user.py b/apps/assets/models/user.py index c9db83e27..56651b4eb 100644 --- a/apps/assets/models/user.py +++ b/apps/assets/models/user.py @@ -198,7 +198,7 @@ class SystemUser(AssetUser): ('P', 'Password'), ('K', 'Public key'), ) - cluster = models.ManyToManyField('assets.Cluster', null=True, blank=True, verbose_name=_("Cluster")) + cluster = models.ManyToManyField('assets.Cluster', blank=True, verbose_name=_("Cluster")) priority = models.IntegerField(default=10, verbose_name=_("Priority")) # Todo: If user granted more priority user, default will be login as the hign protocol = models.CharField(max_length=16, choices=PROTOCOL_CHOICES, default='ssh', verbose_name=_('Protocol')) auto_push = models.BooleanField(default=True, verbose_name=_('Auto push')) diff --git a/apps/assets/templates/assets/admin_user_detail.html b/apps/assets/templates/assets/admin_user_detail.html index 577f16402..b5eb5da05 100644 --- a/apps/assets/templates/assets/admin_user_detail.html +++ b/apps/assets/templates/assets/admin_user_detail.html @@ -123,24 +123,6 @@ {% endblock %} {% block custom_foot_js %} + $('.select2.group').select2() + .on('select2:select', function(evt) { + var data = evt.params.data; + jumpserver.groups_selected[data.id] = data.text; + }) + .on('select2:unselect', function(evt) { + var data = evt.params.data; + delete jumpserver.groups_selected[data.id] + }) +}) +.on('click', '.btn-add-assets', function () { + if (Object.keys(jumpserver.assets_selected).length === 0) { + return false; + } + var assets = []; + $.map(jumpserver.assets_selected, function(value, index) { + assets.push(index); + }); + addAssets(assets); +}) +.on('click', '.btn-remove-asset', function () { + var asset_id = $(this).data("gid"); + if (asset_id === "") { + return + } + var assets = [asset_id]; + removeAssets(assets) +}) +.on('click', '#btn-add-group', function () { + if (Object.keys(jumpserver.groups_selected).length === 0) { + return false; + } + + var groups = $('.bdg_group').map(function() { + return $(this).data('gid'); + }).get(); + + $.map(jumpserver.groups_selected, function(group_name, index) { + groups.push(index); + $('#opt_' + index).remove(); + $('.group_edit tbody').append( + '' + + '' + group_name + '' + + '' + + '' + ) + }); + + updateGroup(groups); +}) +.on('click', '.btn-remove-group', function () { + var $this = $(this); + var $tr = $this.closest('tr'); + var groups = $('.bdg_group').map(function() { + if ($(this).data('gid') !== $this.data('gid')){ + return $(this).data('gid'); + } + }).get(); + updateGroup(groups); + $tr.remove() +}) + {% endblock %} diff --git a/apps/perms/templates/perms/asset_permission_detail.html b/apps/perms/templates/perms/asset_permission_detail.html index c69909467..5b945a900 100644 --- a/apps/perms/templates/perms/asset_permission_detail.html +++ b/apps/perms/templates/perms/asset_permission_detail.html @@ -126,23 +126,6 @@ - - {% trans 'Retest asset connectivity' %}: - - - - - - - - - {% trans 'Repush system user' %}: - - - - - - @@ -153,21 +136,21 @@ {% trans 'System user' %}
- +
@@ -176,7 +159,7 @@ {% endfor %} @@ -190,80 +173,70 @@ - {% endblock %} {% block custom_foot_js %} - + .on('select2:unselect', function(evt) { + var data = evt.params.data; + delete jumpserver.system_users_selected[data.id] + }) +}) +.on('click', '.btn-delete-perm', function () { + var $this = $(this); + var name = "{{ asset_permission.name }}"; + var uid = "{{ asset_permission.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); +}) +.on('click', '#btn-add-system-user', function () { + if (Object.keys(jumpserver.system_users_selected).length === 0) { + return false; + } + var system_users = $('.bdg-system-user').map(function() { + return $(this).data('uid'); + }).get(); + + $.map(jumpserver.system_users_selected, function(name, index) { + system_users.push(index); + $('#opt_' + index).remove(); + $('.group_edit tbody').append( + '' + + '' + + '' + + '' + ) + }); + updateSystemUser(system_users); +}).on('click', '.btn-remove-user', function () { + var $this = $(this); + var $tr = $this.closest('tr'); + var system_users = $('.bdg-system-user').map(function() { + if ($(this).data('uid') !== $this.data('uid')){ + return $(this).data('uid'); + } + }).get(); + updateSystemUser(system_users); + $tr.remove() +}) + {% endblock %} diff --git a/apps/perms/templates/perms/asset_permission_list.html b/apps/perms/templates/perms/asset_permission_list.html index ab76dee88..71861702b 100644 --- a/apps/perms/templates/perms/asset_permission_list.html +++ b/apps/perms/templates/perms/asset_permission_list.html @@ -1,69 +1,96 @@ {% extends '_base_list.html' %} {% load i18n %} -{% block content_left_head %} - + +{% block table_search %} +{% endblock %} + +{% block table_container %} +
+ {% trans "Create permission" %} -{% endblock %} - -{% block table_head %} -
- - - - - - - -{% endblock %} - -{% block table_body %} - {% for asset_permission in asset_permission_list %} - - - - - - - - - - - {% endfor %} + +
- +
{{ system_user.name }} - +
' + name + '
{% trans 'Name' %}{% trans 'User' %}{% trans 'User group' %}{% trans 'Asset' %}{% trans 'Asset group' %}{% trans 'System user' %}{% trans 'Is valid' %}{% trans 'Action' %}
- - {{ asset_permission.name }} - - {{ asset_permission.users.count }}{{ asset_permission.user_groups.count }}{{ asset_permission.assets.count }}{{ asset_permission.asset_groups.count }}{{ asset_permission.system_users.count }} - {% if asset_permission.is_valid %} - - {% else %} - - {% endif %} - - {% trans 'Update' %} - - {% trans 'Delete' %} - -
+ + + + + + + + + + + + + + + +
+ + {% trans 'Name' %}{% trans 'User' %}{% trans 'User group' %}{% trans 'Asset' %}{% trans 'Asset group' %}{% trans 'System user' %}{% trans 'Is valid' %}{% trans 'Action' %}
{% endblock %} {% block custom_foot_js %} - + {% endblock %} diff --git a/apps/perms/templates/perms/asset_permission_user.html b/apps/perms/templates/perms/asset_permission_user.html index f91682c83..8f24cbeea 100644 --- a/apps/perms/templates/perms/asset_permission_user.html +++ b/apps/perms/templates/perms/asset_permission_user.html @@ -26,16 +26,6 @@ {% trans 'Assets and asset groups' %} -
@@ -83,7 +73,7 @@ - + {% endfor %} @@ -108,7 +98,7 @@ @@ -143,16 +133,16 @@ - + {% for user_group in user_groups %} - {{ user_group.name }} + {{ user_group.name }} - + {% endfor %} @@ -169,30 +159,118 @@ {% endblock %} {% block custom_foot_js %} - + .on('select2:unselect', function(evt) { + var data = evt.params.data; + delete jumpserver.users_selected[data.id] + }); + $('.select2.user-group').select2() + .on('select2:select', function(evt) { + var data = evt.params.data; + jumpserver.groups_selected[data.id] = data.text; + }) + .on('select2:unselect', function(evt) { + var data = evt.params.data; + delete jumpserver.groups_selected[data.id] + }) +}).on('click', '.btn-add-user', function () { + if (Object.keys(jumpserver.users_selected).length === 0) { + return false; + } + var users_id = []; + $.map(jumpserver.users_selected, function(value, index) { + users_id.push(index); + }); + console.log(users_id); + addUsers(users_id); +}).on('click', '.btn-remove-user', function () { + var user_id = $(this).data("gid"); + if (user_id === "") { + return + } + var users = [user_id]; + removeUser(users) +}).on('click', '#btn-add-group', function () { + if (Object.keys(jumpserver.groups_selected).length === 0) { + return false; + } + + var groups = $('.bdg_group').map(function() { + return $(this).data('gid'); + }).get(); + + $.map(jumpserver.groups_selected, function(group_name, index) { + groups.push(index); + $('#opt_' + index).remove(); + $('.group_edit tbody').append( + '' + + '' + group_name + '' + + '' + + '' + ) + }); + + updateGroup(groups); +}).on('click', '.btn-remove-group', function () { + var $this = $(this); + var $tr = $this.closest('tr'); + var groups = $('.bdg_group').map(function() { + if ($(this).data('gid') !== $this.data('gid')){ + return $(this).data('gid'); + } + }).get(); + updateGroup(groups); + $tr.remove() +}) + {% endblock %} diff --git a/apps/perms/urls/api_urls.py b/apps/perms/urls/api_urls.py index 2c5df54d4..f59bb178e 100644 --- a/apps/perms/urls/api_urls.py +++ b/apps/perms/urls/api_urls.py @@ -19,41 +19,22 @@ urlpatterns = [ url(r'^v1/user/my/asset-group/(?P[0-9a-zA-Z\-]{36})/assets/$', api.MyAssetGroupOfAssetsApi.as_view(), name='my-asset-group-of-assets'), # 查询某个用户授权的资产和资产组 - url(r'^v1/user/(?P[0-9a-zA-Z\-]{36})/assets/$', - api.UserGrantedAssetsApi.as_view(), - name='user-assets'), - url(r'^v1/user/(?P[0-9a-zA-Z\-]{36})/asset-groups/$', - api.UserGrantedAssetGroupsApi.as_view(), - name='user-asset-groups'), - url(r'^v1/user/(?P[0-9a-zA-Z\-]{36})/asset-groups-assets/$', - api.UserGrantedAssetGroupsWithAssetsApi.as_view(), - name='user-asset-groups'), + url(r'^v1/user/(?P[0-9a-zA-Z\-]{36})/assets/$', api.UserGrantedAssetsApi.as_view(), name='user-assets'), + url(r'^v1/user/(?P[0-9a-zA-Z\-]{36})/asset-groups/$', api.UserGrantedAssetGroupsApi.as_view(), name='user-asset-groups'), + url(r'^v1/user/(?P[0-9a-zA-Z\-]{36})/asset-groups-assets/$', api.UserGrantedAssetGroupsWithAssetsApi.as_view(), name='user-asset-groups'), # 查询某个用户组授权的资产和资产组 - url(r'^v1/user-group/(?P[0-9a-zA-Z\-]{36})/assets/$', - api.UserGroupGrantedAssetsApi.as_view(), - name='user-group-assets'), - url(r'^v1/user-group/(?P[0-9a-zA-Z\-]{36})/asset-groups/$', - api.UserGroupGrantedAssetGroupsApi.as_view(), - name='user-group-asset-groups'), + url(r'^v1/user-group/(?P[0-9a-zA-Z\-]{36})/assets/$', api.UserGroupGrantedAssetsApi.as_view(), name='user-group-assets'), + url(r'^v1/user-group/(?P[0-9a-zA-Z\-]{36})/asset-groups/$', api.UserGroupGrantedAssetGroupsApi.as_view(), name='user-group-asset-groups'), - # 回收用户或用户组授权 - url(r'^v1/asset-permissions/user/revoke/$', - api.RevokeUserAssetPermission.as_view(), - name='revoke-user-asset-permission'), - url(r'^v1/asset-permissions/user-group/revoke/$', - api.RevokeUserGroupAssetPermission.as_view(), - name='revoke-user-group-asset-permission'), + # 用户和资产授权变更 + url(r'^v1/asset-permissions/(?P[0-9a-zA-Z\-]{36})/user/remove/$', api.AssetPermissionRemoveUserApi.as_view(), name='asset-permission-remove-user'), + url(r'^v1/asset-permissions/(?P[0-9a-zA-Z\-]{36})/user/add/$', api.AssetPermissionAddUserApi.as_view(), name='asset-permission-add-user'), + url(r'^v1/asset-permissions/(?P[0-9a-zA-Z\-]{36})/asset/remove/$', api.AssetPermissionRemoveAssetApi.as_view(), name='asset-permission-remove-asset'), + url(r'^v1/asset-permissions/(?P[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'), - - # 删除asset permission中的某个系统用户 - url(r'^v1/asset-permissions/(?P[0-9a-zA-Z\-]{36})/system-user/remove/$', - api.RemoveSystemUserAssetPermission.as_view(), - name='remove-system-user-asset-permission'), + url(r'v1/asset-permission/user/validate/$', api.ValidateUserAssetPermissionView.as_view(), name='validate-user-asset-permission'), ] urlpatterns += router.urls diff --git a/apps/perms/utils.py b/apps/perms/utils.py index edbbe6dc9..8f494f2f8 100644 --- a/apps/perms/utils.py +++ b/apps/perms/utils.py @@ -179,31 +179,3 @@ def push_system_user(assets, system_user): system_user = system_user._to_secret_json() task = push_users.delay(assets, system_user) return task.id - - -def associate_system_users_and_assets(system_users, assets, asset_groups, force=False): - """关联系统用户和资产, 目的是保存它们的关系, 然后新加入的资产或系统 - 用户时,推送系统用户到资产 - - Todo: 这里需要最终Api定下来更改一下, 现在策略是以系统用户为核心推送, 一个系统用户 - 推送一次 - """ - assets_all = set(assets) - - for asset_group in asset_groups: - assets_all |= set(asset_group.assets.all()) - - for system_user in system_users: - assets_need_push = [] - if system_user.auto_push: - if force: - assets_need_push = assets_all - else: - assets_need_push.extend( - [asset for asset in assets_all - if asset not in system_user.assets.all() - ] - ) - system_user.assets.add(*(tuple(assets_all))) - push_system_user(assets_need_push, system_user) - diff --git a/apps/perms/views.py b/apps/perms/views.py index 3063488d2..9875b1366 100644 --- a/apps/perms/views.py +++ b/apps/perms/views.py @@ -2,24 +2,19 @@ from __future__ import unicode_literals, absolute_import -import functools - from django.utils.translation import ugettext as _ -from django.db import transaction from django.conf import settings -from django.db.models import Q from django.views.generic import ListView, CreateView, UpdateView from django.views.generic.edit import DeleteView, FormView from django.urls import reverse_lazy from django.contrib.messages.views import SuccessMessageMixin from django.views.generic.detail import DetailView, SingleObjectMixin +from django.contrib import messages -from common.utils import search_object_attr from .hands import AdminUserRequiredMixin, User, UserGroup, SystemUser, \ Asset, AssetGroup from .models import AssetPermission from .forms import AssetPermissionForm -# from .utils import associate_system_users_and_assets class AssetPermissionListView(AdminUserRequiredMixin, ListView): @@ -32,52 +27,34 @@ class AssetPermissionListView(AdminUserRequiredMixin, ListView): context = { 'app': _('Perms'), 'action': _('Asset permission list'), - 'keyword': self.keyword, } kwargs.update(context) - return super(AssetPermissionListView, self).get_context_data(**kwargs) - - def get_queryset(self): - self.queryset = super(AssetPermissionListView, self).get_queryset() - self.keyword = keyword = self.request.GET.get('keyword', '') - self.sort = sort = self.request.GET.get('sort', '-date_created') - - if keyword: - self.queryset = self.queryset\ - .filter(Q(users__name__contains=keyword) | - Q(users__username__contains=keyword) | - Q(user_groups__name__contains=keyword) | - Q(assets__ip__contains=keyword) | - Q(assets__hostname__contains=keyword) | - Q(system_users__username__icontains=keyword) | - Q(system_users__name__icontains=keyword) | - Q(asset_groups__name__icontains=keyword) | - Q(comment__icontains=keyword) | - Q(name__icontains=keyword)).distinct() - if sort: - self.queryset = self.queryset.order_by(sort) - return self.queryset + return super().get_context_data(**kwargs) -class AssetPermissionCreateView(AdminUserRequiredMixin, - SuccessMessageMixin, - CreateView): - model = AssetPermission - form_class = AssetPermissionForm - template_name = 'perms/asset_permission_create_update.html' - success_url = reverse_lazy('perms:asset-permission-list') +class MessageMixin: + def form_valid(self, form): + response = super().form_valid(form) + errors = self.object.check_system_user_in_assets() + if errors: + message = self.get_warning_messages(errors) + messages.warning(self.request, message) + else: + message = self.get_success_message(form.cleaned_data) + messages.success(self.request, message) - @transaction.atomic - def post(self, request, *args, **kwargs): - return super(AssetPermissionCreateView, self).post(request, *args, **kwargs) + success_message = self.get_success_message(form.cleaned_data) + if success_message: + messages.success(self.request, success_message) + return response - def get_context_data(self, **kwargs): - context = { - 'app': _('Perms'), - 'action': _('Create asset permission'), - } - kwargs.update(context) - return super(AssetPermissionCreateView, self).get_context_data(**kwargs) + @staticmethod + def get_warning_messages(errors): + message = "System user should in behind clusters, so that " \ + "system user auto push to cluster assets
" + for system_user, clusters in errors: + message += "{}: {} ".format(system_user.name, ", ".join(list(clusters))) + return message def get_success_message(self, cleaned_data): url = reverse_lazy('perms:asset-permission-detail', @@ -87,24 +64,29 @@ class AssetPermissionCreateView(AdminUserRequiredMixin, 'successfully.'.format(url=url, name=self.object.name)) return success_message - # Todo: When create push system user - # def form_valid(self, form): - # assets = form.cleaned_data['assets'] - # asset_groups = form.cleaned_data['asset_groups'] - # system_users = form.cleaned_data['system_users'] - # response = super(AssetPermissionCreateView, self).form_valid(form) - # self.object.created_by = self.request.user.name - # self.object.save() - # return response - -class AssetPermissionUpdateView(AdminUserRequiredMixin, UpdateView): +class AssetPermissionCreateView(AdminUserRequiredMixin, + MessageMixin, + CreateView): + model = AssetPermission + form_class = AssetPermissionForm + template_name = 'perms/asset_permission_create_update.html' + success_url = reverse_lazy('perms:asset-permission-list') + warning = None + + def get_context_data(self, **kwargs): + context = { + 'app': _('Perms'), + 'action': _('Create asset permission'), + } + kwargs.update(context) + return super().get_context_data(**kwargs) + + +class AssetPermissionUpdateView(AdminUserRequiredMixin, MessageMixin, UpdateView): model = AssetPermission form_class = AssetPermissionForm template_name = 'perms/asset_permission_create_update.html' - success_message = _( - 'Update asset permission {name} successfully.' - ) success_url = reverse_lazy("perms:asset-permission-list") def get_context_data(self, **kwargs): @@ -113,14 +95,7 @@ class AssetPermissionUpdateView(AdminUserRequiredMixin, UpdateView): 'action': _('Update asset permission') } kwargs.update(context) - return super(AssetPermissionUpdateView, self).get_context_data(**kwargs) - - def get_success_message(self): - url = reverse_lazy('perms:asset-permission-detail', - kwargs={'pk': self.object.pk}) - return self.success_message.format( - url=url, name=self.object.name - ) + return super().get_context_data(**kwargs) class AssetPermissionDetailView(AdminUserRequiredMixin, DetailView): @@ -138,7 +113,7 @@ class AssetPermissionDetailView(AdminUserRequiredMixin, DetailView): 'system_users': self.object.system_users.all(), } kwargs.update(context) - return super(AssetPermissionDetailView, self).get_context_data(**kwargs) + return super().get_context_data(**kwargs) class AssetPermissionDeleteView(AdminUserRequiredMixin, DeleteView): @@ -153,40 +128,28 @@ class AssetPermissionUserView(AdminUserRequiredMixin, 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()) - self.keyword = self.request.GET.get('keyword', '') - return super(AssetPermissionUserView, self).get(request, *args, **kwargs) + return super().get(request, *args, **kwargs) def get_queryset(self): queryset = self.object.get_granted_users() - if self.keyword: - search_func = functools.partial( - search_object_attr, - value=self.keyword, - attr_list=['username', 'name', 'email'], - ignore_case=True) - queryset = filter(search_func, queryset) return queryset def get_context_data(self, **kwargs): users_granted = self.get_queryset() - user_groups_granted = self.object.user_groups.all() + groups_granted = self.object.user_groups.all() context = { 'app': _('Perms'), 'action': _('Asset permission user list'), - 'users_remain': [ - user for user in User.objects.all() - if user not in users_granted], + 'users_remain': User.objects.exclude(id__in=[user.id for user in users_granted]), 'user_groups': self.object.user_groups.all(), - 'user_groups_remain': [ - user_group for user_group in UserGroup.objects.all() - if user_group not in user_groups_granted], - 'keyword': self.keyword, + 'user_groups_remain': UserGroup.objects.exclude(id__in=[group.id for group in groups_granted]) } kwargs.update(context) - return super(AssetPermissionUserView, self).get_context_data(**kwargs) + return super().get_context_data(**kwargs) class AssetPermissionAssetView(AdminUserRequiredMixin, @@ -195,37 +158,25 @@ class AssetPermissionAssetView(AdminUserRequiredMixin, 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()) - self.keyword = self.request.GET.get('keyword', '') - return super(AssetPermissionAssetView, self)\ - .get(request, *args, **kwargs) + return super().get(request, *args, **kwargs) def get_queryset(self): queryset = self.object.get_granted_assets() - if self.keyword: - search_func = functools.partial( - search_object_attr, value=self.keyword, - attr_list=['hostname', 'ip'], - ignore_case=True) - queryset = filter(search_func, queryset) return queryset def get_context_data(self, **kwargs): assets_granted = self.get_queryset() - asset_groups_granted = self.object.user_groups.all() + groups_granted = self.object.asset_groups.all() context = { 'app': _('Perms'), 'action': _('Asset permission asset list'), - 'assets_remain': [ - asset for asset in Asset.objects.all() - if asset not in assets_granted], + 'assets_remain': Asset.objects.exclude(id__in=[asset.id for asset in assets_granted]), 'asset_groups': self.object.asset_groups.all(), - 'asset_groups_remain': [ - asset_group for asset_group in AssetGroup.objects.all() - if asset_group not in asset_groups_granted], - 'keyword': self.keyword, + 'asset_groups_remain': AssetGroup.objects.exclude(id__in=[group.id for group in groups_granted]) } kwargs.update(context) - return super(AssetPermissionAssetView, self).get_context_data(**kwargs) + return super().get_context_data(**kwargs)