From a79c3dd15624e39d9f3644088a9da1301bf18444 Mon Sep 17 00:00:00 2001 From: ibuler Date: Mon, 6 Feb 2017 23:13:27 +0800 Subject: [PATCH] =?UTF-8?q?[Fixture]=20=E6=B7=BB=E5=8A=A0command=20log=20b?= =?UTF-8?q?ackends,=20=E6=9C=AA=E6=9D=A5=E6=94=AF=E6=8C=81es?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/applications/api.py | 3 +- apps/assets/models/user.py | 10 ++ apps/assets/views.py | 16 +- apps/audits/__init__.py | 1 + apps/audits/api.py | 24 ++- apps/audits/backends/__init__.py | 6 + .../audits/backends/command/__init__.py | 0 apps/audits/backends/command/base.py | 19 +++ apps/audits/backends/command/db.py | 45 ++++++ apps/audits/backends/command/serializers.py | 21 +++ apps/audits/models.py | 73 ++++----- apps/audits/serializers.py | 12 +- .../templates/audits/command_log_list.html | 31 ++-- .../templates/audits/proxy_log_detail.html | 6 +- .../templates/audits/proxy_log_list.html | 69 ++++---- apps/audits/urls/api_urls.py | 3 +- apps/audits/views.py | 153 ++++++++++-------- apps/common/templatetags/common_tags.py | 27 +++- apps/common/utils.py | 10 +- apps/jumpserver/middleware.py | 14 ++ apps/jumpserver/settings.py | 3 + apps/perms/api.py | 43 +++++ apps/perms/forms.py | 11 -- apps/perms/hands.py | 8 +- apps/perms/models.py | 45 ++++-- .../perms/asset_permission_detail.html | 69 +++++++- .../perms/asset_permission_user.html | 50 +++--- apps/perms/tests.py | 3 +- apps/perms/urls/api_urls.py | 7 +- apps/perms/utils.py | 41 ++++- apps/perms/views.py | 25 ++- apps/static/js/jumpserver.js | 44 ++--- apps/users/authentication.py | 1 - apps/users/forms.py | 63 ++++---- apps/users/views/group.py | 1 + .../deb_requirements.txt | 0 requirements/mac_requirements.py | 0 .../requirements.txt | 0 .../rpm_requirements.txt | 0 39 files changed, 646 insertions(+), 311 deletions(-) create mode 100644 apps/audits/backends/__init__.py rename mac_requirements.py => apps/audits/backends/command/__init__.py (100%) create mode 100644 apps/audits/backends/command/base.py create mode 100644 apps/audits/backends/command/db.py create mode 100644 apps/audits/backends/command/serializers.py create mode 100644 apps/jumpserver/middleware.py rename deb_requirements.txt => requirements/deb_requirements.txt (100%) create mode 100644 requirements/mac_requirements.py rename requirements.txt => requirements/requirements.txt (100%) rename rpm_requirements.txt => requirements/rpm_requirements.txt (100%) diff --git a/apps/applications/api.py b/apps/applications/api.py index a54c382b9..6a6b348e6 100644 --- a/apps/applications/api.py +++ b/apps/applications/api.py @@ -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 diff --git a/apps/assets/models/user.py b/apps/assets/models/user.py index 584472146..5440d3910 100644 --- a/apps/assets/models/user.py +++ b/apps/assets/models/user.py @@ -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'] diff --git a/apps/assets/views.py b/apps/assets/views.py index 66a8c8531..497bf2a31 100644 --- a/apps/assets/views.py +++ b/apps/assets/views.py @@ -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) diff --git a/apps/audits/__init__.py b/apps/audits/__init__.py index e69de29bb..8b1378917 100644 --- a/apps/audits/__init__.py +++ b/apps/audits/__init__.py @@ -0,0 +1 @@ + diff --git a/apps/audits/api.py b/apps/audits/api.py index 15a0e5ce4..c83517afd 100644 --- a/apps/audits/api.py +++ b/apps/audits/api.py @@ -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,) diff --git a/apps/audits/backends/__init__.py b/apps/audits/backends/__init__.py new file mode 100644 index 000000000..432716414 --- /dev/null +++ b/apps/audits/backends/__init__.py @@ -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 diff --git a/mac_requirements.py b/apps/audits/backends/command/__init__.py similarity index 100% rename from mac_requirements.py rename to apps/audits/backends/command/__init__.py diff --git a/apps/audits/backends/command/base.py b/apps/audits/backends/command/base.py new file mode 100644 index 000000000..e5bb696fa --- /dev/null +++ b/apps/audits/backends/command/base.py @@ -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 + + + diff --git a/apps/audits/backends/command/db.py b/apps/audits/backends/command/db.py new file mode 100644 index 000000000..91d33c79d --- /dev/null +++ b/apps/audits/backends/command/db.py @@ -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() + diff --git a/apps/audits/backends/command/serializers.py b/apps/audits/backends/command/serializers.py new file mode 100644 index 000000000..b7f5edc97 --- /dev/null +++ b/apps/audits/backends/command/serializers.py @@ -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)) diff --git a/apps/audits/models.py b/apps/audits/models.py index a6b1c1133..92687d76b 100644 --- a/apps/audits/models.py +++ b/apps/audits/models.py @@ -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', '
') - 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 diff --git a/apps/audits/serializers.py b/apps/audits/serializers.py index 7212c0d97..2f474ff7e 100644 --- a/apps/audits/serializers.py +++ b/apps/audits/serializers.py @@ -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__' diff --git a/apps/audits/templates/audits/command_log_list.html b/apps/audits/templates/audits/command_log_list.html index 5e0cf0752..3ed7d5d0b 100644 --- a/apps/audits/templates/audits/command_log_list.html +++ b/apps/audits/templates/audits/command_log_list.html @@ -23,32 +23,31 @@
- - {% for user in user_list %} - + {% for u in user_list %} + {% endfor %}
- - {% for asset in asset_list %} - + {% for a in asset_list %} + {% endfor %}
- +
@@ -78,12 +77,12 @@ {{ command.id }} {{ command.command }} - {{ command.proxy_log.username }} - {{ command.proxy_log.ip }} - {{ command.proxy_log.system_user }} - {{ command.proxy_log.id}} - {{ command.datetime }} - {{ command.output_decode|safe }} + {{ command.user }} + {{ command.asset }} + {{ command.system_user }} + {{ command.proxy_log_id}} + {{ command.timestamp|ts_to_date }} +
{{ command.output|to_html|safe }}
{% endfor %} diff --git a/apps/audits/templates/audits/proxy_log_detail.html b/apps/audits/templates/audits/proxy_log_detail.html index 05b06071c..c1b1dea57 100644 --- a/apps/audits/templates/audits/proxy_log_detail.html +++ b/apps/audits/templates/audits/proxy_log_detail.html @@ -19,7 +19,7 @@
-
+
{% trans 'Command log list' %} {{ user_object.name }} @@ -52,8 +52,8 @@ {{ command.command_no }} {{ command.command }} - {{ command.output_decode|safe }} - {{ command.datetime }} +
{{ command.output|to_html|safe}}
+ {{ command.timestamp|ts_to_date}} {% endfor %} diff --git a/apps/audits/templates/audits/proxy_log_list.html b/apps/audits/templates/audits/proxy_log_list.html index d28548e47..f2f983641 100644 --- a/apps/audits/templates/audits/proxy_log_list.html +++ b/apps/audits/templates/audits/proxy_log_list.html @@ -47,7 +47,7 @@
- +
@@ -61,44 +61,53 @@ {% block table_head %} {% trans 'ID' %} - {% trans 'Username' %} - {% trans 'IP' %} + {% trans 'User' %} + {% trans 'Asset' %} {% trans 'System user' %} {% trans 'Command' %} {% trans 'Success' %} {% trans 'Finished' %} + {% trans 'R/M' %} {% trans 'Date start' %} {% trans 'Time' %} {% endblock %} {% block table_body %} - {% for proxy_log in proxy_log_list %} - - - {{ proxy_log.id }} - - {{ proxy_log.username }} - {{ proxy_log.ip }} - {{ proxy_log.system_user }} - {{ proxy_log.commands.all|length}} - - {% if proxy_log.was_failed %} - - {% else %} - - {% endif %} - - - {% if proxy_log.is_finished %} - - {% else %} - - {% endif %} - - {{ proxy_log.date_start }} - {{ proxy_log.date_finished|timeuntil:proxy_log.date_start }} - - {% endfor %} + {% for proxy_log in proxy_log_list %} + + + {{ proxy_log.id }} + + {{ proxy_log.user }} + {{ proxy_log.asset }} + {{ proxy_log.system_user }} + {{ proxy_log.commands.all|length}} + + {% if proxy_log.is_failed %} + + {% else %} + + {% endif %} + + {% if proxy_log.is_finished %} + + + + + + + {% else %} + + + + + + + {% endif %} + {{ proxy_log.date_start }} + {{ proxy_log.date_finished|timeuntil:proxy_log.date_start }} + + {% endfor %} {% endblock %} {% block custom_foot_js %} diff --git a/apps/audits/urls/api_urls.py b/apps/audits/urls/api_urls.py index 1f06cc3cc..ca8ed9c19 100644 --- a/apps/audits/urls/api_urls.py +++ b/apps/audits/urls/api_urls.py @@ -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 diff --git a/apps/audits/views.py b/apps/audits/views.py index 8ff2fc6cf..144e8f35d 100644 --- a/apps/audits/views.py +++ b/apps/audits/views.py @@ -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): diff --git a/apps/common/templatetags/common_tags.py b/apps/common/templatetags/common_tags.py index f096df945..01d1ab8f3 100644 --- a/apps/common/templatetags/common_tags.py +++ b/apps/common/templatetags/common_tags.py @@ -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) \ No newline at end of file + 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', '
') + + +@register.filter +def proxy_log_commands(log_id): + return command_store.filter(proxy_log_id=log_id) \ No newline at end of file diff --git a/apps/common/utils.py b/apps/common/utils.py index efc1b769d..a5f7d8c26 100644 --- a/apps/common/utils.py +++ b/apps/common/utils.py @@ -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 diff --git a/apps/jumpserver/middleware.py b/apps/jumpserver/middleware.py new file mode 100644 index 000000000..2ed94c6a8 --- /dev/null +++ b/apps/jumpserver/middleware.py @@ -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() diff --git a/apps/jumpserver/settings.py b/apps/jumpserver/settings.py index 8fa5a65c5..5f68ed231 100644 --- a/apps/jumpserver/settings.py +++ b/apps/jumpserver/settings.py @@ -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' + diff --git a/apps/perms/api.py b/apps/perms/api.py index ea4c9e05a..0854feb0a 100644 --- a/apps/perms/api.py +++ b/apps/perms/api.py @@ -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,) diff --git a/apps/perms/forms.py b/apps/perms/forms.py index 27c8136c0..d99dfa3be 100644 --- a/apps/perms/forms.py +++ b/apps/perms/forms.py @@ -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', } - diff --git a/apps/perms/hands.py b/apps/perms/hands.py index eac38f7d0..dd8e61090 100644 --- a/apps/perms/hands.py +++ b/apps/perms/hands.py @@ -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)) diff --git a/apps/perms/models.py b/apps/perms/models.py index f1d4b4b1f..1cdd39558 100644 --- a/apps/perms/models.py +++ b/apps/perms/models.py @@ -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) + + + diff --git a/apps/perms/templates/perms/asset_permission_detail.html b/apps/perms/templates/perms/asset_permission_detail.html index 4f6b7725e..1126b9705 100644 --- a/apps/perms/templates/perms/asset_permission_detail.html +++ b/apps/perms/templates/perms/asset_permission_detail.html @@ -169,16 +169,16 @@ - + {% for system_user in system_users %} - {{ system_user.name }} + {{ system_user.name }} - + {% endfor %} @@ -196,8 +196,47 @@ {% endblock %} {% block custom_foot_js %} {% endblock %} \ No newline at end of file diff --git a/apps/perms/templates/perms/asset_permission_user.html b/apps/perms/templates/perms/asset_permission_user.html index 02a571e9f..168bc1149 100644 --- a/apps/perms/templates/perms/asset_permission_user.html +++ b/apps/perms/templates/perms/asset_permission_user.html @@ -108,7 +108,7 @@
- {% for user in users_remain %} {% endfor %} @@ -117,7 +117,7 @@ - +
@@ -136,7 +136,7 @@
- {% for user_group in user_groups_remain %} {% endfor %} @@ -145,7 +145,7 @@ - +
@@ -172,25 +172,29 @@ {% endblock %} {% block custom_foot_js %} {% endblock %} \ No newline at end of file diff --git a/apps/perms/tests.py b/apps/perms/tests.py index 7ce503c2d..4fd76b0f2 100644 --- a/apps/perms/tests.py +++ b/apps/perms/tests.py @@ -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 \ No newline at end of file diff --git a/apps/perms/urls/api_urls.py b/apps/perms/urls/api_urls.py index 0e97a441e..db2d74829 100644 --- a/apps/perms/urls/api_urls.py +++ b/apps/perms/urls/api_urls.py @@ -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[0-9]+)/system-user/remove/$', + api.RemoveSystemUserAssetPermission.as_view(), + name='remove-system-user-asset-permission'), ] urlpatterns += router.urls diff --git a/apps/perms/utils.py b/apps/perms/utils.py index f01ae1779..efad6cbce 100644 --- a/apps/perms/utils.py +++ b/apps/perms/utils.py @@ -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) diff --git a/apps/perms/views.py b/apps/perms/views.py index 72db844c9..b1aac2f9a 100644 --- a/apps/perms/views.py +++ b/apps/perms/views.py @@ -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' diff --git a/apps/static/js/jumpserver.js b/apps/static/js/jumpserver.js index f11416e90..2ad48ec86 100644 --- a/apps/static/js/jumpserver.js +++ b/apps/static/js/jumpserver.js @@ -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; } diff --git a/apps/users/authentication.py b/apps/users/authentication.py index 57cd89386..b8e2601e6 100644 --- a/apps/users/authentication.py +++ b/apps/users/authentication.py @@ -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 diff --git a/apps/users/forms.py b/apps/users/forms.py index 56be49f0f..5521713fc 100644 --- a/apps/users/forms.py +++ b/apps/users/forms.py @@ -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')}), } diff --git a/apps/users/views/group.py b/apps/users/views/group.py index 48232b72a..fe04987a4 100644 --- a/apps/users/views/group.py +++ b/apps/users/views/group.py @@ -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', []) diff --git a/deb_requirements.txt b/requirements/deb_requirements.txt similarity index 100% rename from deb_requirements.txt rename to requirements/deb_requirements.txt diff --git a/requirements/mac_requirements.py b/requirements/mac_requirements.py new file mode 100644 index 000000000..e69de29bb diff --git a/requirements.txt b/requirements/requirements.txt similarity index 100% rename from requirements.txt rename to requirements/requirements.txt diff --git a/rpm_requirements.txt b/requirements/rpm_requirements.txt similarity index 100% rename from rpm_requirements.txt rename to requirements/rpm_requirements.txt