mirror of https://github.com/jumpserver/jumpserver
				
				
				
			[Fixture] 添加command log backends, 未来支持es
							parent
							
								
									0869931e67
								
							
						
					
					
						commit
						a79c3dd156
					
				| 
						 | 
				
			
			@ -1,6 +1,7 @@
 | 
			
		|||
# -*- coding: utf-8 -*-
 | 
			
		||||
# 
 | 
			
		||||
 | 
			
		||||
from collections import OrderedDict
 | 
			
		||||
from django.core.cache import cache
 | 
			
		||||
from django.conf import settings
 | 
			
		||||
import copy
 | 
			
		||||
| 
						 | 
				
			
			@ -35,7 +36,7 @@ class TerminalRegisterView(ListCreateAPIView):
 | 
			
		|||
        if serializer.is_valid():
 | 
			
		||||
            terminal = serializer.save()
 | 
			
		||||
            app_user, access_key = terminal.create_related_app_user()
 | 
			
		||||
            data = {}
 | 
			
		||||
            data = OrderedDict()
 | 
			
		||||
            data['terminal'] = copy.deepcopy(serializer.data)
 | 
			
		||||
            data['user'] = app_user.to_json()
 | 
			
		||||
            data['access_key_id'] = access_key.id
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -167,6 +167,16 @@ class SystemUser(models.Model):
 | 
			
		|||
    def asset_group_amount(self):
 | 
			
		||||
        return self.asset_groups.count()
 | 
			
		||||
 | 
			
		||||
    def to_json(self):
 | 
			
		||||
        return {
 | 
			
		||||
            'id': self.id,
 | 
			
		||||
            'name': self.name,
 | 
			
		||||
            'username': self.username,
 | 
			
		||||
            'protocol': self.protocol,
 | 
			
		||||
            'auth_method': self.auth_method,
 | 
			
		||||
            'auto_push': self.auto_push,
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    class Meta:
 | 
			
		||||
        ordering = ['name']
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -233,9 +233,6 @@ class AssetGroupCreateView(AdminUserRequiredMixin, CreateView):
 | 
			
		|||
    form_class = forms.AssetGroupForm
 | 
			
		||||
    template_name = 'assets/asset_group_create.html'
 | 
			
		||||
    success_url = reverse_lazy('assets:asset-group-list')
 | 
			
		||||
    #ordering = '-id'
 | 
			
		||||
 | 
			
		||||
    # Todo: Asset group create template select assets so hard, need be resolve next
 | 
			
		||||
 | 
			
		||||
    def get_context_data(self, **kwargs):
 | 
			
		||||
        context = {
 | 
			
		||||
| 
						 | 
				
			
			@ -249,7 +246,8 @@ class AssetGroupCreateView(AdminUserRequiredMixin, CreateView):
 | 
			
		|||
    def form_valid(self, form):
 | 
			
		||||
        asset_group = form.save()
 | 
			
		||||
        assets_id_list = self.request.POST.getlist('assets', [])
 | 
			
		||||
        assets = [get_object_or_404(Asset, id=int(asset_id)) for asset_id in assets_id_list]
 | 
			
		||||
        assets = [get_object_or_404(Asset, id=int(asset_id))
 | 
			
		||||
                  for asset_id in assets_id_list]
 | 
			
		||||
        asset_group.created_by = self.request.user.username or 'Admin'
 | 
			
		||||
        asset_group.assets.add(*tuple(assets))
 | 
			
		||||
        asset_group.save()
 | 
			
		||||
| 
						 | 
				
			
			@ -284,7 +282,8 @@ class AssetGroupDetailView(AdminUserRequiredMixin, DetailView):
 | 
			
		|||
            'app': _('Assets'),
 | 
			
		||||
            'action': _('Asset group detail'),
 | 
			
		||||
            'assets_remain': assets_remain,
 | 
			
		||||
            'assets': [asset for asset in Asset.objects.all() if asset not in assets_remain],
 | 
			
		||||
            'assets': [asset for asset in Asset.objects.all()
 | 
			
		||||
                       if asset not in assets_remain],
 | 
			
		||||
            'system_users': system_users,
 | 
			
		||||
            'system_users_remain': system_users_remain,
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -349,10 +348,9 @@ class IDCCreateView(AdminUserRequiredMixin, CreateView):
 | 
			
		|||
        return super(IDCCreateView, self).get_context_data(**kwargs)
 | 
			
		||||
 | 
			
		||||
    def form_valid(self, form):
 | 
			
		||||
        IDC = form.save(commit=False)
 | 
			
		||||
        IDC.created_by = self.request.user.username or 'System'
 | 
			
		||||
        IDC.save()
 | 
			
		||||
        # IDC_add_success_next(user)
 | 
			
		||||
        idc = form.save(commit=False)
 | 
			
		||||
        idc.created_by = self.request.user.username or 'System'
 | 
			
		||||
        idc.save()
 | 
			
		||||
        return super(IDCCreateView, self).form_valid(form)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1 @@
 | 
			
		|||
 | 
			
		||||
| 
						 | 
				
			
			@ -5,11 +5,12 @@
 | 
			
		|||
from __future__ import absolute_import, unicode_literals
 | 
			
		||||
 | 
			
		||||
from rest_framework import generics, viewsets
 | 
			
		||||
from rest_framework.views import APIView, Response
 | 
			
		||||
from rest_framework_bulk import BulkModelViewSet
 | 
			
		||||
 | 
			
		||||
from audits.backends import command_store
 | 
			
		||||
from audits.backends.command.serializers import CommandLogSerializer
 | 
			
		||||
from . import models, serializers
 | 
			
		||||
from .hands import IsSuperUserOrAppUser, Terminal, IsAppUser
 | 
			
		||||
from .hands import IsSuperUserOrAppUser, IsAppUser
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ProxyLogReceiveView(generics.CreateAPIView):
 | 
			
		||||
| 
						 | 
				
			
			@ -47,8 +48,21 @@ class ProxyLogViewSet(viewsets.ModelViewSet):
 | 
			
		|||
    permission_classes = (IsSuperUserOrAppUser,)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class CommandLogViewSet(viewsets.ModelViewSet):
 | 
			
		||||
    queryset = models.CommandLog.objects.all()
 | 
			
		||||
    serializer_class = serializers.CommandLogSerializer
 | 
			
		||||
class CommandLogViewSet(BulkModelViewSet):
 | 
			
		||||
    """接受app发送来的command log, 格式如下
 | 
			
		||||
    {
 | 
			
		||||
        "proxy_log_id": 23,
 | 
			
		||||
        "user": "admin",
 | 
			
		||||
        "asset": "localhost",
 | 
			
		||||
        "system_user": "web",
 | 
			
		||||
        "command_no": 1,
 | 
			
		||||
        "command": "whoami",
 | 
			
		||||
        "output": "d2hvbWFp",  # base64.b64encode(s)
 | 
			
		||||
        "timestamp": 1485238673.0
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    """
 | 
			
		||||
    queryset = command_store.all()
 | 
			
		||||
    serializer_class = CommandLogSerializer
 | 
			
		||||
    permission_classes = (IsSuperUserOrAppUser,)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,6 @@
 | 
			
		|||
from importlib import import_module
 | 
			
		||||
from django.conf import settings
 | 
			
		||||
 | 
			
		||||
command_engine = import_module(settings.COMMAND_STORE_BACKEND)
 | 
			
		||||
command_store = command_engine.CommandStore()
 | 
			
		||||
from .command.serializers import CommandLogSerializer
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,19 @@
 | 
			
		|||
# coding: utf-8
 | 
			
		||||
import abc
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class CommandBase(object):
 | 
			
		||||
    __metaclass__ = abc.ABCMeta
 | 
			
		||||
 | 
			
		||||
    @abc.abstractmethod
 | 
			
		||||
    def save(self, proxy_log_id, user, asset, system_user,
 | 
			
		||||
             command_no, command, output, timestamp):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    @abc.abstractmethod
 | 
			
		||||
    def filter(self, date_from=None, date_to=None, user='',
 | 
			
		||||
               asset='', system_user='', command=''):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,45 @@
 | 
			
		|||
# ~*~ coding: utf-8 ~*~
 | 
			
		||||
 | 
			
		||||
from .base import CommandBase
 | 
			
		||||
from audits.models import CommandLog
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class CommandStore(CommandBase):
 | 
			
		||||
    model = CommandLog
 | 
			
		||||
    queryset = []
 | 
			
		||||
 | 
			
		||||
    def save(self, proxy_log_id, user, asset, system_user,
 | 
			
		||||
             command_no, command, output, timestamp):
 | 
			
		||||
        self.model.objects.create(
 | 
			
		||||
            proxy_log_id=proxy_log_id, user=user, asset=asset,
 | 
			
		||||
            system_user=system_user, command_no=command_no,
 | 
			
		||||
            command=command, output=output, timestamp=timestamp
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def filter(self, date_from_ts=None, date_to_ts=None, user='',
 | 
			
		||||
               asset='', system_user='', command='', proxy_log_id=''):
 | 
			
		||||
        filter_kwargs = {}
 | 
			
		||||
 | 
			
		||||
        if date_from_ts:
 | 
			
		||||
            filter_kwargs['timestamp__gte'] = date_from_ts
 | 
			
		||||
        if date_to_ts:
 | 
			
		||||
            filter_kwargs['timestamp__lte'] = date_to_ts
 | 
			
		||||
        if user:
 | 
			
		||||
            filter_kwargs['user'] = user
 | 
			
		||||
        if asset:
 | 
			
		||||
            filter_kwargs['asset'] = asset
 | 
			
		||||
        if system_user:
 | 
			
		||||
            filter_kwargs['system_user'] = system_user
 | 
			
		||||
        if command:
 | 
			
		||||
            filter_kwargs['command__icontains'] = command
 | 
			
		||||
        if proxy_log_id:
 | 
			
		||||
            filter_kwargs['proxy_log_id'] = proxy_log_id
 | 
			
		||||
 | 
			
		||||
        if filter_kwargs:
 | 
			
		||||
            self.queryset = self.model.objects.filter(**filter_kwargs)
 | 
			
		||||
        return self.queryset
 | 
			
		||||
 | 
			
		||||
    def all(self):
 | 
			
		||||
        """返回所有数据"""
 | 
			
		||||
        return self.model.objects.iterator()
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,21 @@
 | 
			
		|||
# ~*~ coding: utf-8 ~*~
 | 
			
		||||
import base64
 | 
			
		||||
from rest_framework import serializers
 | 
			
		||||
from audits.models import CommandLog
 | 
			
		||||
from audits.backends import command_store
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class CommandLogSerializer(serializers.ModelSerializer):
 | 
			
		||||
    """使用这个类作为基础Command Log Serializer类, 用来序列化"""
 | 
			
		||||
 | 
			
		||||
    class Meta:
 | 
			
		||||
        model = CommandLog
 | 
			
		||||
        fields = '__all__'
 | 
			
		||||
 | 
			
		||||
    def save(self):
 | 
			
		||||
        try:
 | 
			
		||||
            output = self.validated_data['output']
 | 
			
		||||
            self.validated_data['output'] = base64.b64decode(output)
 | 
			
		||||
        except IndexError:
 | 
			
		||||
            pass
 | 
			
		||||
        return command_store.save(**dict(self.validated_data))
 | 
			
		||||
| 
						 | 
				
			
			@ -24,7 +24,8 @@ class LoginLog(models.Model):
 | 
			
		|||
                                  verbose_name=_('Login city'))
 | 
			
		||||
    user_agent = models.CharField(max_length=100, blank=True, null=True,
 | 
			
		||||
                                  verbose_name=_('User agent'))
 | 
			
		||||
    date_login = models.DateTimeField(auto_now_add=True, verbose_name=_('Date login'))
 | 
			
		||||
    date_login = models.DateTimeField(auto_now_add=True,
 | 
			
		||||
                                      verbose_name=_('Date login'))
 | 
			
		||||
 | 
			
		||||
    class Meta:
 | 
			
		||||
        db_table = 'login_log'
 | 
			
		||||
| 
						 | 
				
			
			@ -37,56 +38,52 @@ class ProxyLog(models.Model):
 | 
			
		|||
        ('WT', 'Web Terminal'),
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    username = models.CharField(max_length=20,  verbose_name=_('Username'))
 | 
			
		||||
    name = models.CharField(max_length=20, blank=True, verbose_name=_('Name'))
 | 
			
		||||
    hostname = models.CharField(max_length=128, blank=True, verbose_name=_('Hostname'))
 | 
			
		||||
    ip = models.GenericIPAddressField(max_length=32, verbose_name=_('IP'))
 | 
			
		||||
    system_user = models.CharField(max_length=20, verbose_name=_('System user'))
 | 
			
		||||
    login_type = models.CharField(choices=LOGIN_TYPE_CHOICE, max_length=2, blank=True,
 | 
			
		||||
                                  null=True, verbose_name=_('Login type'))
 | 
			
		||||
    terminal = models.CharField(max_length=32, blank=True, null=True, verbose_name=_('Terminal'))
 | 
			
		||||
    user = models.CharField(max_length=32,  verbose_name=_('User'))
 | 
			
		||||
    asset = models.CharField(max_length=32, verbose_name=_('Asset'))
 | 
			
		||||
    system_user = models.CharField(max_length=32, verbose_name=_('System user'))
 | 
			
		||||
    login_type = models.CharField(
 | 
			
		||||
        choices=LOGIN_TYPE_CHOICE, max_length=2, blank=True,
 | 
			
		||||
        null=True, verbose_name=_('Login type'))
 | 
			
		||||
    terminal = models.CharField(
 | 
			
		||||
        max_length=32, blank=True, null=True, verbose_name=_('Terminal'))
 | 
			
		||||
    log_file = models.CharField(max_length=1000, blank=True, null=True)
 | 
			
		||||
    was_failed = models.BooleanField(default=False, verbose_name=_('Did connect failed'))
 | 
			
		||||
    is_finished = models.BooleanField(default=False, verbose_name=_('Is finished'))
 | 
			
		||||
    date_start = models.DateTimeField(auto_created=True, verbose_name=_('Date start'))
 | 
			
		||||
    date_finished = models.DateTimeField(null=True, verbose_name=_('Date finished'))
 | 
			
		||||
    is_failed = models.BooleanField(
 | 
			
		||||
        default=False, verbose_name=_('Did connect failed'))
 | 
			
		||||
    is_finished = models.BooleanField(
 | 
			
		||||
        default=False, verbose_name=_('Is finished'))
 | 
			
		||||
    date_start = models.DateTimeField(
 | 
			
		||||
        auto_created=True, verbose_name=_('Date start'))
 | 
			
		||||
    date_finished = models.DateTimeField(
 | 
			
		||||
        null=True, verbose_name=_('Date finished'))
 | 
			
		||||
 | 
			
		||||
    def __unicode__(self):
 | 
			
		||||
        return '%s-%s-%s-%s' % (self.username, self.hostname, self.system_user, self.id)
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def commands_dict(self):
 | 
			
		||||
        commands = self.command_log.all()
 | 
			
		||||
        return [{
 | 
			
		||||
                    "command_no": command.command_no,
 | 
			
		||||
                    "command": command.command,
 | 
			
		||||
                    "output": command.output_decode,
 | 
			
		||||
                    "datetime": command.datetime,
 | 
			
		||||
                    } for command in commands]
 | 
			
		||||
        return '%s-%s-%s' % (self.user, self.asset, self.system_user)
 | 
			
		||||
 | 
			
		||||
    class Meta:
 | 
			
		||||
        ordering = ['-date_start', 'username']
 | 
			
		||||
        ordering = ['-date_start', 'user']
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class CommandLog(models.Model):
 | 
			
		||||
    proxy_log = models.ForeignKey(ProxyLog, on_delete=models.CASCADE,
 | 
			
		||||
                                  related_name='commands')
 | 
			
		||||
    proxy_log_id = models.IntegerField()
 | 
			
		||||
    user = models.CharField(max_length=48, db_index=True)
 | 
			
		||||
    asset = models.CharField(max_length=128, db_index=True)
 | 
			
		||||
    system_user = models.CharField(max_length=48, db_index=True)
 | 
			
		||||
    command_no = models.IntegerField()
 | 
			
		||||
    command = models.CharField(max_length=1000, blank=True)
 | 
			
		||||
    command = models.CharField(max_length=1000, blank=True, db_index=True)
 | 
			
		||||
    output = models.TextField(blank=True)
 | 
			
		||||
    datetime = models.DateTimeField(null=True)
 | 
			
		||||
    timestamp = models.FloatField(null=True, db_index=True)
 | 
			
		||||
 | 
			
		||||
    def __unicode__(self):
 | 
			
		||||
        return '%s: %s' % (self.id, self.command)
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def output_decode(self):
 | 
			
		||||
        try:
 | 
			
		||||
            return base64.b64decode(self.output).decode('utf-8') \
 | 
			
		||||
                .replace('\n', '<br />')
 | 
			
		||||
        except UnicodeDecodeError:
 | 
			
		||||
            return 'UnicodeDecodeError'
 | 
			
		||||
 | 
			
		||||
    class Meta:
 | 
			
		||||
        db_table = 'command_log'
 | 
			
		||||
        ordering = ['command_no', 'command']
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class RecordLog(models.Model):
 | 
			
		||||
    proxy_log_id = models.IntegerField()
 | 
			
		||||
    output = models.TextField(verbose_name=_('Output'))
 | 
			
		||||
    timestamp = models.FloatField(null=True)
 | 
			
		||||
 | 
			
		||||
    def __unicode__(self):
 | 
			
		||||
        return 'Record: %s' % self.proxy_log_id
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -13,10 +13,7 @@ class ProxyLogSerializer(serializers.ModelSerializer):
 | 
			
		|||
 | 
			
		||||
    class Meta:
 | 
			
		||||
        model = models.ProxyLog
 | 
			
		||||
        fields = ['id', 'name', 'username', 'hostname', 'ip', 'system_user',
 | 
			
		||||
                  'login_type', 'terminal', 'log_file', 'was_failed',
 | 
			
		||||
                  'is_finished', 'date_start', 'date_finished', 'time',
 | 
			
		||||
                  'command_length', "commands_dict"]
 | 
			
		||||
        fields = '__all__'
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def get_time(obj):
 | 
			
		||||
| 
						 | 
				
			
			@ -27,10 +24,5 @@ class ProxyLogSerializer(serializers.ModelSerializer):
 | 
			
		|||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def get_command_length(obj):
 | 
			
		||||
        return len(obj.commands.all())
 | 
			
		||||
        return 2
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class CommandLogSerializer(serializers.ModelSerializer):
 | 
			
		||||
    class Meta:
 | 
			
		||||
        model = models.CommandLog
 | 
			
		||||
        fields = '__all__'
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -23,32 +23,31 @@
 | 
			
		|||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="input-group">
 | 
			
		||||
            <select class="select2 form-control" name="username">
 | 
			
		||||
            <select class="select2 form-control" name="user">
 | 
			
		||||
                <option value="">{% trans 'User' %}</option>
 | 
			
		||||
                {% for user in user_list %}
 | 
			
		||||
                    <option value="{{ user.username }}" {% if user.username == username %} selected {% endif %}>{{ user.username }}</option>
 | 
			
		||||
                {% for u in user_list %}
 | 
			
		||||
                    <option value="{{ u.username }}" {% if user == u.username %} selected {% endif %}>{{ u.username }}</option>
 | 
			
		||||
                {% endfor %}
 | 
			
		||||
            </select>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="input-group">
 | 
			
		||||
            <select class="select2 form-control" name="ip">
 | 
			
		||||
            <select class="select2 form-control" name="asset">
 | 
			
		||||
                <option value="">{% trans 'Asset' %}</option>
 | 
			
		||||
                {% for asset in asset_list %}
 | 
			
		||||
                    <option value="{{ asset.ip }}" {% if asset.ip == ip %} selected {% endif %}>{{ asset.ip }}</option>
 | 
			
		||||
                {% for a in asset_list %}
 | 
			
		||||
                    <option value="{{ a.ip }}" {% if asset == a.ip %} selected {% endif %}>{{ a.ip }}</option>
 | 
			
		||||
                {% endfor %}
 | 
			
		||||
            </select>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="input-group">
 | 
			
		||||
            <select class="select2 form-control" name="system_user">
 | 
			
		||||
{#                <option value="">{{ system_user }}</option>#}
 | 
			
		||||
                <option value="">{% trans 'System user' %}</option>
 | 
			
		||||
                {% for system_user in system_user_list %}
 | 
			
		||||
                    <option value="{{ system_user.username }}" {% if system_user.username == system_user %} selected {% endif %}>{{ system_user.username }}</option>
 | 
			
		||||
                {% for s in system_user_list %}
 | 
			
		||||
                    <option value="{{ s.username }}" {% if s.username == system_user %} selected {% endif %}>{{ s.username }}</option>
 | 
			
		||||
                {% endfor %}
 | 
			
		||||
            </select>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="input-group">
 | 
			
		||||
            <input type="text" class="form-control input-sm" name="keyword" placeholder="Search" value="{{ keyword }}">
 | 
			
		||||
            <input type="text" class="form-control input-sm" name="command" placeholder="Command" value="{{ command }}">
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="input-group">
 | 
			
		||||
            <div class="input-group-btn">
 | 
			
		||||
| 
						 | 
				
			
			@ -78,12 +77,12 @@
 | 
			
		|||
            <tr>
 | 
			
		||||
                <td>{{ command.id }}</td>
 | 
			
		||||
                <td>{{ command.command }}</td>
 | 
			
		||||
                <td>{{ command.proxy_log.username }}</td>
 | 
			
		||||
                <td>{{ command.proxy_log.ip }}</td>
 | 
			
		||||
                <td>{{ command.proxy_log.system_user }}</td>
 | 
			
		||||
                <td><a href="{% url 'audits:proxy-log-detail' pk=command.proxy_log.id  %}">{{ command.proxy_log.id}}</a></td>
 | 
			
		||||
                <td>{{ command.datetime }}</td>
 | 
			
		||||
                <td>{{ command.output_decode|safe }}</td>
 | 
			
		||||
                <td>{{ command.user }}</td>
 | 
			
		||||
                <td>{{ command.asset }}</td>
 | 
			
		||||
                <td>{{ command.system_user }}</td>
 | 
			
		||||
                <td><a href="{% url 'audits:proxy-log-detail' pk=command.proxy_log_id  %}">{{ command.proxy_log_id}}</a></td>
 | 
			
		||||
                <td>{{ command.timestamp|ts_to_date }}</td>
 | 
			
		||||
                <td><pre style="border: none; background: none">{{ command.output|to_html|safe }}</pre></td>
 | 
			
		||||
            </tr>
 | 
			
		||||
        {% endfor %}
 | 
			
		||||
        </tbody>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -19,7 +19,7 @@
 | 
			
		|||
                        </ul>
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <div class="tab-content">
 | 
			
		||||
                        <div class="col-sm-7" style="padding-left: 0;">
 | 
			
		||||
                        <div class="col-sm-11" style="padding-left: 0;">
 | 
			
		||||
                            <div class="ibox float-e-margins">
 | 
			
		||||
                                <div class="ibox-title">
 | 
			
		||||
                                    <span style="float: left">{% trans 'Command log list' %} <b>{{ user_object.name }}</b></span>
 | 
			
		||||
| 
						 | 
				
			
			@ -52,8 +52,8 @@
 | 
			
		|||
                                            <tr>
 | 
			
		||||
                                                <td>{{ command.command_no }}</td>
 | 
			
		||||
                                                <td>{{ command.command }}</td>
 | 
			
		||||
                                                <td>{{ command.output_decode|safe }}</td>
 | 
			
		||||
                                                <td>{{ command.datetime }}</td>
 | 
			
		||||
                                                <td><pre style="border: none;background: none">{{ command.output|to_html|safe}}</pre></td>
 | 
			
		||||
                                                <td>{{ command.timestamp|ts_to_date}}</td>
 | 
			
		||||
                                            </tr>
 | 
			
		||||
                                        {% endfor %}
 | 
			
		||||
                                        </tbody>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -47,7 +47,7 @@
 | 
			
		|||
            </select>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="input-group">
 | 
			
		||||
            <input type="text" class="form-control input-sm" name="keyword" placeholder="Command" value="{{ keyword }}">
 | 
			
		||||
            <input type="text" class="form-control input-sm" name="keyword" placeholder="Keyword" value="{{ keyword }}">
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="input-group">
 | 
			
		||||
            <div class="input-group-btn">
 | 
			
		||||
| 
						 | 
				
			
			@ -61,44 +61,53 @@
 | 
			
		|||
 | 
			
		||||
{% block table_head %}
 | 
			
		||||
    <th class="text-center">{% trans 'ID' %}</th>
 | 
			
		||||
    <th class="text-center">{% trans 'Username' %}</th>
 | 
			
		||||
    <th class="text-center">{% trans 'IP' %}</th>
 | 
			
		||||
    <th class="text-center">{% trans 'User' %}</th>
 | 
			
		||||
    <th class="text-center">{% trans 'Asset' %}</th>
 | 
			
		||||
    <th class="text-center">{% trans 'System user' %}</th>
 | 
			
		||||
    <th class="text-center">{% trans 'Command' %}</th>
 | 
			
		||||
    <th class="text-center">{% trans 'Success' %}</th>
 | 
			
		||||
    <th class="text-center">{% trans 'Finished' %}</th>
 | 
			
		||||
    <th class="text-center">{% trans 'R/M' %}</th>
 | 
			
		||||
    <th class="text-center">{% trans 'Date start' %}</th>
 | 
			
		||||
    <th class="text-center">{% trans 'Time' %}</th>
 | 
			
		||||
{% endblock %}
 | 
			
		||||
 | 
			
		||||
{% block table_body %}
 | 
			
		||||
     {% for proxy_log in proxy_log_list %}
 | 
			
		||||
         <tr class="gradeX">
 | 
			
		||||
             <td class="text-center">
 | 
			
		||||
                 <a href="{% url 'audits:proxy-log-detail' pk=proxy_log.id %}">{{ proxy_log.id }}</a>
 | 
			
		||||
             </td>
 | 
			
		||||
             <td class="text-center">{{ proxy_log.username }}</td>
 | 
			
		||||
             <td class="text-center">{{ proxy_log.ip }}</td>
 | 
			
		||||
             <td class="text-center">{{ proxy_log.system_user }}</td>
 | 
			
		||||
             <td class="text-center">{{ proxy_log.commands.all|length}}</td>
 | 
			
		||||
             <td class="text-center">
 | 
			
		||||
             {% if proxy_log.was_failed %}
 | 
			
		||||
                 <i class="fa fa-times text-danger"></i>
 | 
			
		||||
             {% else %}
 | 
			
		||||
                 <i class="fa fa-check text-navy"></i>
 | 
			
		||||
             {% endif %}
 | 
			
		||||
             </td>
 | 
			
		||||
             <td class="text-center">
 | 
			
		||||
             {% if proxy_log.is_finished %}
 | 
			
		||||
                 <i class="fa fa-check text-navy"></i>
 | 
			
		||||
             {% else %}
 | 
			
		||||
                 <i class="fa fa-times text-danger"></i>
 | 
			
		||||
             {% endif %}
 | 
			
		||||
             </td>
 | 
			
		||||
             <td class="text-center">{{ proxy_log.date_start }}</td>
 | 
			
		||||
             <td class="text-center">{{ proxy_log.date_finished|timeuntil:proxy_log.date_start }}</td>
 | 
			
		||||
         </tr>
 | 
			
		||||
     {% endfor %}
 | 
			
		||||
    {% for proxy_log in proxy_log_list %}
 | 
			
		||||
        <tr class="gradeX">
 | 
			
		||||
            <td class="text-center">
 | 
			
		||||
                <a href="{% url 'audits:proxy-log-detail' pk=proxy_log.id %}">{{ proxy_log.id }}</a>
 | 
			
		||||
            </td>
 | 
			
		||||
            <td class="text-center">{{ proxy_log.user }}</td>
 | 
			
		||||
            <td class="text-center">{{ proxy_log.asset }}</td>
 | 
			
		||||
            <td class="text-center">{{ proxy_log.system_user }}</td>
 | 
			
		||||
            <td class="text-center">{{ proxy_log.commands.all|length}}</td>
 | 
			
		||||
            <td class="text-center">
 | 
			
		||||
            {% if proxy_log.is_failed %}
 | 
			
		||||
                <i class="fa fa-times text-danger"></i>
 | 
			
		||||
            {% else %}
 | 
			
		||||
                <i class="fa fa-check text-navy"></i>
 | 
			
		||||
            {% endif %}
 | 
			
		||||
            </td>
 | 
			
		||||
            {% if proxy_log.is_finished %}
 | 
			
		||||
                <td class="text-center">
 | 
			
		||||
                    <i class="fa fa-check text-navy"></i>
 | 
			
		||||
                </td>
 | 
			
		||||
                <td class="text-center">
 | 
			
		||||
                    <a><span class="text-navy"><i class="fa fa-play-circle"></i></span></a>
 | 
			
		||||
                </td>
 | 
			
		||||
            {% else %}
 | 
			
		||||
                <td class="text-center">
 | 
			
		||||
                    <i class="fa fa-times text-danger"></i>
 | 
			
		||||
                </td>
 | 
			
		||||
                <td class="text-center">
 | 
			
		||||
                    <a><span class="text-danger"><i class="fa fa-eye"></i></span></a>
 | 
			
		||||
                </td>
 | 
			
		||||
            {% endif %}
 | 
			
		||||
            <td class="text-center">{{ proxy_log.date_start }}</td>
 | 
			
		||||
            <td class="text-center">{{ proxy_log.date_finished|timeuntil:proxy_log.date_start }}</td>
 | 
			
		||||
        </tr>
 | 
			
		||||
    {% endfor %}
 | 
			
		||||
{% endblock %}
 | 
			
		||||
 | 
			
		||||
{% block custom_foot_js %}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -10,7 +10,8 @@ router.register(r'v1/proxy-log', api.ProxyLogViewSet, 'proxy-log')
 | 
			
		|||
router.register(r'v1/command-log', api.CommandLogViewSet, 'command-log')
 | 
			
		||||
 | 
			
		||||
urlpatterns = [
 | 
			
		||||
    url(r'^v1/proxy-log/receive/$', api.ProxyLogReceiveView.as_view(), name='proxy-log-receive'),
 | 
			
		||||
    url(r'^v1/proxy-log/receive/$', api.ProxyLogReceiveView.as_view(),
 | 
			
		||||
        name='proxy-log-receive'),
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
urlpatterns += router.urls
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,17 +1,23 @@
 | 
			
		|||
# ~*~ coding: utf-8 ~*~
 | 
			
		||||
#
 | 
			
		||||
import datetime
 | 
			
		||||
import time
 | 
			
		||||
from datetime import datetime
 | 
			
		||||
 | 
			
		||||
import pytz
 | 
			
		||||
from django.views.generic import ListView, UpdateView, DeleteView, DetailView, TemplateView
 | 
			
		||||
from django.views.generic.edit import SingleObjectMixin
 | 
			
		||||
from django.utils.translation import ugettext as _
 | 
			
		||||
from django.utils import timezone
 | 
			
		||||
from django.utils.module_loading import import_string
 | 
			
		||||
from django.urls import reverse_lazy
 | 
			
		||||
from django.http import HttpResponse
 | 
			
		||||
from django.conf import settings
 | 
			
		||||
from django.db.models import Q
 | 
			
		||||
 | 
			
		||||
from .models import ProxyLog, CommandLog, LoginLog
 | 
			
		||||
from .hands import User, Asset, SystemUser, AdminUserRequiredMixin
 | 
			
		||||
from audits.backends import command_store
 | 
			
		||||
from audits.backends import CommandLogSerializer
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ProxyLogListView(AdminUserRequiredMixin, ListView):
 | 
			
		||||
| 
						 | 
				
			
			@ -19,42 +25,45 @@ class ProxyLogListView(AdminUserRequiredMixin, ListView):
 | 
			
		|||
    template_name = 'audits/proxy_log_list.html'
 | 
			
		||||
    context_object_name = 'proxy_log_list'
 | 
			
		||||
    paginate_by = settings.CONFIG.DISPLAY_PER_PAGE
 | 
			
		||||
 | 
			
		||||
    keyword = username = ip = system_user = date_from_s = date_to_s = ''
 | 
			
		||||
    keyword = user = asset = system_user = date_from_s = date_to_s = ''
 | 
			
		||||
    ordering = ['is_finished', '-id']
 | 
			
		||||
    date_format = '%m/%d/%Y'
 | 
			
		||||
 | 
			
		||||
    def get_queryset(self):
 | 
			
		||||
        date_now = timezone.localtime(timezone.now())
 | 
			
		||||
        now_s = date_now.strftime('%m/%d/%Y')
 | 
			
		||||
        seven_days_ago_s = (date_now-timezone.timedelta(7)).strftime('%m/%d/%Y')
 | 
			
		||||
        date_to_default = date_now.strftime(self.date_format)
 | 
			
		||||
        date_from_default = (date_now-timezone.timedelta(7))\
 | 
			
		||||
            .strftime(self.date_format)
 | 
			
		||||
 | 
			
		||||
        self.queryset = super(ProxyLogListView, self).get_queryset()
 | 
			
		||||
        self.keyword = keyword = self.request.GET.get('keyword', '')
 | 
			
		||||
        self.username = username = self.request.GET.get('username', '')
 | 
			
		||||
        self.ip = ip = self.request.GET.get('ip', '')
 | 
			
		||||
        self.system_user = system_user = self.request.GET.get('system_user', '')
 | 
			
		||||
        self.date_from_s = date_from_s = self.request.GET.get('date_from', '%s' % seven_days_ago_s)
 | 
			
		||||
        self.date_to_s = date_to_s = self.request.GET.get('date_to', '%s' % now_s)
 | 
			
		||||
        self.keyword = self.request.GET.get('keyword', '')
 | 
			
		||||
        self.user = self.request.GET.get('user')
 | 
			
		||||
        self.asset = self.request.GET.get('asset')
 | 
			
		||||
        self.system_user = self.request.GET.get('system_user')
 | 
			
		||||
        self.date_from_s = self.request.GET.get('date_from', date_from_default)
 | 
			
		||||
        self.date_to_s = self.request.GET.get('date_to', date_to_default)
 | 
			
		||||
 | 
			
		||||
        if date_from_s:
 | 
			
		||||
            date_from = timezone.datetime.strptime(date_from_s, '%m/%d/%Y')
 | 
			
		||||
            self.queryset = self.queryset.filter(date_start__gt=date_from)
 | 
			
		||||
        if date_to_s:
 | 
			
		||||
            date_to = timezone.datetime.strptime(date_to_s + ' 23:59:59',
 | 
			
		||||
                                                 '%m/%d/%Y %H:%M:%S')
 | 
			
		||||
            self.queryset = self.queryset.filter(date_start__lt=date_to)
 | 
			
		||||
        if username:
 | 
			
		||||
            self.queryset = self.queryset.filter(username=username)
 | 
			
		||||
        if ip:
 | 
			
		||||
            self.queryset = self.queryset.filter(ip=ip)
 | 
			
		||||
        if system_user:
 | 
			
		||||
            self.queryset = self.queryset.filter(system_user=system_user)
 | 
			
		||||
        if keyword:
 | 
			
		||||
        filter_kwargs = {}
 | 
			
		||||
        if self.date_from_s:
 | 
			
		||||
            date_from = datetime.strptime(self.date_from_s, self.date_format)
 | 
			
		||||
            date_from.replace(tzinfo=timezone.get_current_timezone())
 | 
			
		||||
            filter_kwargs['date_start__gt'] = date_from
 | 
			
		||||
        if self.date_to_s:
 | 
			
		||||
            date_to = timezone.datetime.strptime(
 | 
			
		||||
                self.date_to_s + ' 23:59:59', '%m/%d/%Y %H:%M:%S')
 | 
			
		||||
            date_to.replace(tzinfo=timezone.get_current_timezone())
 | 
			
		||||
            filter_kwargs['date_start__lt'] = date_to
 | 
			
		||||
        if self.user:
 | 
			
		||||
            filter_kwargs['user'] = self.user
 | 
			
		||||
        if self.asset:
 | 
			
		||||
            filter_kwargs['asset'] = self.asset
 | 
			
		||||
        if self.system_user:
 | 
			
		||||
            filter_kwargs['system_user'] = self.system_user
 | 
			
		||||
        if self.keyword:
 | 
			
		||||
            self.queryset = self.queryset.filter(
 | 
			
		||||
                Q(username__contains=keyword) |
 | 
			
		||||
                Q(name__icontains=keyword) |
 | 
			
		||||
                Q(hostname__icontains=keyword) |
 | 
			
		||||
                Q(ip__icontains=keyword) |
 | 
			
		||||
                Q(system_user__icontains=keyword)).distinct()
 | 
			
		||||
                Q(user__icontains=self.keyword) |
 | 
			
		||||
                Q(asset__icontains=self.keyword) |
 | 
			
		||||
                Q(system_user__icontains=self.keyword)).distinct()
 | 
			
		||||
        return self.queryset
 | 
			
		||||
 | 
			
		||||
    def get_context_data(self, **kwargs):
 | 
			
		||||
| 
						 | 
				
			
			@ -62,16 +71,16 @@ class ProxyLogListView(AdminUserRequiredMixin, ListView):
 | 
			
		|||
            'app': _('Audits'),
 | 
			
		||||
            'action': _('Proxy log list'),
 | 
			
		||||
            'user_list': set(
 | 
			
		||||
                list(ProxyLog.objects.values_list('username', flat=True))),
 | 
			
		||||
                list(ProxyLog.objects.values_list('user', flat=True))),
 | 
			
		||||
            'asset_list': set(
 | 
			
		||||
                list(ProxyLog.objects.values_list('ip', flat=True))),
 | 
			
		||||
                list(ProxyLog.objects.values_list('asset', flat=True))),
 | 
			
		||||
            'system_user_list': set(
 | 
			
		||||
                list(ProxyLog.objects.values_list('system_user', flat=True))),
 | 
			
		||||
            'keyword': self.keyword,
 | 
			
		||||
            'date_from': self.date_from_s,
 | 
			
		||||
            'date_to': self.date_to_s,
 | 
			
		||||
            'username': self.username,
 | 
			
		||||
            'ip': self.ip,
 | 
			
		||||
            'user': self.user,
 | 
			
		||||
            'asset': self.asset,
 | 
			
		||||
            'system_user': self.system_user,
 | 
			
		||||
        }
 | 
			
		||||
        kwargs.update(context)
 | 
			
		||||
| 
						 | 
				
			
			@ -90,7 +99,7 @@ class ProxyLogDetailView(AdminUserRequiredMixin,
 | 
			
		|||
        return super(ProxyLogDetailView, self).get(request, *args, **kwargs)
 | 
			
		||||
 | 
			
		||||
    def get_queryset(self):
 | 
			
		||||
        return list(self.object.commands.all())
 | 
			
		||||
        return list(command_store.filter(proxy_log_id=self.object.id))
 | 
			
		||||
 | 
			
		||||
    def get_context_data(self, **kwargs):
 | 
			
		||||
        context = {
 | 
			
		||||
| 
						 | 
				
			
			@ -117,42 +126,49 @@ class ProxyLogCommandsListView(AdminUserRequiredMixin,
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
class CommandLogListView(AdminUserRequiredMixin, ListView):
 | 
			
		||||
    model = CommandLog
 | 
			
		||||
    template_name = 'audits/command_log_list.html'
 | 
			
		||||
    paginate_by = settings.CONFIG.DISPLAY_PER_PAGE
 | 
			
		||||
    context_object_name = 'command_list'
 | 
			
		||||
    keyword = username = ip = system_user = date_from_s = date_to_s = ''
 | 
			
		||||
    user = asset = system_user = command = date_from_s = date_to_s = ''
 | 
			
		||||
    date_format = '%m/%d/%Y'
 | 
			
		||||
    ordering = ['-id']
 | 
			
		||||
 | 
			
		||||
    def get_queryset(self):
 | 
			
		||||
        date_now = timezone.localtime(timezone.now())
 | 
			
		||||
        now_s = date_now.strftime('%m/%d/%Y')
 | 
			
		||||
        seven_days_ago_s = (date_now-timezone.timedelta(7)).strftime('%m/%d/%Y')
 | 
			
		||||
        self.queryset = super(CommandLogListView, self).get_queryset()
 | 
			
		||||
        self.keyword = keyword = self.request.GET.get('keyword', '')
 | 
			
		||||
        self.username = username = self.request.GET.get('username', '')
 | 
			
		||||
        self.ip = ip = self.request.GET.get('ip', '')
 | 
			
		||||
        self.system_user = system_user = self.request.GET.get('system_user', '')
 | 
			
		||||
        self.date_from_s = date_from_s = \
 | 
			
		||||
            self.request.GET.get('date_from', '%s' % seven_days_ago_s)
 | 
			
		||||
        self.date_to_s = date_to_s = \
 | 
			
		||||
            self.request.GET.get('date_to', '%s' % now_s)
 | 
			
		||||
        date_to_default = date_now.strftime(self.date_format)
 | 
			
		||||
        date_from_default = (date_now - timezone.timedelta(7)) \
 | 
			
		||||
            .strftime(self.date_format)
 | 
			
		||||
        self.command = self.request.GET.get('command', '')
 | 
			
		||||
        self.user = self.request.GET.get('user')
 | 
			
		||||
        self.asset = self.request.GET.get('asset')
 | 
			
		||||
        self.system_user = self.request.GET.get('system_user')
 | 
			
		||||
        self.date_from_s = \
 | 
			
		||||
            self.request.GET.get('date_from', date_from_default)
 | 
			
		||||
        self.date_to_s = \
 | 
			
		||||
            self.request.GET.get('date_to', date_to_default)
 | 
			
		||||
 | 
			
		||||
        if date_from_s:
 | 
			
		||||
            date_from = timezone.datetime.strptime(date_from_s, '%m/%d/%Y')
 | 
			
		||||
            self.queryset = self.queryset.filter(datetime__gt=date_from)
 | 
			
		||||
        if date_to_s:
 | 
			
		||||
            date_to = timezone.datetime.strptime(
 | 
			
		||||
                date_to_s + ' 23:59:59', '%m/%d/%Y %H:%M:%S')
 | 
			
		||||
            self.queryset = self.queryset.filter(datetime__lt=date_to)
 | 
			
		||||
        if username:
 | 
			
		||||
            self.queryset = self.queryset.filter(proxy_log__username=username)
 | 
			
		||||
        if ip:
 | 
			
		||||
            self.queryset = self.queryset.filter(proxy_log__ip=ip)
 | 
			
		||||
        if system_user:
 | 
			
		||||
            self.queryset = self.queryset.filter(
 | 
			
		||||
                proxy_log__system_user=system_user)
 | 
			
		||||
        if keyword:
 | 
			
		||||
            self.queryset = self.queryset.filter(command=keyword)
 | 
			
		||||
        filter_kwargs = {}
 | 
			
		||||
        if self.date_from_s:
 | 
			
		||||
            date_from = datetime.strptime(self.date_from_s, self.date_format)\
 | 
			
		||||
                .replace(tzinfo=timezone.get_current_timezone())
 | 
			
		||||
            # date_from_utc = date_from.astimezone(pytz.utc)
 | 
			
		||||
            date_from_ts = time.mktime(date_from.timetuple())
 | 
			
		||||
            filter_kwargs['date_from_ts'] = date_from_ts
 | 
			
		||||
        if self.date_to_s:
 | 
			
		||||
            date_to = datetime.strptime(
 | 
			
		||||
                self.date_to_s + ' 23:59:59', '%m/%d/%Y %H:%M:%S')\
 | 
			
		||||
                .replace(tzinfo=timezone.get_current_timezone())
 | 
			
		||||
            date_to_ts = time.mktime(date_to.timetuple())
 | 
			
		||||
            filter_kwargs['date_to_ts'] = date_to_ts
 | 
			
		||||
        if self.user:
 | 
			
		||||
            filter_kwargs['user'] = self.user
 | 
			
		||||
        if self.asset:
 | 
			
		||||
            filter_kwargs['asset'] = self.asset
 | 
			
		||||
        if self.system_user:
 | 
			
		||||
            filter_kwargs['system_user'] = self.system_user
 | 
			
		||||
        if self.command:
 | 
			
		||||
            filter_kwargs['command'] = self.command
 | 
			
		||||
        self.queryset = command_store.filter(**filter_kwargs).order_by(*self.ordering)
 | 
			
		||||
        return self.queryset
 | 
			
		||||
 | 
			
		||||
    def get_context_data(self, **kwargs):
 | 
			
		||||
| 
						 | 
				
			
			@ -161,12 +177,12 @@ class CommandLogListView(AdminUserRequiredMixin, ListView):
 | 
			
		|||
            'action': _('Command log list'),
 | 
			
		||||
            'user_list': User.objects.all().order_by('username'),
 | 
			
		||||
            'asset_list': Asset.objects.all().order_by('ip'),
 | 
			
		||||
            'system_user_list': SystemUser.objects.all().order_by('name'),
 | 
			
		||||
            'keyword': self.keyword,
 | 
			
		||||
            'system_user_list': SystemUser.objects.all().order_by('username'),
 | 
			
		||||
            'command': self.command,
 | 
			
		||||
            'date_from': self.date_from_s,
 | 
			
		||||
            'date_to': self.date_to_s,
 | 
			
		||||
            'username': self.username,
 | 
			
		||||
            'ip': self.ip,
 | 
			
		||||
            'user': self.user,
 | 
			
		||||
            'asset': self.asset,
 | 
			
		||||
            'system_user': self.system_user,
 | 
			
		||||
        }
 | 
			
		||||
        kwargs.update(context)
 | 
			
		||||
| 
						 | 
				
			
			@ -178,7 +194,6 @@ class LoginLogListView(AdminUserRequiredMixin, ListView):
 | 
			
		|||
    paginate_by = settings.CONFIG.DISPLAY_PER_PAGE
 | 
			
		||||
    template_name = 'audits/login_log_list.html'
 | 
			
		||||
    context_object_name = 'login_log_list'
 | 
			
		||||
 | 
			
		||||
    keyword = username = date_from_s = date_to_s = ''
 | 
			
		||||
 | 
			
		||||
    def get_queryset(self):
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,7 +3,8 @@
 | 
			
		|||
from django import template
 | 
			
		||||
from django.utils import timezone
 | 
			
		||||
from django.conf import settings
 | 
			
		||||
 | 
			
		||||
from django.utils.html import escape
 | 
			
		||||
from audits.backends import command_store
 | 
			
		||||
 | 
			
		||||
register = template.Library()
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -41,10 +42,30 @@ def join_attr(seq, attr=None, sep=None):
 | 
			
		|||
        sep = ', '
 | 
			
		||||
    if attr is not None:
 | 
			
		||||
        seq = [getattr(obj, attr) for obj in seq]
 | 
			
		||||
    print(seq)
 | 
			
		||||
    return sep.join(seq)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@register.filter
 | 
			
		||||
def int_to_str(value):
 | 
			
		||||
    return str(value)
 | 
			
		||||
    return str(value)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@register.filter
 | 
			
		||||
def ts_to_date(ts):
 | 
			
		||||
    try:
 | 
			
		||||
        ts = float(ts)
 | 
			
		||||
    except TypeError:
 | 
			
		||||
        ts = 0
 | 
			
		||||
    dt = timezone.datetime.fromtimestamp(ts).\
 | 
			
		||||
        replace(tzinfo=timezone.get_current_timezone())
 | 
			
		||||
    return dt.strftime('%Y-%m-%d %H:%M:%S')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@register.filter
 | 
			
		||||
def to_html(s):
 | 
			
		||||
    return escape(s).replace('\n', '<br />')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@register.filter
 | 
			
		||||
def proxy_log_commands(log_id):
 | 
			
		||||
    return command_store.filter(proxy_log_id=log_id)
 | 
			
		||||
| 
						 | 
				
			
			@ -33,8 +33,10 @@ from .compat import to_bytes, to_string
 | 
			
		|||
SECRET_KEY = settings.SECRET_KEY
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def reverse(view_name, urlconf=None, args=None, kwargs=None, current_app=None, external=False):
 | 
			
		||||
    url = dj_reverse(view_name, urlconf=urlconf, args=args, kwargs=kwargs, current_app=current_app)
 | 
			
		||||
def reverse(view_name, urlconf=None, args=None, kwargs=None,
 | 
			
		||||
            current_app=None, external=False):
 | 
			
		||||
    url = dj_reverse(view_name, urlconf=urlconf, args=args,
 | 
			
		||||
                     kwargs=kwargs, current_app=current_app)
 | 
			
		||||
 | 
			
		||||
    if external:
 | 
			
		||||
        url = settings.SITE_URL.strip('/') + url
 | 
			
		||||
| 
						 | 
				
			
			@ -44,8 +46,8 @@ def reverse(view_name, urlconf=None, args=None, kwargs=None, current_app=None, e
 | 
			
		|||
def get_object_or_none(model, **kwargs):
 | 
			
		||||
    try:
 | 
			
		||||
        obj = model.objects.get(**kwargs)
 | 
			
		||||
    except model.DoesNotExist:
 | 
			
		||||
        obj = None
 | 
			
		||||
    except (model.FieldError, model.DoesNotExist):
 | 
			
		||||
        return None
 | 
			
		||||
    return obj
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,14 @@
 | 
			
		|||
# ~*~ coding: utf-8 ~*~
 | 
			
		||||
 | 
			
		||||
import pytz
 | 
			
		||||
from django.utils import timezone
 | 
			
		||||
from django.utils.deprecation import MiddlewareMixin
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TimezoneMiddleware(MiddlewareMixin):
 | 
			
		||||
    def process_request(self, request):
 | 
			
		||||
        tzname = request.META.get('TZ')
 | 
			
		||||
        if tzname:
 | 
			
		||||
            timezone.activate(pytz.timezone(tzname))
 | 
			
		||||
        else:
 | 
			
		||||
            timezone.deactivate()
 | 
			
		||||
| 
						 | 
				
			
			@ -79,6 +79,7 @@ MIDDLEWARE = [
 | 
			
		|||
    'django.contrib.auth.middleware.AuthenticationMiddleware',
 | 
			
		||||
    'django.contrib.messages.middleware.MessageMiddleware',
 | 
			
		||||
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
 | 
			
		||||
    'jumpserver.middleware.TimezoneMiddleware',
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
ROOT_URLCONF = 'jumpserver.urls'
 | 
			
		||||
| 
						 | 
				
			
			@ -323,3 +324,5 @@ CACHES = {
 | 
			
		|||
CAPTCHA_IMAGE_SIZE = (75, 33)
 | 
			
		||||
CAPTCHA_FOREGROUND_COLOR = '#001100'
 | 
			
		||||
 | 
			
		||||
COMMAND_STORE_BACKEND = 'audits.backends.command.db'
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -14,6 +14,7 @@ from .models import AssetPermission
 | 
			
		|||
from .hands import AssetGrantedSerializer, User, UserGroup, AssetGroup, Asset, \
 | 
			
		||||
    AssetGroup, AssetGroupSerializer, SystemUser
 | 
			
		||||
from . import serializers
 | 
			
		||||
from .utils import associate_system_users_and_assets
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class AssetPermissionViewSet(viewsets.ModelViewSet):
 | 
			
		||||
| 
						 | 
				
			
			@ -35,11 +36,32 @@ class AssetPermissionViewSet(viewsets.ModelViewSet):
 | 
			
		|||
            queryset = get_user_group_asset_permissions(user_group)
 | 
			
		||||
        return queryset
 | 
			
		||||
 | 
			
		||||
    # Todo: 忘记为何要重写get_serializer_class了
 | 
			
		||||
    def get_serializer_class(self):
 | 
			
		||||
        if getattr(self, 'user_id', ''):
 | 
			
		||||
            return serializers.UserAssetPermissionSerializer
 | 
			
		||||
        return serializers.AssetPermissionSerializer
 | 
			
		||||
 | 
			
		||||
    def associate_system_users_and_assets(self, serializer):
 | 
			
		||||
        assets = serializer.validated_data.get('assets', [])
 | 
			
		||||
        asset_groups = serializer.validated_data.get('asset_groups', [])
 | 
			
		||||
        system_users = serializer.validated_data.get('system_users', [])
 | 
			
		||||
        if serializer.partial:
 | 
			
		||||
            instance = self.get_object()
 | 
			
		||||
            assets.extend(list(instance.assets.all()))
 | 
			
		||||
            asset_groups.extend(list(instance.asset_groups.all()))
 | 
			
		||||
            system_users.extend(list(instance.system_users.all()))
 | 
			
		||||
        print('Run')
 | 
			
		||||
        associate_system_users_and_assets(system_users, assets, asset_groups)
 | 
			
		||||
 | 
			
		||||
    def perform_create(self, serializer):
 | 
			
		||||
        self.associate_system_users_and_assets(serializer)
 | 
			
		||||
        return super(AssetPermissionViewSet, self).perform_create(serializer)
 | 
			
		||||
 | 
			
		||||
    def perform_update(self, serializer):
 | 
			
		||||
        self.associate_system_users_and_assets(serializer)
 | 
			
		||||
        return super(AssetPermissionViewSet, self).perform_update(serializer)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class RevokeUserAssetPermission(APIView):
 | 
			
		||||
    permission_classes = (IsSuperUser,)
 | 
			
		||||
| 
						 | 
				
			
			@ -58,6 +80,27 @@ class RevokeUserAssetPermission(APIView):
 | 
			
		|||
        return Response({'msg': 'failed'}, status=404)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class RemoveSystemUserAssetPermission(APIView):
 | 
			
		||||
    """将系统用户从授权中移除, Detail页面会调用"""
 | 
			
		||||
    permission_classes = (IsSuperUser,)
 | 
			
		||||
 | 
			
		||||
    def put(self, request, *args, **kwargs):
 | 
			
		||||
        response = []
 | 
			
		||||
        asset_permission_id = kwargs.pop('pk')
 | 
			
		||||
        system_users_id = request.data.get('system_users')
 | 
			
		||||
        print(system_users_id)
 | 
			
		||||
        asset_permission = get_object_or_404(
 | 
			
		||||
            AssetPermission, id=asset_permission_id)
 | 
			
		||||
        if not isinstance(system_users_id, list):
 | 
			
		||||
            system_users_id = [system_users_id]
 | 
			
		||||
        for system_user_id in system_users_id:
 | 
			
		||||
            system_user = get_object_or_none(SystemUser, id=system_user_id)
 | 
			
		||||
            if system_user:
 | 
			
		||||
                asset_permission.system_users.remove(system_user)
 | 
			
		||||
                response.append(system_user.to_json())
 | 
			
		||||
        return Response(response, status=200)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class RevokeUserGroupAssetPermission(APIView):
 | 
			
		||||
    permission_classes = (IsSuperUser,)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -6,19 +6,9 @@ from django.utils.translation import ugettext_lazy as _
 | 
			
		|||
 | 
			
		||||
# from .hands import User, UserGroup, Asset, AssetGroup, SystemUser
 | 
			
		||||
from .models import AssetPermission
 | 
			
		||||
from .hands import associate_system_users_with_assets
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class AssetPermissionForm(forms.ModelForm):
 | 
			
		||||
    def save(self, commit=True):
 | 
			
		||||
        instance = super(AssetPermissionForm, self).save(commit=commit)
 | 
			
		||||
 | 
			
		||||
        assets = instance.assets.all()
 | 
			
		||||
        asset_groups = instance.asset_groups.all()
 | 
			
		||||
        system_users = instance.system_users.all()
 | 
			
		||||
        associate_system_users_with_assets(system_users, assets, asset_groups)
 | 
			
		||||
        return instance
 | 
			
		||||
 | 
			
		||||
    class Meta:
 | 
			
		||||
        model = AssetPermission
 | 
			
		||||
        fields = [
 | 
			
		||||
| 
						 | 
				
			
			@ -48,4 +38,3 @@ class AssetPermissionForm(forms.ModelForm):
 | 
			
		|||
            'asset_groups': '* Asset or Asset group at least one required',
 | 
			
		||||
            'system_users': '* required',
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,9 +7,9 @@ from assets.models import Asset, AssetGroup, SystemUser
 | 
			
		|||
from assets.serializers import AssetGrantedSerializer, AssetGroupSerializer
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def associate_system_users_with_assets(system_users, assets, asset_groups):
 | 
			
		||||
def push_system_user(assets, system_user):
 | 
			
		||||
    print('Push system user %s' % system_user.name)
 | 
			
		||||
    for asset in assets:
 | 
			
		||||
        asset.system_users.add(*tuple(system_users))
 | 
			
		||||
        print('\tAsset: %s' % asset.ip)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    for asset_group in asset_groups:
 | 
			
		||||
        asset_group.system_users.add(*tuple(system_users))
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,6 +4,7 @@ import functools
 | 
			
		|||
from django.db import models
 | 
			
		||||
from django.utils.translation import ugettext_lazy as _
 | 
			
		||||
from django.utils import timezone
 | 
			
		||||
from django.db.models.signals import m2m_changed
 | 
			
		||||
 | 
			
		||||
from users.models import User, UserGroup
 | 
			
		||||
from assets.models import Asset, AssetGroup, SystemUser
 | 
			
		||||
| 
						 | 
				
			
			@ -16,18 +17,26 @@ class AssetPermission(models.Model):
 | 
			
		|||
    #     ('U', 'user'),
 | 
			
		||||
    #     ('G', 'user group'),
 | 
			
		||||
    # )
 | 
			
		||||
    name = models.CharField(max_length=128, unique=True, verbose_name=_('Name'))
 | 
			
		||||
    users = models.ManyToManyField(User, related_name='asset_permissions', blank=True)
 | 
			
		||||
    user_groups = models.ManyToManyField(UserGroup, related_name='asset_permissions', blank=True)
 | 
			
		||||
    assets = models.ManyToManyField(Asset, related_name='granted_by_permissions', blank=True)
 | 
			
		||||
    asset_groups = models.ManyToManyField(AssetGroup, related_name='granted_by_permissions', blank=True)
 | 
			
		||||
    system_users = models.ManyToManyField(SystemUser, related_name='granted_by_permissions')
 | 
			
		||||
    # private_for = models.CharField(choices=PRIVATE_FOR_CHOICE, max_length=1, default='N', blank=True,
 | 
			
		||||
    #                                verbose_name=_('Private for'))
 | 
			
		||||
    is_active = models.BooleanField(default=True, verbose_name=_('Active'))
 | 
			
		||||
    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'))
 | 
			
		||||
    name = models.CharField(
 | 
			
		||||
        max_length=128, unique=True, verbose_name=_('Name'))
 | 
			
		||||
    users = models.ManyToManyField(
 | 
			
		||||
        User, related_name='asset_permissions', blank=True)
 | 
			
		||||
    user_groups = models.ManyToManyField(
 | 
			
		||||
        UserGroup, related_name='asset_permissions', blank=True)
 | 
			
		||||
    assets = models.ManyToManyField(
 | 
			
		||||
        Asset, related_name='granted_by_permissions', blank=True)
 | 
			
		||||
    asset_groups = models.ManyToManyField(
 | 
			
		||||
        AssetGroup, related_name='granted_by_permissions', blank=True)
 | 
			
		||||
    system_users = models.ManyToManyField(
 | 
			
		||||
        SystemUser, related_name='granted_by_permissions')
 | 
			
		||||
    is_active = models.BooleanField(
 | 
			
		||||
        default=True, verbose_name=_('Active'))
 | 
			
		||||
    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)
 | 
			
		||||
 | 
			
		||||
    def __unicode__(self):
 | 
			
		||||
| 
						 | 
				
			
			@ -68,3 +77,15 @@ class AssetPermission(models.Model):
 | 
			
		|||
    class Meta:
 | 
			
		||||
        db_table = 'asset_permission'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# def change_permission(sender, **kwargs):
 | 
			
		||||
#     print('Sender: %s' % sender)
 | 
			
		||||
#     for k, v in kwargs.items():
 | 
			
		||||
#         print('%s: %s' % (k, v))
 | 
			
		||||
#     print()
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# m2m_changed.connect(change_permission, sender=AssetPermission.assets.through)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -169,16 +169,16 @@
 | 
			
		|||
                                            </tr>
 | 
			
		||||
                                            <tr>
 | 
			
		||||
                                                <td colspan="2" class="no-borders">
 | 
			
		||||
                                                    <button type="button" class="btn btn-info btn-small" id="btn_add_user_group">{% trans 'Join' %}</button>
 | 
			
		||||
                                                    <button type="button" class="btn btn-info btn-small" id="btn_add_system_user">{% trans 'Join' %}</button>
 | 
			
		||||
                                                </td>
 | 
			
		||||
                                            </tr>
 | 
			
		||||
                                        </form>
 | 
			
		||||
 | 
			
		||||
                                        {% for system_user in system_users %}
 | 
			
		||||
                                        <tr>
 | 
			
		||||
                                          <td ><b class="bdg_user_group" data-gid={{ system_user.id }}>{{ system_user.name }}</b></td>
 | 
			
		||||
                                          <td ><b class="bdg-system-user" data-uid={{ system_user.id }}>{{ system_user.name }}</b></td>
 | 
			
		||||
                                          <td>
 | 
			
		||||
                                              <button class="btn btn-danger btn-xs btn_delete_user_group" type="button" style="float: right;"><i class="fa fa-minus"></i></button>
 | 
			
		||||
                                              <button class="btn btn-danger btn-xs btn-del" data-uid="{{ system_user.id }}" type="button" style="float: right;"><i class="fa fa-minus"></i></button>
 | 
			
		||||
                                          </td>
 | 
			
		||||
                                        </tr>
 | 
			
		||||
                                        {% endfor %}
 | 
			
		||||
| 
						 | 
				
			
			@ -196,8 +196,47 @@
 | 
			
		|||
{% endblock %}
 | 
			
		||||
{% block custom_foot_js %}
 | 
			
		||||
    <script>
 | 
			
		||||
        jumpserver.system_users_selected = {};
 | 
			
		||||
        function addSystemUser(system_users) {
 | 
			
		||||
            var the_url = "{% url 'api-perms:asset-permission-detail' pk=asset_permission.id %}";
 | 
			
		||||
            var body = {
 | 
			
		||||
                system_users: Object.assign([], system_users)
 | 
			
		||||
            };
 | 
			
		||||
            var success = function(data) {
 | 
			
		||||
                window.location.reload();
 | 
			
		||||
            };
 | 
			
		||||
            APIUpdateAttr({
 | 
			
		||||
                url: the_url,
 | 
			
		||||
                body: JSON.stringify(body),
 | 
			
		||||
                success: success
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
        function removeSystemUser(system_users, tr) {
 | 
			
		||||
            var the_url = "{% url 'api-perms:remove-system-user-asset-permission' pk=asset_permission.id %}";
 | 
			
		||||
            var body = {
 | 
			
		||||
                system_users: system_users
 | 
			
		||||
            };
 | 
			
		||||
            var success = function (data) {
 | 
			
		||||
                tr.remove()
 | 
			
		||||
            };
 | 
			
		||||
            APIUpdateAttr({
 | 
			
		||||
                url: the_url,
 | 
			
		||||
                body: JSON.stringify(body),
 | 
			
		||||
                method: 'PUT',
 | 
			
		||||
                success: success
 | 
			
		||||
            })
 | 
			
		||||
        }
 | 
			
		||||
        $(document).ready(function () {
 | 
			
		||||
            $('.select2').select2();
 | 
			
		||||
            $('.select2').select2()
 | 
			
		||||
                .on('select2:select', function(evt) {
 | 
			
		||||
                     var data = evt.params.data;
 | 
			
		||||
                     jumpserver.system_users_selected[data.id] = data.text;
 | 
			
		||||
                })
 | 
			
		||||
                .on('select2:unselect', function(evt) {
 | 
			
		||||
                    var data = evt.params.data;
 | 
			
		||||
                    delete jumpserver.system_users_selected[data.id]
 | 
			
		||||
                })
 | 
			
		||||
 | 
			
		||||
        }).on('click', '.btn-delete-perm', function () {
 | 
			
		||||
            var $this = $(this);
 | 
			
		||||
            var name = "{{ asset_permission.name }}";
 | 
			
		||||
| 
						 | 
				
			
			@ -205,6 +244,28 @@
 | 
			
		|||
            var the_url = '{% url "api-perms:asset-permission-detail" pk=99991937 %}'.replace('99991937', uid);
 | 
			
		||||
            var redirect_url = "{% url 'perms:asset-permission-list' %}";
 | 
			
		||||
            objectDelete($this, name, the_url, redirect_url);
 | 
			
		||||
        }).on('click', '#btn_add_system_user', function () {
 | 
			
		||||
            if (Object.keys(jumpserver.system_users_selected).length === 0) {
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
            var system_users = $('.bdg-system-user').map(function() {
 | 
			
		||||
                return $(this).data('uid');
 | 
			
		||||
            }).get();
 | 
			
		||||
            $.map(jumpserver.system_users_selected, function(value, index) {
 | 
			
		||||
                system_users.push(parseInt(index));
 | 
			
		||||
                $('#opt_' + index).remove();
 | 
			
		||||
            });
 | 
			
		||||
            addSystemUser(system_users)
 | 
			
		||||
        }).on('click', '.btn-del', function () {
 | 
			
		||||
            var $this = $(this);
 | 
			
		||||
            var $uid = $this.data('uid');
 | 
			
		||||
            var $tr = $this.closest('tr');
 | 
			
		||||
            var $badge = $tr.find('.bdg-system-user');
 | 
			
		||||
            var $system_user = $badge.html() || $badge.text();
 | 
			
		||||
            $('#groups_selected').append(
 | 
			
		||||
                '<option value="' + $uid + '" id="opt_' + $uid + '">' + $system_user + '</option>'
 | 
			
		||||
            );
 | 
			
		||||
            removeSystemUser($uid, $tr)
 | 
			
		||||
        })
 | 
			
		||||
    </script>
 | 
			
		||||
{% endblock %}
 | 
			
		||||
| 
						 | 
				
			
			@ -108,7 +108,7 @@
 | 
			
		|||
                                        <form>
 | 
			
		||||
                                            <tr class="no-borders-tr">
 | 
			
		||||
                                                <td colspan="2">
 | 
			
		||||
                                                    <select data-placeholder="{% trans 'Select user' %}" class="select2" style="width: 100%" multiple="" tabindex="4">
 | 
			
		||||
                                                    <select data-placeholder="{% trans 'Select user' %}" class="select2 user" style="width: 100%" multiple="" tabindex="4">
 | 
			
		||||
                                                        {% for user in users_remain %}
 | 
			
		||||
                                                            <option value="{{ user.id }}">{{ user.name }}: {{ user.username }}</option>
 | 
			
		||||
                                                        {% endfor %}
 | 
			
		||||
| 
						 | 
				
			
			@ -117,7 +117,7 @@
 | 
			
		|||
                                            </tr>
 | 
			
		||||
                                            <tr class="no-borders-tr">
 | 
			
		||||
                                                <td colspan="2">
 | 
			
		||||
                                                    <button type="button" class="btn btn-primary btn-sm">{% trans 'Add' %}</button>
 | 
			
		||||
                                                    <button type="button" class="btn btn-primary btn-sm btn-add-user">{% trans 'Add' %}</button>
 | 
			
		||||
                                                </td>
 | 
			
		||||
                                            </tr>
 | 
			
		||||
                                        </form>
 | 
			
		||||
| 
						 | 
				
			
			@ -136,7 +136,7 @@
 | 
			
		|||
                                        <form>
 | 
			
		||||
                                            <tr>
 | 
			
		||||
                                                <td colspan="2" class="no-borders">
 | 
			
		||||
                                                    <select data-placeholder="{% trans 'Select user groups' %}" class="select2" style="width: 100%" multiple="" tabindex="4">
 | 
			
		||||
                                                    <select data-placeholder="{% trans 'Select user groups' %}" class="select2 user-group" style="width: 100%" multiple="" tabindex="4">
 | 
			
		||||
                                                        {% for user_group in user_groups_remain %}
 | 
			
		||||
                                                        <option value="{{ user_group.id }}" id="opt_{{ user_group.id }}">{{ user_group.name }}</option>
 | 
			
		||||
                                                        {% endfor %}
 | 
			
		||||
| 
						 | 
				
			
			@ -145,7 +145,7 @@
 | 
			
		|||
                                            </tr>
 | 
			
		||||
                                            <tr>
 | 
			
		||||
                                                <td colspan="2" class="no-borders">
 | 
			
		||||
                                                    <button type="button" class="btn btn-info btn-small" id="btn_add_user_group">{% trans 'Join' %}</button>
 | 
			
		||||
                                                    <button type="button" class="btn btn-info btn-small" id="btn_add_user_group">{% trans 'Add' %}</button>
 | 
			
		||||
                                                </td>
 | 
			
		||||
                                            </tr>
 | 
			
		||||
                                        </form>
 | 
			
		||||
| 
						 | 
				
			
			@ -172,25 +172,29 @@
 | 
			
		|||
{% endblock %}
 | 
			
		||||
{% block custom_foot_js %}
 | 
			
		||||
    <script>
 | 
			
		||||
{#        function switch_user_status(obj) {#}
 | 
			
		||||
{#            var status = $(obj).prop('checked');#}
 | 
			
		||||
{##}
 | 
			
		||||
{#            $.ajax({#}
 | 
			
		||||
{#                url: "{% url 'users:user-active-api' pk=user.id %}",#}
 | 
			
		||||
{#                type: "PUT",#}
 | 
			
		||||
{#                data: {#}
 | 
			
		||||
{#                    'is_active': status#}
 | 
			
		||||
{#                },#}
 | 
			
		||||
{#                success: function (data, status) {#}
 | 
			
		||||
{#                    console.log(data)#}
 | 
			
		||||
{#                },#}
 | 
			
		||||
{#                error: function () {#}
 | 
			
		||||
{#                    console.log('error')#}
 | 
			
		||||
{#                }#}
 | 
			
		||||
{#            })#}
 | 
			
		||||
{#        }#}
 | 
			
		||||
        jumpserver.users_selected = {};
 | 
			
		||||
        jumpserver.user_groups_selected = {};
 | 
			
		||||
        $(document).ready(function () {
 | 
			
		||||
            $('.select2').select2();
 | 
			
		||||
        });
 | 
			
		||||
            $('.select2.user').select2()
 | 
			
		||||
                .on('select2:select', function(evt) {
 | 
			
		||||
                     var data = evt.params.data;
 | 
			
		||||
                     jumpserver.users_selected[data.id] = data.text;
 | 
			
		||||
                })
 | 
			
		||||
                .on('select2:unselect', function(evt) {
 | 
			
		||||
                    var data = evt.params.data;
 | 
			
		||||
                    delete jumpserver.users_selected[data.id]
 | 
			
		||||
                });
 | 
			
		||||
            $('.select2.user-group').select2()
 | 
			
		||||
                .on('select2:select', function(evt) {
 | 
			
		||||
                     var data = evt.params.data;
 | 
			
		||||
                     jumpserver.user_groups_selected[data.id] = data.text;
 | 
			
		||||
                })
 | 
			
		||||
                .on('select2:unselect', function(evt) {
 | 
			
		||||
                    var data = evt.params.data;
 | 
			
		||||
                    delete jumpserver.user_groups_selected[data.id]
 | 
			
		||||
                })
 | 
			
		||||
        }).on('click', '.btn-add-user', function () {
 | 
			
		||||
            console.log(jumpserver.users_selected)
 | 
			
		||||
        })
 | 
			
		||||
    </script>
 | 
			
		||||
{% endblock %}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,3 +1,4 @@
 | 
			
		|||
from django.test import TestCase
 | 
			
		||||
 | 
			
		||||
# Create your tests here.
 | 
			
		||||
from django.contrib.sessions.backends import file, db, cache
 | 
			
		||||
from django.contrib.auth.views import login
 | 
			
		||||
| 
						 | 
				
			
			@ -50,7 +50,12 @@ urlpatterns = [
 | 
			
		|||
    # 验证用户是否有某个资产和系统用户的权限
 | 
			
		||||
    url(r'v1/asset-permission/user/validate/$',
 | 
			
		||||
        api.ValidateUserAssetPermissionView.as_view(),
 | 
			
		||||
        name='validate-user-asset-permission')
 | 
			
		||||
        name='validate-user-asset-permission'),
 | 
			
		||||
 | 
			
		||||
    # 删除asset permission中的某个系统用户
 | 
			
		||||
    url(r'^v1/asset-permissions/(?P<pk>[0-9]+)/system-user/remove/$',
 | 
			
		||||
        api.RemoveSystemUserAssetPermission.as_view(),
 | 
			
		||||
        name='remove-system-user-asset-permission'),
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
urlpatterns += router.urls
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,14 +1,18 @@
 | 
			
		|||
# coding: utf-8
 | 
			
		||||
 | 
			
		||||
from __future__ import absolute_import, unicode_literals
 | 
			
		||||
 | 
			
		||||
from common.utils import setattr_bulk
 | 
			
		||||
from .hands import User, UserGroup, Asset, AssetGroup, SystemUser
 | 
			
		||||
from .hands import User, UserGroup, Asset, AssetGroup, SystemUser, \
 | 
			
		||||
    push_system_user
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_user_group_granted_asset_groups(user_group):
 | 
			
		||||
    """Return asset groups granted of the user group
 | 
			
		||||
 | 
			
		||||
     :param user_group: Instance of :class: ``UserGroup``
 | 
			
		||||
     :return: {asset_group1: {system_user1, }, asset_group2: {system_user1, system_user2}}
 | 
			
		||||
     :return: {asset_group1: {system_user1, },
 | 
			
		||||
               asset_group2: {system_user1, system_user2}}
 | 
			
		||||
    """
 | 
			
		||||
    asset_groups = {}
 | 
			
		||||
    asset_permissions = user_group.asset_permissions.all()
 | 
			
		||||
| 
						 | 
				
			
			@ -50,7 +54,8 @@ def get_user_granted_asset_groups_direct(user):
 | 
			
		|||
    """Return asset groups granted of the user direct nor inherit from user group
 | 
			
		||||
 | 
			
		||||
        :param user: Instance of :class: ``User``
 | 
			
		||||
        :return: {asset_group: {system_user1, }, asset_group2: {system_user1, system_user2]}
 | 
			
		||||
        :return: {asset_group: {system_user1, },
 | 
			
		||||
                  asset_group2: {system_user1, system_user2]}
 | 
			
		||||
        """
 | 
			
		||||
    asset_groups = {}
 | 
			
		||||
    asset_permissions_direct = user.asset_permissions.all()
 | 
			
		||||
| 
						 | 
				
			
			@ -72,7 +77,8 @@ def get_user_granted_asset_groups_inherit_from_user_groups(user):
 | 
			
		|||
    """Return asset groups granted of the user and inherit from user group
 | 
			
		||||
 | 
			
		||||
    :param user: Instance of :class: ``User``
 | 
			
		||||
    :return: {asset_group: {system_user1, }, asset_group2: {system_user1, system_user2]}
 | 
			
		||||
    :return: {asset_group: {system_user1, },
 | 
			
		||||
              asset_group2: {system_user1, system_user2]}
 | 
			
		||||
    """
 | 
			
		||||
    asset_groups = {}
 | 
			
		||||
    user_groups = user.groups.all()
 | 
			
		||||
| 
						 | 
				
			
			@ -103,7 +109,8 @@ def get_user_granted_asset_groups(user):
 | 
			
		|||
    :return: {asset1: {system_user1, system_user2}, asset2: {...}}
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    asset_groups_inherit_from_user_groups = get_user_granted_asset_groups_inherit_from_user_groups(user)
 | 
			
		||||
    asset_groups_inherit_from_user_groups = \
 | 
			
		||||
        get_user_granted_asset_groups_inherit_from_user_groups(user)
 | 
			
		||||
    asset_groups_direct = get_user_granted_asset_groups_direct(user)
 | 
			
		||||
    asset_groups = asset_groups_inherit_from_user_groups
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -211,3 +218,27 @@ def get_user_groups_granted_in_asset_group(asset):
 | 
			
		|||
 | 
			
		||||
def get_users_granted_in_asset_group(asset):
 | 
			
		||||
    pass
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def associate_system_users_and_assets(system_users, assets, asset_groups):
 | 
			
		||||
    """关联系统用户和资产, 目的是保存它们的关系, 然后新加入的资产或系统
 | 
			
		||||
    用户时,推送系统用户到资产
 | 
			
		||||
 | 
			
		||||
    Todo: 这里需要最终Api定下来更改一下, 现在策略是以系统用户为核心推送, 一个系统用户
 | 
			
		||||
    推送一次
 | 
			
		||||
    """
 | 
			
		||||
    assets_all = set(assets)
 | 
			
		||||
 | 
			
		||||
    for asset_group in asset_groups:
 | 
			
		||||
        assets_all |= set(asset_group.assets.all())
 | 
			
		||||
 | 
			
		||||
    for system_user in system_users:
 | 
			
		||||
        assets_need_push = []
 | 
			
		||||
        if system_user.auto_push:
 | 
			
		||||
            assets_need_push.extend(
 | 
			
		||||
                [asset for asset in assets_all
 | 
			
		||||
                 if asset not in system_user.assets.all()
 | 
			
		||||
                 ]
 | 
			
		||||
            )
 | 
			
		||||
        system_user.assets.add(*(tuple(assets_all)))
 | 
			
		||||
        push_system_user(assets_need_push, system_user)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -6,16 +6,18 @@ import functools
 | 
			
		|||
from django.utils.translation import ugettext as _
 | 
			
		||||
from django.conf import settings
 | 
			
		||||
from django.db.models import Q
 | 
			
		||||
from django.views.generic import TemplateView, ListView
 | 
			
		||||
from django.views.generic.edit import CreateView, DeleteView, FormView, UpdateView
 | 
			
		||||
from django.views.generic import ListView, CreateView, UpdateView
 | 
			
		||||
from django.views.generic.edit import DeleteView, FormView
 | 
			
		||||
from django.urls import reverse_lazy
 | 
			
		||||
from django.contrib.messages.views import SuccessMessageMixin
 | 
			
		||||
from django.views.generic.detail import DetailView, SingleObjectMixin
 | 
			
		||||
 | 
			
		||||
from common.utils import search_object_attr
 | 
			
		||||
from .hands import AdminUserRequiredMixin, User, UserGroup, SystemUser, Asset, AssetGroup
 | 
			
		||||
from .hands import AdminUserRequiredMixin, User, UserGroup, SystemUser, \
 | 
			
		||||
    Asset, AssetGroup
 | 
			
		||||
from .models import AssetPermission
 | 
			
		||||
from .forms import AssetPermissionForm
 | 
			
		||||
from .utils import associate_system_users_and_assets
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class AssetPermissionListView(AdminUserRequiredMixin, ListView):
 | 
			
		||||
| 
						 | 
				
			
			@ -79,6 +81,16 @@ class AssetPermissionCreateView(AdminUserRequiredMixin,
 | 
			
		|||
                               self.object.name,))
 | 
			
		||||
        return success_message
 | 
			
		||||
 | 
			
		||||
    def form_valid(self, form):
 | 
			
		||||
        assets = form.cleaned_data['assets']
 | 
			
		||||
        asset_groups = form.cleaned_data['asset_groups']
 | 
			
		||||
        system_users = form.cleaned_data['system_users']
 | 
			
		||||
        associate_system_users_and_assets(system_users, assets, asset_groups)
 | 
			
		||||
        response = super(AssetPermissionCreateView, self).form_valid(form)
 | 
			
		||||
        self.object.created_by = self.request.user.name
 | 
			
		||||
        self.object.save()
 | 
			
		||||
        return response
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class AssetPermissionUpdateView(AdminUserRequiredMixin, UpdateView):
 | 
			
		||||
    model = AssetPermission
 | 
			
		||||
| 
						 | 
				
			
			@ -100,6 +112,13 @@ class AssetPermissionUpdateView(AdminUserRequiredMixin, UpdateView):
 | 
			
		|||
                                   kwargs={'pk': self.object.pk})
 | 
			
		||||
        return success_url
 | 
			
		||||
 | 
			
		||||
    def form_valid(self, form):
 | 
			
		||||
        assets = form.cleaned_data['assets']
 | 
			
		||||
        asset_groups = form.cleaned_data['asset_groups']
 | 
			
		||||
        system_users = form.cleaned_data['system_users']
 | 
			
		||||
        associate_system_users_and_assets(system_users, assets, asset_groups)
 | 
			
		||||
        return super(AssetPermissionUpdateView, self).form_valid(form)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class AssetPermissionDetailView(AdminUserRequiredMixin, DetailView):
 | 
			
		||||
    template_name = 'perms/asset_permission_detail.html'
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -187,28 +187,28 @@ function activeNav() {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
function APIUpdateAttr(props) {
 | 
			
		||||
  // props = {url: .., body: , success: , error: , method: ,}
 | 
			
		||||
  props = props || {};
 | 
			
		||||
  var success_message = props.success_message || 'Update Successfully!';
 | 
			
		||||
  var fail_message = props.fail_message || 'Error occurred while updating.';
 | 
			
		||||
  $.ajax({
 | 
			
		||||
    url: props.url,
 | 
			
		||||
    type: props.method || "PATCH",
 | 
			
		||||
    data: props.body,
 | 
			
		||||
    contentType: props.content_type || "application/json; charset=utf-8",
 | 
			
		||||
    dataType: props.data_type || "json"
 | 
			
		||||
  }).done(function(data, textStatue, jqXHR) {
 | 
			
		||||
    toastr.success(success_message);
 | 
			
		||||
    if (typeof props.success === 'function') {
 | 
			
		||||
      return props.success(data);
 | 
			
		||||
    } 
 | 
			
		||||
    
 | 
			
		||||
  }).fail(function(jqXHR, textStatue, errorThrown) {
 | 
			
		||||
    toastr.error(fail_message);
 | 
			
		||||
    if (typeof props.error === 'function') {
 | 
			
		||||
      return props.error(errorThrown);
 | 
			
		||||
    } 
 | 
			
		||||
  });
 | 
			
		||||
    // props = {url: .., body: , success: , error: , method: ,}
 | 
			
		||||
    props = props || {};
 | 
			
		||||
    var success_message = props.success_message || 'Update Successfully!';
 | 
			
		||||
    var fail_message = props.fail_message || 'Error occurred while updating.';
 | 
			
		||||
    $.ajax({
 | 
			
		||||
        url: props.url,
 | 
			
		||||
        type: props.method || "PATCH",
 | 
			
		||||
        data: props.body,
 | 
			
		||||
        contentType: props.content_type || "application/json; charset=utf-8",
 | 
			
		||||
        dataType: props.data_type || "json"
 | 
			
		||||
    }).done(function(data, textStatue, jqXHR) {
 | 
			
		||||
        toastr.success(success_message);
 | 
			
		||||
        if (typeof props.success === 'function') {
 | 
			
		||||
            return props.success(data);
 | 
			
		||||
        } 
 | 
			
		||||
      
 | 
			
		||||
    }).fail(function(jqXHR, textStatue, errorThrown) {
 | 
			
		||||
        toastr.error(fail_message);
 | 
			
		||||
        if (typeof props.error === 'function') {
 | 
			
		||||
            return props.error(errorThrown);
 | 
			
		||||
        } 
 | 
			
		||||
    });
 | 
			
		||||
  // return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -107,7 +107,6 @@ class AccessKeyAuthentication(authentication.BaseAuthentication):
 | 
			
		|||
 | 
			
		||||
        if not access_key.user.is_active:
 | 
			
		||||
            raise exceptions.AuthenticationFailed(_('User disabled.'))
 | 
			
		||||
 | 
			
		||||
        return access_key.user, None
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -31,7 +31,9 @@ class UserCreateUpdateForm(forms.ModelForm):
 | 
			
		|||
            'email': '* required',
 | 
			
		||||
        }
 | 
			
		||||
        widgets = {
 | 
			
		||||
            'groups': forms.SelectMultiple(attrs={'class': 'select2', 'data-placeholder': _('Join user groups')}),
 | 
			
		||||
            'groups': forms.SelectMultiple(
 | 
			
		||||
                attrs={'class': 'select2',
 | 
			
		||||
                       'data-placeholder': _('Join user groups')}),
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -41,29 +43,11 @@ class UserBulkImportForm(forms.ModelForm):
 | 
			
		|||
        fields = ['username', 'email', 'enable_otp', 'role']
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# class UserUpdateForm(forms.ModelForm):
 | 
			
		||||
#
 | 
			
		||||
#     class Meta:
 | 
			
		||||
#         model = User
 | 
			
		||||
#         fields = [
 | 
			
		||||
#             'name', 'email', 'groups', 'wechat',
 | 
			
		||||
#             'phone', 'enable_otp', 'role', 'date_expired', 'comment',
 | 
			
		||||
#         ]
 | 
			
		||||
#         help_texts = {
 | 
			
		||||
#             'username': '* required',
 | 
			
		||||
#             'email': '* required',
 | 
			
		||||
#             'groups': '* required'
 | 
			
		||||
#         }
 | 
			
		||||
#         widgets = {
 | 
			
		||||
#             'groups': forms.SelectMultiple(attrs={'class': 'select2', 'data-placeholder': _('Join user groups')}),
 | 
			
		||||
#         }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class UserGroupForm(forms.ModelForm):
 | 
			
		||||
    class Meta:
 | 
			
		||||
        model = UserGroup
 | 
			
		||||
        fields = [
 | 
			
		||||
            'name', 'comment',
 | 
			
		||||
            'name', 'comment'
 | 
			
		||||
        ]
 | 
			
		||||
        help_texts = {
 | 
			
		||||
            'name': '* required'
 | 
			
		||||
| 
						 | 
				
			
			@ -87,7 +71,8 @@ class UserKeyForm(forms.Form):
 | 
			
		|||
    def clean_public_key(self):
 | 
			
		||||
        public_key = self.cleaned_data['public_key']
 | 
			
		||||
        if self.user.public_key and public_key == self.user.public_key:
 | 
			
		||||
            raise forms.ValidationError(_('Public key should not be the same as your old one.'))
 | 
			
		||||
            raise forms.ValidationError(_('Public key should not be the '
 | 
			
		||||
                                          'same as your old one.'))
 | 
			
		||||
 | 
			
		||||
        if not validate_ssh_public_key(public_key):
 | 
			
		||||
            raise forms.ValidationError(_('Not a valid ssh public key'))
 | 
			
		||||
| 
						 | 
				
			
			@ -97,7 +82,8 @@ class UserKeyForm(forms.Form):
 | 
			
		|||
class UserPrivateAssetPermissionForm(forms.ModelForm):
 | 
			
		||||
 | 
			
		||||
    def save(self, commit=True):
 | 
			
		||||
        self.instance = super(UserPrivateAssetPermissionForm, self).save(commit=commit)
 | 
			
		||||
        self.instance = super(UserPrivateAssetPermissionForm, self)\
 | 
			
		||||
            .save(commit=commit)
 | 
			
		||||
        self.instance.users = [self.user]
 | 
			
		||||
        self.instance.save()
 | 
			
		||||
        return self.instance
 | 
			
		||||
| 
						 | 
				
			
			@ -108,19 +94,23 @@ class UserPrivateAssetPermissionForm(forms.ModelForm):
 | 
			
		|||
            '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')}),
 | 
			
		||||
            '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 = super(UserGroupPrivateAssetPermissionForm, self)\
 | 
			
		||||
            .save(commit=commit)
 | 
			
		||||
        self.instance.user_groups = [self.user_group]
 | 
			
		||||
        self.instance.save()
 | 
			
		||||
        return self.instance
 | 
			
		||||
| 
						 | 
				
			
			@ -131,12 +121,15 @@ class UserGroupPrivateAssetPermissionForm(forms.ModelForm):
 | 
			
		|||
            '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')}),
 | 
			
		||||
            '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')}),
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -44,6 +44,7 @@ class UserGroupCreateView(AdminUserRequiredMixin, CreateView):
 | 
			
		|||
                        'users': users})
 | 
			
		||||
        return context
 | 
			
		||||
 | 
			
		||||
    # 需要添加组下用户, 而user并不是group的多对多,所以需要手动建立关系
 | 
			
		||||
    def form_valid(self, form):
 | 
			
		||||
        user_group = form.save()
 | 
			
		||||
        users_id_list = self.request.POST.getlist('users', [])
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue