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):
ID