From b63999f3859c8c8b269b68e7e16093bc509453f9 Mon Sep 17 00:00:00 2001 From: BaiJiangjie Date: Tue, 3 Apr 2018 16:28:58 +0800 Subject: [PATCH 01/13] =?UTF-8?q?=E5=88=9B=E5=BB=BA=E7=94=A8=E6=88=B7?= =?UTF-8?q?=E5=8F=91=E9=80=81=E9=82=AE=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/users/signals_handler.py | 30 ++++++++++++++++++++---------- apps/users/views/user.py | 1 + 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/apps/users/signals_handler.py b/apps/users/signals_handler.py index 9e1abf350..4c6afc663 100644 --- a/apps/users/signals_handler.py +++ b/apps/users/signals_handler.py @@ -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) diff --git a/apps/users/views/user.py b/apps/users/views/user.py index 84c670370..1d68eb81d 100644 --- a/apps/users/views/user.py +++ b/apps/users/views/user.py @@ -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) From d0ede246e786bfb209ef39d0a4cf9282cbb737ae Mon Sep 17 00:00:00 2001 From: ibuler Date: Sun, 8 Apr 2018 00:16:37 +0800 Subject: [PATCH 02/13] =?UTF-8?q?[Update]=20=E4=BF=AE=E6=94=B9=E6=8E=88?= =?UTF-8?q?=E6=9D=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/assets/models/asset.py | 4 + apps/assets/signals_handler.py | 2 +- apps/common/utils.py | 8 + apps/perms/forms.py | 31 +- apps/perms/models.py | 66 +++- .../perms/asset_permission_create_update.html | 41 +- .../perms/asset_permission_list.html | 364 +++++++----------- apps/perms/utils.py | 173 ++++++++- apps/perms/views.py | 70 +++- .../templates/terminal/command_list.html | 4 +- apps/users/forms.py | 49 ++- 11 files changed, 494 insertions(+), 318 deletions(-) diff --git a/apps/assets/models/asset.py b/apps/assets/models/asset.py index 4aaa0287d..1f42a2978 100644 --- a/apps/assets/models/asset.py +++ b/apps/assets/models/asset.py @@ -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: diff --git a/apps/assets/signals_handler.py b/apps/assets/signals_handler.py index fe5508720..b530067a7 100644 --- a/apps/assets/signals_handler.py +++ b/apps/assets/signals_handler.py @@ -31,7 +31,7 @@ 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) + # set_asset_root_node(instance) if created: logger.info("Asset `{}` create signal received".format(instance)) update_asset_hardware_info_on_created(instance) diff --git a/apps/common/utils.py b/apps/common/utils.py index b4dd0aef8..c6f5398cc 100644 --- a/apps/common/utils.py +++ b/apps/common/utils.py @@ -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类型。 diff --git a/apps/perms/forms.py b/apps/perms/forms.py index f84e56693..3756fb797 100644 --- a/apps/perms/forms.py +++ b/apps/perms/forms.py @@ -4,27 +4,32 @@ from __future__ import absolute_import, unicode_literals from django import forms from django.utils.translation import ugettext_lazy as _ -from .models import NodePermission +from .models import AssetPermission class AssetPermissionForm(forms.ModelForm): 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')} ), } - - def clean_system_user(self): - return self.cleaned_data['system_user'] + labels = { + 'nodes': _("Node"), + } diff --git a/apps/perms/models.py b/apps/perms/models.py index 49825e16d..8263b1174 100644 --- a/apps/perms/models.py +++ b/apps/perms/models.py @@ -7,28 +7,42 @@ from django.utils import timezone from common.utils import date_expired_default +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() + inherit_from = None + 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 @@ -68,6 +82,42 @@ class AssetPermission(models.Model): errors[system_user] = cluster_remain return errors + @property + def users_detail(self): + return " ".join([u.name for u in self.users.all()]) + + @property + def user_groups_detail(self): + return " ".join([g.name for g in self.user_groups.all()]) + + @property + def assets_detail(self): + return " ".join([a.hostname for a in self.assets.all()]) + + @property + def nodes_detail(self): + return " ".join([g.value for g in self.nodes.all()]) + + @property + def system_users_detail(self): + return " ".join([s.name for s in self.system_users.all()]) + + @property + def detail(self): + data = "" + if self.users.all(): + comment = _("User") + users = "{}: ".format(comment) + for u in self.users.all(): + users += u.name + " " + data += users + "
" + if self.assets.all(): + assets = _("Assets: ") + for a in self.assets.all(): + assets += a.hostname + " " + data += assets + "
" + return data + class NodePermission(models.Model): id = models.UUIDField(default=uuid.uuid4, primary_key=True) diff --git a/apps/perms/templates/perms/asset_permission_create_update.html b/apps/perms/templates/perms/asset_permission_create_update.html index d02b354f5..197044856 100644 --- a/apps/perms/templates/perms/asset_permission_create_update.html +++ b/apps/perms/templates/perms/asset_permission_create_update.html @@ -28,23 +28,19 @@
- {% if form.non_field_errors %} -
- {{ form.non_field_errors }} -
- {% endif %}
{% csrf_token %}

{% trans 'Basic' %}

-
- -
- -
-
- {{ form.node }} - {% bootstrap_field form.user_group layout="horizontal" %} - {% bootstrap_field form.system_user layout="horizontal" %} + {% bootstrap_field form.name layout="horizontal" %} +
+

{% trans 'User' %}

+ {% bootstrap_field form.users layout="horizontal" %} + {% bootstrap_field form.user_groups layout="horizontal" %} +
+

{% trans 'Asset' %}

+ {% bootstrap_field form.assets layout="horizontal" %} + {% bootstrap_field form.nodes layout="horizontal" %} + {% bootstrap_field form.system_users layout="horizontal" %}

{% trans 'Other' %}

@@ -53,17 +49,19 @@ {{ form.is_active }}
- -
+
-
+
- + + to +
{{ form.date_expired.errors }} + {{ form.date_start.errors }}
-
+
{% bootstrap_field form.comment layout="horizontal" %}
@@ -84,15 +82,14 @@ {% endblock %} \ No newline at end of file diff --git a/apps/perms/templates/perms/asset_permission_list.html b/apps/perms/templates/perms/asset_permission_list.html index c1a3e6114..84ad0f4f0 100644 --- a/apps/perms/templates/perms/asset_permission_list.html +++ b/apps/perms/templates/perms/asset_permission_list.html @@ -1,238 +1,154 @@ -{% extends 'base.html' %} -{% load static %} +{% extends '_base_list.html' %} {% load i18n %} - +{% load static %} +{% load common_tags %} {% block custom_head_css_js %} - - - - + +{% endblock %} + +{% block content_left_head %} {% endblock %} -{% block content %} -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
- - - - - - - - - - - - - - - -
- - {% trans 'Node' %}{% trans 'User group' %}{% trans 'System user' %}{% trans 'Is active' %}{% trans 'Date expired' %}{% trans 'Action' %}
-
-
-
-
+{% block table_search %} + + +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+ +
+
+ +{% endblock %} +{% block table_container %} + + + + + + + + + + + + + + + + + + + + + {% for object in object_list %} + + + + + + + + + + + + + + + + + {% endfor %} + +
ID{% trans 'Name' %}{% trans 'User' %}{% trans 'User group' %}{% trans 'Asset' %}{% trans 'Node'%}{% trans 'System user' %}{% trans 'Active' %}{% trans 'Action' %}{% trans 'User' %}{% trans 'User group' %}{% trans 'Asset' %}{% trans 'Node' %}{% trans 'System user' %}
{{ forloop.counter }}{{ object.name }}{{ object.users.count }}{{ object.user_groups.count }}{{ object.assets.count }}{{ object.nodes.count }}{{ object.system_users.count }} + {% if object.is_valid %} + + {% else %} + + {% endif %} + + {% trans "Update" %} + {% trans "Delete" %} + {{ object.users_detail }}{{ object.user_groups_detail }}{{ object.assets_detail }}{{ object.nodes_detail }}{{ object.system_users_detail }}
{% endblock %} {% block custom_foot_js %} - + + {% endblock %} + + diff --git a/apps/perms/utils.py b/apps/perms/utils.py index 7abf96352..5947ccefb 100644 --- a/apps/perms/utils.py +++ b/apps/perms/utils.py @@ -2,16 +2,183 @@ 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 AssetPermissionUtils: + + @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_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_node_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 + + + class NodePermissionUtil: @staticmethod diff --git a/apps/perms/views.py b/apps/perms/views.py index 33447db7b..01d621ce4 100644 --- a/apps/perms/views.py +++ b/apps/perms/views.py @@ -6,21 +6,65 @@ from django.utils.translation import ugettext as _ from django.views.generic import ListView, CreateView, UpdateView from django.views.generic.edit import DeleteView from django.urls import reverse_lazy +from django.conf import settings +from django.db.models import Q -from common.utils import get_object_or_none -from .hands import AdminUserRequiredMixin, Node +from .hands import AdminUserRequiredMixin, Node, User, UserGroup, Asset, SystemUser from .models import AssetPermission, NodePermission 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_queryset(self): + self.q = self.request.GET.get('q', '') + self.user = self.request.GET.get("user", '') + self.user_group = self.request.GET.get("user_group", '') + self.asset = self.request.GET.get('asset', '') + self.node = self.request.GET.get('node', '') + self.system_user = self.request.GET.get('system_user', '') + filter_kwargs = dict() + if self.user: + filter_kwargs['users__name'] = self.user + if self.user_group: + filter_kwargs['user_groups__name'] = self.user_group + if self.asset: + filter_kwargs['assets__hostname'] = self.asset + if self.node: + filter_kwargs['nodes__value'] = self.node + if self.system_user: + filter_kwargs['system_users__name'] = self.system_user + queryset = self.model.objects.filter(**filter_kwargs) + if self.q: + queryset = queryset.filter( + Q(name__contains=self.q) | + Q(users__name=self.q) | + Q(user_groups__name=self.q) | + Q(assets__hostname=self.q) | + Q(nodes__value=self.q) | + Q(system_users__name=self.q) + ) + queryset = queryset.order_by('-date_start') + return queryset def get_context_data(self, **kwargs): context = { 'app': _('Perms'), + 'user_list': User.objects.all().values_list('name', flat=True), + 'user_group_list': UserGroup.objects.all().values_list('name', flat=True), + 'asset_list': Asset.objects.all().values_list('hostname', flat=True), + 'node_list': Node.objects.all().values_list('value', flat=True), + 'system_user_list': SystemUser.objects.all().values_list('name', flat=True), + 'user': self.user, + 'user_group': self.user_group, + 'asset': self.asset, + 'node': self.node, + 'system_user': self.system_user, + 'q': self.q, 'action': _('Asset permission list'), } kwargs.update(context) @@ -28,20 +72,11 @@ 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 - return form - def get_context_data(self, **kwargs): context = { 'app': _('Perms'), @@ -52,16 +87,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'), diff --git a/apps/terminal/templates/terminal/command_list.html b/apps/terminal/templates/terminal/command_list.html index c8e4cf6ac..6b55d787e 100644 --- a/apps/terminal/templates/terminal/command_list.html +++ b/apps/terminal/templates/terminal/command_list.html @@ -18,7 +18,7 @@ {% endblock %} {% block table_search %} -
+
@@ -64,7 +64,7 @@ {% endblock %} {% block table_container %} - +
diff --git a/apps/users/forms.py b/apps/users/forms.py index f7c8be563..cbb9beaf5 100644 --- a/apps/users/forms.py +++ b/apps/users/forms.py @@ -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): From fffa0def9ed5880865ef8d653d09932816962ddc Mon Sep 17 00:00:00 2001 From: ibuler Date: Sun, 8 Apr 2018 20:02:40 +0800 Subject: [PATCH 03/13] =?UTF-8?q?[Update]=20=E4=BF=AE=E6=94=B9api=E5=92=8C?= =?UTF-8?q?view?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/assets/forms/asset.py | 16 ++- apps/assets/models/user.py | 12 +-- apps/assets/serializers/system_user.py | 2 +- apps/assets/signals_handler.py | 43 +++++--- apps/assets/tasks.py | 49 ++-------- .../assets/templates/assets/asset_create.html | 2 +- .../assets/templates/assets/asset_update.html | 2 +- apps/perms/api.py | 97 ++++++++++++------- apps/perms/serializers.py | 29 +----- apps/perms/signals_handler.py | 38 ++++++-- .../perms/asset_permission_list.html | 11 ++- apps/perms/urls/api_urls.py | 3 + apps/perms/utils.py | 26 ++++- .../templates/users/user_granted_asset.html | 29 +++--- .../users/user_group_granted_asset.html | 25 +++-- 15 files changed, 211 insertions(+), 173 deletions(-) diff --git a/apps/assets/forms/asset.py b/apps/assets/forms/asset.py index a6f488761..f8f187b4d 100644 --- a/apps/assets/forms/asset.py +++ b/apps/assets/forms/asset.py @@ -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')} ), } diff --git a/apps/assets/models/user.py b/apps/assets/models/user.py index 541ef8b6a..2326f1c46 100644 --- a/apps/assets/models/user.py +++ b/apps/assets/models/user.py @@ -3,6 +3,7 @@ # import logging +import uuid from django.core.cache import cache from django.db import models @@ -100,10 +101,11 @@ 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): @@ -119,9 +121,8 @@ class SystemUser(AssetUser): 'auto_push': self.auto_push, } - @property - def assets(self): - assets = set() + def get_assets(self): + assets = set(self.assets.all()) for node in self.nodes.all(): assets.update(set(node.get_all_assets())) return assets @@ -168,6 +169,3 @@ class SystemUser(AssetUser): except IntegrityError: print('Error continue') continue - - - diff --git a/apps/assets/serializers/system_user.py b/apps/assets/serializers/system_user.py index 1dff79422..7abd09d29 100644 --- a/apps/assets/serializers/system_user.py +++ b/apps/assets/serializers/system_user.py @@ -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): diff --git a/apps/assets/signals_handler.py b/apps/assets/signals_handler.py index b530067a7..06cd9f63e 100644 --- a/apps/assets/signals_handler.py +++ b/apps/assets/signals_handler.py @@ -1,14 +1,13 @@ # -*- 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": - for pk in kwargs['pk_set']: - node = kwargs['model'].objects.get(pk=pk) - push_system_user_to_node(instance, node) + 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": + 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)) diff --git a/apps/assets/tasks.py b/apps/assets/tasks.py index 53a54764d..b51798e3d 100644 --- a/apps/assets/tasks.py +++ b/apps/assets/tasks.py @@ -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 diff --git a/apps/assets/templates/assets/asset_create.html b/apps/assets/templates/assets/asset_create.html index 7f01e0530..eca0f6a03 100644 --- a/apps/assets/templates/assets/asset_create.html +++ b/apps/assets/templates/assets/asset_create.html @@ -34,7 +34,7 @@
- {% for name, labels in form.labels.field.queryset|group_labels %} {% for label in labels %} diff --git a/apps/assets/templates/assets/asset_update.html b/apps/assets/templates/assets/asset_update.html index 3e96438bc..3d42ca2b5 100644 --- a/apps/assets/templates/assets/asset_update.html +++ b/apps/assets/templates/assets/asset_update.html @@ -39,7 +39,7 @@
- {% for name, labels in form.labels.field.queryset|group_labels %} {% for label in labels %} diff --git a/apps/perms/api.py b/apps/perms/api.py index 6b0b15f76..0ec3a7980 100644 --- a/apps/perms/api.py +++ b/apps/perms/api.py @@ -7,9 +7,9 @@ from rest_framework.generics import ListAPIView, get_object_or_404 from rest_framework import viewsets 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 +18,7 @@ class AssetPermissionViewSet(viewsets.ModelViewSet): """ 资产授权列表的增删改查api """ - queryset = NodePermission.objects.all() + queryset = AssetPermission.objects.all() serializer_class = serializers.AssetPermissionCreateUpdateSerializer permission_classes = (IsSuperUser,) @@ -27,15 +27,6 @@ class AssetPermissionViewSet(viewsets.ModelViewSet): return serializers.AssetPermissionListSerializer return self.serializer_class - def get_queryset(self): - queryset = super().get_queryset() - node_id = self.request.query_params.get('node_id') - - if node_id: - queryset = queryset.filter(node__id=node_id) - - return queryset - class UserGrantedAssetsApi(ListAPIView): """ @@ -53,7 +44,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,14 +69,14 @@ 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): """ 授权用户的资产组,注:这里的资产组并非是授权列表中授权的, - 而是把所有资产取出来,然后反查出所有资产组,然后合并得到, + 而是把所有资产取出来,然后反查出所有节点,然后合并得到, 结果里也包含资产组下授权的资产 数据结构如下: [ @@ -121,18 +112,12 @@ 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 asset, system_users in _assets.items(): + asset.system_users_granted = system_users + node.assets_granted = assets queryset.append(node) return queryset @@ -142,6 +127,26 @@ 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 + + class UserGroupGrantedAssetsApi(ListAPIView): permission_classes = (IsSuperUser,) serializer_class = AssetGrantedSerializer @@ -154,7 +159,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 +176,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 +193,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,7 +233,7 @@ 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: diff --git a/apps/perms/serializers.py b/apps/perms/serializers.py index 6decf663b..22306cad2 100644 --- a/apps/perms/serializers.py +++ b/apps/perms/serializers.py @@ -1,29 +1,19 @@ # -*- 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 class AssetPermissionCreateUpdateSerializer(serializers.ModelSerializer): class Meta: - model = NodePermission - fields = [ - 'id', 'node', 'user_group', 'system_user', - 'is_active', 'date_expired' - ] + model = AssetPermission + exclude = ('id', 'create_by', 'date_created') class AssetPermissionListSerializer(serializers.ModelSerializer): - node = StringIDField(read_only=True) - user_group = StringIDField(read_only=True) - system_user = StringIDField(read_only=True) - class Meta: - model = NodePermission + model = AssetPermission fields = '__all__' @@ -40,14 +30,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 - diff --git a/apps/perms/signals_handler.py b/apps/perms/signals_handler.py index ab16a85e7..e6127f836 100644 --- a/apps/perms/signals_handler.py +++ b/apps/perms/signals_handler.py @@ -1,18 +1,42 @@ # -*- 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)) diff --git a/apps/perms/templates/perms/asset_permission_list.html b/apps/perms/templates/perms/asset_permission_list.html index 84ad0f4f0..6980ba838 100644 --- a/apps/perms/templates/perms/asset_permission_list.html +++ b/apps/perms/templates/perms/asset_permission_list.html @@ -116,7 +116,7 @@
@@ -147,6 +147,15 @@ calendarWeeks: true, autoclose: true }); + }).on('click', '.btn-delete', function () { + var $this = $(this); + var uid = $this.data('uid'); + var the_url = '{% url "api-perms:asset-permission-detail" pk=DEFAULT_PK %}'.replace('{{ DEFAULT_PK }}', uid); + var name = $(this).closest("tr").find(":nth-child(2)").children('a').html(); + objectDelete($this, name, the_url); + setTimeout( function () { + window.reload(); + }, 1000); }); {% endblock %} diff --git a/apps/perms/urls/api_urls.py b/apps/perms/urls/api_urls.py index 78c869b2e..d114e4f0b 100644 --- a/apps/perms/urls/api_urls.py +++ b/apps/perms/urls/api_urls.py @@ -15,6 +15,8 @@ urlpatterns = [ url(r'^v1/user/assets/$', api.UserGrantedAssetsApi.as_view(), name='my-assets'), url(r'^v1/user/(?P[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[0-9a-zA-Z\-]{36})/nodes/(?P[0-9a-zA-Z\-]{36})/assets/$', api.UserGrantedNodeAssetsApi.as_view(), name='user-node-assets'), + url(r'^v1/user/nodes/(?P[0-9a-zA-Z\-]{36})/assets/$', api.UserGrantedNodeAssetsApi.as_view(), name='my-node-assets'), url(r'^v1/user/(?P[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'), @@ -22,6 +24,7 @@ urlpatterns = [ 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})/nodes/$', api.UserGroupGrantedNodesApi.as_view(), name='user-group-nodes'), url(r'^v1/user-group/(?P[0-9a-zA-Z\-]{36})/nodes-assets/$', api.UserGroupGrantedNodesWithAssetsApi.as_view(), name='user-group-nodes-assets'), + url(r'^v1/user-group/(?P[0-9a-zA-Z\-]{36})/nodes/(?P[0-9a-zA-Z\-]{36})/assets/$', api.UserGroupGrantedNodeAssetsApi.as_view(), name='user-group-node-assets'), # 验证用户是否有某个资产和系统用户的权限 url(r'v1/asset-permission/user/validate/$', api.ValidateUserAssetPermissionView.as_view(), name='validate-user-asset-permission'), diff --git a/apps/perms/utils.py b/apps/perms/utils.py index 5947ccefb..81ac4bf05 100644 --- a/apps/perms/utils.py +++ b/apps/perms/utils.py @@ -12,7 +12,7 @@ from .models import AssetPermission logger = get_logger(__file__) -class AssetPermissionUtils: +class AssetPermissionUtil: @staticmethod def get_user_permissions(user): @@ -81,6 +81,23 @@ class AssetPermissionUtils: 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) @@ -142,7 +159,7 @@ class AssetPermissionUtils: return assets @classmethod - def get_user_node_with_assets(cls, user): + def get_user_nodes_with_assets(cls, user): """ :param user: :return: {node: {asset: set(su1, su2)}} @@ -178,8 +195,11 @@ class AssetPermissionUtils: return system_users - +# Abandon class NodePermissionUtil: + """ + + """ @staticmethod def get_user_group_permissions(user_group): diff --git a/apps/users/templates/users/user_granted_asset.html b/apps/users/templates/users/user_granted_asset.html index 92f355d9f..ec807aec5 100644 --- a/apps/users/templates/users/user_granted_asset.html +++ b/apps/users/templates/users/user_granted_asset.html @@ -44,7 +44,7 @@ - + @@ -63,6 +63,8 @@ + + + {% endblock %} -{% block content_left_head %} - -{% endblock %} - -{% block table_search %} - - -
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
-
- -
-
- -{% endblock %} -{% block table_container %} -
ID {% trans "Update" %} - {% trans "Delete" %} + {% trans "Delete" %} {{ object.users_detail }} {{ object.user_groups_detail }}{% trans 'Hostname' %} {% trans 'IP' %} {% trans 'Active' %}{% trans 'Reachable' %}{% trans 'System users' %}
- - - - - - - - - - - - - - - - - - - - {% for object in object_list %} - - - - - - - - - - - - - - - - - {% endfor %} - -
ID{% trans 'Name' %}{% trans 'User' %}{% trans 'User group' %}{% trans 'Asset' %}{% trans 'Node'%}{% trans 'System user' %}{% trans 'Active' %}{% trans 'Action' %}{% trans 'User' %}{% trans 'User group' %}{% trans 'Asset' %}{% trans 'Node' %}{% trans 'System user' %}
{{ forloop.counter }}{{ object.name }}{{ object.users.count }}{{ object.user_groups.count }}{{ object.assets.count }}{{ object.nodes.count }}{{ object.system_users.count }} - {% if object.is_valid %} - - {% else %} - - {% endif %} - - {% trans "Update" %} - {% trans "Delete" %} - {{ object.users_detail }}{{ object.user_groups_detail }}{{ object.assets_detail }}{{ object.nodes_detail }}{{ object.system_users_detail }}
+{% block content %} +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+ + + + + + + + + + + + + + + + + +
{% trans 'Name' %}{% trans 'User' %}{% trans 'User group' %}{% trans 'Asset' %}{% trans 'Node'%}{% trans 'System user' %}{% trans 'Active' %}{% trans 'Action' %}
+
+
+
+
{% endblock %} {% block custom_foot_js %} {% endblock %} - - diff --git a/apps/perms/urls/views_urls.py b/apps/perms/urls/views_urls.py index a4a6e49a5..a1acc570c 100644 --- a/apps/perms/urls/views_urls.py +++ b/apps/perms/urls/views_urls.py @@ -9,7 +9,7 @@ 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[0-9a-zA-Z\-]{36})/update$', views.AssetPermissionUpdateView.as_view(), name='asset-permission-update'), - # url(r'^asset-permission/(?P[0-9a-zA-Z\-]{36})$', views.AssetPermissionDetailView.as_view(),name='asset-permission-detail'), + url(r'^asset-permission/(?P[0-9a-zA-Z\-]{36})$', views.AssetPermissionDetailView.as_view(),name='asset-permission-detail'), url(r'^asset-permission/(?P[0-9a-zA-Z\-]{36})/delete$', views.AssetPermissionDeleteView.as_view(), name='asset-permission-delete'), # url(r'^asset-permission/(?P[0-9a-zA-Z\-]{36})/user$', views.AssetPermissionUserView.as_view(), name='asset-permission-user-list'), # url(r'^asset-permission/(?P[0-9a-zA-Z\-]{36})/asset$', views.AssetPermissionAssetView.as_view(), name='asset-permission-asset-list'), diff --git a/apps/perms/views.py b/apps/perms/views.py index 01d621ce4..11d8112e0 100644 --- a/apps/perms/views.py +++ b/apps/perms/views.py @@ -3,14 +3,14 @@ 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 import ListView, CreateView, UpdateView, DetailView from django.views.generic.edit import DeleteView from django.urls import reverse_lazy from django.conf import settings -from django.db.models import Q -from .hands import AdminUserRequiredMixin, Node, User, UserGroup, Asset, SystemUser -from .models import AssetPermission, NodePermission +from common.utils import get_object_or_none +from .hands import AdminUserRequiredMixin, Node, Asset +from .models import AssetPermission from .forms import AssetPermissionForm @@ -20,51 +20,9 @@ class AssetPermissionListView(AdminUserRequiredMixin, ListView): paginate_by = settings.DISPLAY_PER_PAGE user = user_group = asset = node = system_user = q = "" - def get_queryset(self): - self.q = self.request.GET.get('q', '') - self.user = self.request.GET.get("user", '') - self.user_group = self.request.GET.get("user_group", '') - self.asset = self.request.GET.get('asset', '') - self.node = self.request.GET.get('node', '') - self.system_user = self.request.GET.get('system_user', '') - filter_kwargs = dict() - if self.user: - filter_kwargs['users__name'] = self.user - if self.user_group: - filter_kwargs['user_groups__name'] = self.user_group - if self.asset: - filter_kwargs['assets__hostname'] = self.asset - if self.node: - filter_kwargs['nodes__value'] = self.node - if self.system_user: - filter_kwargs['system_users__name'] = self.system_user - queryset = self.model.objects.filter(**filter_kwargs) - if self.q: - queryset = queryset.filter( - Q(name__contains=self.q) | - Q(users__name=self.q) | - Q(user_groups__name=self.q) | - Q(assets__hostname=self.q) | - Q(nodes__value=self.q) | - Q(system_users__name=self.q) - ) - queryset = queryset.order_by('-date_start') - return queryset - def get_context_data(self, **kwargs): context = { 'app': _('Perms'), - 'user_list': User.objects.all().values_list('name', flat=True), - 'user_group_list': UserGroup.objects.all().values_list('name', flat=True), - 'asset_list': Asset.objects.all().values_list('hostname', flat=True), - 'node_list': Node.objects.all().values_list('value', flat=True), - 'system_user_list': SystemUser.objects.all().values_list('name', flat=True), - 'user': self.user, - 'user_group': self.user_group, - 'asset': self.asset, - 'node': self.node, - 'system_user': self.system_user, - 'q': self.q, 'action': _('Asset permission list'), } kwargs.update(context) @@ -77,6 +35,19 @@ class AssetPermissionCreateView(AdminUserRequiredMixin, CreateView): 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) + nodes_id = self.request.GET.get("nodes").split(",") + assets_id = self.request.GET.get("assets").split(",") + + if nodes_id: + nodes = Node.objects.filter(id__in=nodes_id) + form['nodes'].initial = nodes + if assets_id: + assets = Asset.objects.filter(id__in=assets_id) + form['assets'].initial = assets + return form + def get_context_data(self, **kwargs): context = { 'app': _('Perms'), @@ -101,6 +72,21 @@ 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') + } + kwargs.update(context) + return super().get_context_data(**kwargs) + + class AssetPermissionDeleteView(AdminUserRequiredMixin, DeleteView): model = AssetPermission template_name = 'delete_confirm.html' diff --git a/apps/static/css/plugins/ztree/awesomeStyle/awesome.css b/apps/static/css/plugins/ztree/awesomeStyle/awesome.css index 773e687ef..045c810ea 100644 --- a/apps/static/css/plugins/ztree/awesomeStyle/awesome.css +++ b/apps/static/css/plugins/ztree/awesomeStyle/awesome.css @@ -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; diff --git a/apps/static/css/plugins/ztree/awesomeStyle/awesome.less b/apps/static/css/plugins/ztree/awesomeStyle/awesome.less index 7fe7d4d77..3d1610279 100644 --- a/apps/static/css/plugins/ztree/awesomeStyle/awesome.less +++ b/apps/static/css/plugins/ztree/awesomeStyle/awesome.less @@ -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;} diff --git a/apps/static/js/jumpserver.js b/apps/static/js/jumpserver.js index 1083f3520..f2f8b8f55 100644 --- a/apps/static/js/jumpserver.js +++ b/apps/static/js/jumpserver.js @@ -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 "" + data[1] + "
" +} + + + 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 || [], diff --git a/apps/users/views/login.py b/apps/users/views/login.py index 0b70238c0..21c63f889 100644 --- a/apps/users/views/login.py +++ b/apps/users/views/login.py @@ -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 @@ -228,7 +226,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 From 33c299566a4dbe5e1fa37fbe88e740cc9d5050a6 Mon Sep 17 00:00:00 2001 From: ibuler Date: Tue, 10 Apr 2018 20:45:01 +0800 Subject: [PATCH 06/13] =?UTF-8?q?[Update]=20=E4=BF=AE=E5=A4=8Dbug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/common/utils.py | 15 +++++++++++---- apps/perms/views.py | 8 +++++--- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/apps/common/utils.py b/apps/common/utils.py index c6f5398cc..5de10b129 100644 --- a/apps/common/utils.py +++ b/apps/common/utils.py @@ -358,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(): @@ -386,3 +392,4 @@ class TeeObj: def close(self): self.file_obj.close() + diff --git a/apps/perms/views.py b/apps/perms/views.py index 11d8112e0..2aa1ddd8b 100644 --- a/apps/perms/views.py +++ b/apps/perms/views.py @@ -8,7 +8,7 @@ from django.views.generic.edit import DeleteView from django.urls import reverse_lazy from django.conf import settings -from common.utils import get_object_or_none +from common.utils import is_uuid from .hands import AdminUserRequiredMixin, Node, Asset from .models import AssetPermission from .forms import AssetPermissionForm @@ -37,13 +37,15 @@ class AssetPermissionCreateView(AdminUserRequiredMixin, CreateView): def get_form(self, form_class=None): form = super().get_form(form_class=form_class) - nodes_id = self.request.GET.get("nodes").split(",") - assets_id = self.request.GET.get("assets").split(",") + 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 From ed71e7d2d9af6f937e03cfefebed9c9432d28a14 Mon Sep 17 00:00:00 2001 From: ibuler Date: Tue, 10 Apr 2018 21:02:07 +0800 Subject: [PATCH 07/13] =?UTF-8?q?[Update]=20=E4=BF=AE=E6=94=B9=E7=94=A8?= =?UTF-8?q?=E6=88=B7Opt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/users/models/user.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/apps/users/models/user.py b/apps/users/models/user.py index 0e2e06f39..8ebfaf590 100644 --- a/apps/users/models/user.py +++ b/apps/users/models/user.py @@ -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')) @@ -202,6 +207,14 @@ 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 + + @property + def otp_force_enabled(self): + return self.otp_level == 2 + def to_json(self): return OrderedDict({ 'id': self.id, From 78f4e5a89a995f8db5305be8888d8e5982d21cbd Mon Sep 17 00:00:00 2001 From: ibuler Date: Tue, 10 Apr 2018 21:04:56 +0800 Subject: [PATCH 08/13] =?UTF-8?q?[Update]=20=E4=BF=AE=E6=94=B9=E7=94=A8?= =?UTF-8?q?=E6=88=B7Opt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/users/models/user.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/apps/users/models/user.py b/apps/users/models/user.py index 8ebfaf590..347848b7b 100644 --- a/apps/users/models/user.py +++ b/apps/users/models/user.py @@ -211,6 +211,12 @@ class User(AbstractUser): 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 From bbaa35c77349dcd8582d85bff5534bfe8c4cd323 Mon Sep 17 00:00:00 2001 From: ibuler Date: Wed, 11 Apr 2018 11:34:15 +0800 Subject: [PATCH 09/13] =?UTF-8?q?[Update]=20=E4=BF=AE=E6=94=B9Perms?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/assets/templates/assets/asset_list.html | 4 +- .../templates/assets/user_asset_list.html | 194 +++++++++++++----- apps/perms/api.py | 32 +-- apps/users/models/user.py | 2 +- apps/users/templates/users/user_profile.html | 2 +- apps/users/views/login.py | 2 - 6 files changed, 151 insertions(+), 85 deletions(-) diff --git a/apps/assets/templates/assets/asset_list.html b/apps/assets/templates/assets/asset_list.html index d849d0748..18dc31645 100644 --- a/apps/assets/templates/assets/asset_list.html +++ b/apps/assets/templates/assets/asset_list.html @@ -41,9 +41,9 @@ {% block content %}
-
+
-
+
diff --git a/apps/assets/templates/assets/user_asset_list.html b/apps/assets/templates/assets/user_asset_list.html index 5c9fa2633..649418aed 100644 --- a/apps/assets/templates/assets/user_asset_list.html +++ b/apps/assets/templates/assets/user_asset_list.html @@ -1,81 +1,171 @@ -{% extends '_base_list.html' %} -{% load i18n %} +{% extends 'base.html' %} {% load static %} +{% load i18n %} + {% block custom_head_css_js %} - - - -{% endblock %} -{% block content_left_head %}{% endblock %} - -{% block table_search %} + + + {% endblock %} -{% block table_container %} - - - - - - - - - - - - - - -
{% trans 'Hostname' %}{% trans 'IP' %}{% trans 'Port' %}{% trans 'Hardware' %}{% trans 'Active' %}{% trans 'Connective' %}
+{% block content %} +
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+ +
+
+
+
+ + +
+ + + + + + + + + + + + +
{% trans 'Hostname' %}{% trans 'IP' %}{% trans 'Active' %}{% trans 'System users' %}
+
+
+
+
{% endblock %} {% block custom_foot_js %} - - -{% endblock %} + +{% endblock %} \ No newline at end of file diff --git a/apps/perms/api.py b/apps/perms/api.py index 148d366db..4f4fa3145 100644 --- a/apps/perms/api.py +++ b/apps/perms/api.py @@ -100,33 +100,6 @@ class UserGrantedNodesApi(ListAPIView): 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 @@ -172,6 +145,11 @@ class UserGrantedNodeAssetsApi(ListAPIView): 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,) diff --git a/apps/users/models/user.py b/apps/users/models/user.py index 347848b7b..06761a5df 100644 --- a/apps/users/models/user.py +++ b/apps/users/models/user.py @@ -241,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() diff --git a/apps/users/templates/users/user_profile.html b/apps/users/templates/users/user_profile.html index 88d963595..f29e1427e 100644 --- a/apps/users/templates/users/user_profile.html +++ b/apps/users/templates/users/user_profile.html @@ -65,7 +65,7 @@ {% trans 'OTP' %} - {{ user.enable_otp|yesno:"Yes,No,Unkown" }} + {{ user.otp_enabled|yesno:"Yes,No,Unkown" }} {% trans 'Public key' %} diff --git a/apps/users/views/login.py b/apps/users/views/login.py index 21c63f889..cd8ce8fca 100644 --- a/apps/users/views/login.py +++ b/apps/users/views/login.py @@ -192,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() From 3835adafb8c49569a21a93ec58474283629f484d Mon Sep 17 00:00:00 2001 From: ibuler Date: Wed, 11 Apr 2018 12:13:49 +0800 Subject: [PATCH 10/13] =?UTF-8?q?[Update]=20=E4=BF=AE=E6=94=B9bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/perms/api.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/apps/perms/api.py b/apps/perms/api.py index 4f4fa3145..1303e4128 100644 --- a/apps/perms/api.py +++ b/apps/perms/api.py @@ -114,8 +114,12 @@ class UserGrantedNodesWithAssetsApi(ListAPIView): nodes = AssetPermissionUtil.get_user_nodes_with_assets(user) for node, _assets in nodes.items(): assets = _assets.keys() - for asset, system_users in _assets.items(): - asset.system_users_granted = system_users + 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 From cbd6c3ee699c3ef2b365589463580b69bcf1a815 Mon Sep 17 00:00:00 2001 From: ibuler Date: Wed, 11 Apr 2018 12:23:35 +0800 Subject: [PATCH 11/13] =?UTF-8?q?[Update]=20=E6=B7=BB=E5=8A=A0=E8=BF=81?= =?UTF-8?q?=E7=A7=BB=E8=84=9A=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- utils/2018_04_11_migrate_permissions.sh | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 utils/2018_04_11_migrate_permissions.sh diff --git a/utils/2018_04_11_migrate_permissions.sh b/utils/2018_04_11_migrate_permissions.sh new file mode 100644 index 000000000..5f98e5d4a --- /dev/null +++ b/utils/2018_04_11_migrate_permissions.sh @@ -0,0 +1,24 @@ +#!/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 + From 7fc2ef00eee69f323e540916d69b975bac0cc6f5 Mon Sep 17 00:00:00 2001 From: ibuler Date: Wed, 11 Apr 2018 12:45:04 +0800 Subject: [PATCH 12/13] =?UTF-8?q?[Update]=20=E4=BF=AE=E6=94=B9=E8=B5=84?= =?UTF-8?q?=E4=BA=A7api=E8=8E=B7=E5=8F=96=E7=9A=84bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/assets/api/asset.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/assets/api/asset.py b/apps/assets/api/asset.py index 9520ff120..50c037df9 100644 --- a/apps/assets/api/asset.py +++ b/apps/assets/api/asset.py @@ -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 From 8b3b517babbb6ce381183070cba3cfadc54310c0 Mon Sep 17 00:00:00 2001 From: ibuler Date: Wed, 11 Apr 2018 15:24:12 +0800 Subject: [PATCH 13/13] =?UTF-8?q?[Update]=20=E4=BF=AE=E6=94=B9=E6=8E=88?= =?UTF-8?q?=E6=9D=83=E8=A7=84=E5=88=99=E8=AF=A6=E6=83=85=E5=88=97=E8=A1=A8?= =?UTF-8?q?=E9=A1=B5=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/__init__.py | 2 +- apps/assets/models/asset.py | 2 +- apps/assets/models/user.py | 2 +- apps/i18n/zh/LC_MESSAGES/django.mo | Bin 30560 -> 30648 bytes apps/i18n/zh/LC_MESSAGES/django.po | 477 ++++++++++-------- apps/perms/api.py | 76 ++- apps/perms/forms.py | 13 + apps/perms/models.py | 18 +- .../perms/asset_permission_asset.html | 38 +- .../perms/asset_permission_detail.html | 46 +- .../perms/asset_permission_user.html | 6 +- apps/perms/urls/api_urls.py | 54 +- apps/perms/urls/views_urls.py | 4 +- apps/perms/views.py | 68 ++- apps/users/models/user.py | 2 +- 15 files changed, 518 insertions(+), 290 deletions(-) diff --git a/apps/__init__.py b/apps/__init__.py index 6110e1346..af5b4a679 100644 --- a/apps/__init__.py +++ b/apps/__init__.py @@ -2,4 +2,4 @@ # -*- coding: utf-8 -*- # -__version__ = "1.0.0" +__version__ = "1.3.0" diff --git a/apps/assets/models/asset.py b/apps/assets/models/asset.py index 1f42a2978..96bd598b0 100644 --- a/apps/assets/models/asset.py +++ b/apps/assets/models/asset.py @@ -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): diff --git a/apps/assets/models/user.py b/apps/assets/models/user.py index e0de2cf49..bf31b8491 100644 --- a/apps/assets/models/user.py +++ b/apps/assets/models/user.py @@ -109,7 +109,7 @@ class SystemUser(AssetUser): 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 { diff --git a/apps/i18n/zh/LC_MESSAGES/django.mo b/apps/i18n/zh/LC_MESSAGES/django.mo index 61ef6ec21bb6059e9edbfaf75676af11deac619e..1d2577577a73c75a8ad2ee49c5b55cd99680e7b3 100644 GIT binary patch delta 11016 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 delta 10991 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 Rxr?Xgt=pM>clf@b{{#0u2r&Qv diff --git a/apps/i18n/zh/LC_MESSAGES/django.po b/apps/i18n/zh/LC_MESSAGES/django.po index fc8255313..27c978fcd 100644 --- a/apps/i18n/zh/LC_MESSAGES/django.po +++ b/apps/i18n/zh/LC_MESSAGES/django.po @@ -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 \n" "Language-Team: Jumpserver team\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 "任务已下发,查看左侧资产状态" diff --git a/apps/perms/api.py b/apps/perms/api.py index 1303e4128..f9b60dcb6 100644 --- a/apps/perms/api.py +++ b/apps/perms/api.py @@ -3,7 +3,7 @@ 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 @@ -246,3 +246,77 @@ class ValidateUserAssetPermissionView(APIView): 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}) diff --git a/apps/perms/forms.py b/apps/perms/forms.py index 060812d4a..c418160dd 100644 --- a/apps/perms/forms.py +++ b/apps/perms/forms.py @@ -4,10 +4,23 @@ from __future__ import absolute_import, unicode_literals from django import forms from django.utils.translation import ugettext_lazy as _ +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 = AssetPermission exclude = ( diff --git a/apps/perms/models.py b/apps/perms/models.py index 6609cd8e2..954cbe5f6 100644 --- a/apps/perms/models.py +++ b/apps/perms/models.py @@ -4,7 +4,7 @@ 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): @@ -45,6 +45,22 @@ class AssetPermission(models.Model): return True return False + 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_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 + class NodePermission(models.Model): id = models.UUIDField(default=uuid.uuid4, primary_key=True) diff --git a/apps/perms/templates/perms/asset_permission_asset.html b/apps/perms/templates/perms/asset_permission_asset.html index 12369574d..fa50b4eb9 100644 --- a/apps/perms/templates/perms/asset_permission_asset.html +++ b/apps/perms/templates/perms/asset_permission_asset.html @@ -57,12 +57,12 @@ - {% for asset in page_obj %} + {% for asset in object_list %} {{ asset.hostname }} {{ asset.ip }} - + {% endfor %} @@ -105,7 +105,7 @@
- {% trans 'Add asset group to this permission' %} + {% trans 'Add node to this permission' %}
@@ -113,25 +113,25 @@ - {% for asset_group in asset_groups %} + {% for node in asset_permission.nodes.all %} - + {% 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( '' + @@ -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() }) diff --git a/apps/perms/templates/perms/asset_permission_detail.html b/apps/perms/templates/perms/asset_permission_detail.html index 5540ca515..4cfa48de4 100644 --- a/apps/perms/templates/perms/asset_permission_detail.html +++ b/apps/perms/templates/perms/asset_permission_detail.html @@ -15,19 +15,19 @@ - + - + - + - + - - + + - + + + + + - + - + - + - +
- + {% for node in nodes_remain %} + {% endfor %}
- +
{{ asset_group.name }}{{ node.value }} - +
{% trans 'Name' %}:{{ asset_permission.name }}{{ object.name }}
{% trans 'User count' %}:{{ asset_permission.users.count }}{{ object.users.count }}
{% trans 'User group count' %}:{{ asset_permission.users.count }}{{ object.users.count }}
{% trans 'Asset count' %}:{{ asset_permission.assets.count }}{{ object.assets.count }}
{% trans 'Asset group count' %}:{{ asset_permission.asset_groups.count }}{% trans 'Node count' %}:{{ object.nodes.count }}
{% trans 'System user count' %}:{{ asset_permission.system_users.count }}{{ object.system_users.count }}
{% trans 'Date start' %}:{{ object.date_start }}
{% trans 'Date expired' %}:{{ asset_permission.date_expired }}{{ object.date_expired }}
{% trans 'Date created' %}:{{ asset_permission.date_created }}{{ object.date_created }}
{% trans 'Created by' %}:{{ asset_permission.created_by }}{{ object.created_by }}
{% trans 'Comment' %}:{{ asset_permission.comment }}{{ object.comment }}
@@ -117,7 +121,7 @@
- +