diff --git a/apps/audits/api.py b/apps/audits/api.py index 64e308699..7deee860d 100644 --- a/apps/audits/api.py +++ b/apps/audits/api.py @@ -5,11 +5,15 @@ from rest_framework.mixins import ListModelMixin from common.mixins.api import CommonApiMixin from common.permissions import IsOrgAdminOrAppUser, IsOrgAuditor, IsOrgAdmin -from common.drf.filters import DatetimeRangeFilter +from common.drf.filters import DatetimeRangeFilter, current_user_filter +from common.api import CommonGenericViewSet from orgs.mixins.api import OrgGenericViewSet from orgs.utils import current_org -from .models import FTPLog, UserLoginLog -from .serializers import FTPLogSerializer, UserLoginLogSerializer +from ops.models import CommandExecution +from .models import FTPLog, UserLoginLog, OperateLog, PasswordChangeLog +from .serializers import FTPLogSerializer, UserLoginLogSerializer, CommandExecutionSerializer +from .serializers import OperateLogSerializer, PasswordChangeLogSerializer +from .filters import CurrentOrgMembersFilter class FTPLogViewSet(ListModelMixin, OrgGenericViewSet): @@ -24,9 +28,8 @@ class FTPLogViewSet(ListModelMixin, OrgGenericViewSet): search_fields = ['filename'] -class UserLoginLogViewSet(CommonApiMixin, - ListModelMixin, - GenericViewSet): +class UserLoginLogViewSet(ListModelMixin, + CommonGenericViewSet): queryset = UserLoginLog.objects.all() permission_classes = [IsOrgAdmin | IsOrgAuditor] serializer_class = UserLoginLogSerializer @@ -48,3 +51,47 @@ class UserLoginLogViewSet(CommonApiMixin, users = self.get_org_members() queryset = queryset.filter(username__in=users) return queryset + + +class OperateLogViewSet(ListModelMixin, OrgGenericViewSet): + model = OperateLog + serializer_class = OperateLogSerializer + permission_classes = [IsOrgAdmin | IsOrgAuditor] + extra_filter_backends = [DatetimeRangeFilter] + date_range_filter_fields = [ + ('datetime', ('date_from', 'date_to')) + ] + filterset_fields = ['user', 'action', 'resource_type'] + search_fields = ['filename'] + ordering_fields = ['-datetime'] + + +class PasswordChangeLogViewSet(ListModelMixin, CommonGenericViewSet): + queryset = PasswordChangeLog.objects.all() + permission_classes = [IsOrgAdmin | IsOrgAuditor] + serializer_class = PasswordChangeLogSerializer + extra_filter_backends = [DatetimeRangeFilter] + date_range_filter_fields = [ + ('datetime', ('date_from', 'date_to')) + ] + filterset_fields = ['user'] + ordering_fields = ['-datetime'] + + def get_queryset(self): + users = current_org.get_org_members() + queryset = super().get_queryset().filter( + user__in=[user.__str__() for user in users] + ) + return queryset + + +class CommandExecutionViewSet(ListModelMixin, OrgGenericViewSet): + model = CommandExecution + serializer_class = CommandExecutionSerializer + permission_classes = [IsOrgAdmin | IsOrgAuditor] + extra_filter_backends = [DatetimeRangeFilter, current_user_filter(), CurrentOrgMembersFilter] + date_range_filter_fields = [ + ('date_start', ('date_from', 'date_to')) + ] + search_fields = ['command'] + ordering_fields = ['-date_created'] diff --git a/apps/audits/filters.py b/apps/audits/filters.py new file mode 100644 index 000000000..6db2d9b21 --- /dev/null +++ b/apps/audits/filters.py @@ -0,0 +1,32 @@ +from rest_framework import filters +from rest_framework.compat import coreapi, coreschema + +from orgs.utils import current_org + + +__all__ = ['CurrentOrgMembersFilter'] + + +class CurrentOrgMembersFilter(filters.BaseFilterBackend): + def get_schema_fields(self, view): + return [ + coreapi.Field( + name='user', location='query', required=False, type='string', + schema=coreschema.String( + title='user', + description='user' + ) + ) + ] + + def _get_user_list(self): + users = current_org.get_org_members(exclude=('Auditor',)) + return users + + def filter_queryset(self, request, queryset, view): + user_id = request.GET.get('user') + if user_id: + queryset = queryset.filter(user=user_id) + else: + queryset = queryset.filter(user__in=self._get_user_list()) + return queryset diff --git a/apps/audits/serializers.py b/apps/audits/serializers.py index 2ea0dec0f..0d3805d85 100644 --- a/apps/audits/serializers.py +++ b/apps/audits/serializers.py @@ -4,6 +4,7 @@ from rest_framework import serializers from terminal.models import Session +from ops.models import CommandExecution from . import models @@ -12,7 +13,7 @@ class FTPLogSerializer(serializers.ModelSerializer): class Meta: model = models.FTPLog fields = ( - 'id', 'user', 'remote_addr', 'asset', 'system_user', + 'user', 'remote_addr', 'asset', 'system_user', 'operate', 'filename', 'is_success', 'date_start' ) @@ -33,13 +34,18 @@ class UserLoginLogSerializer(serializers.ModelSerializer): class OperateLogSerializer(serializers.ModelSerializer): class Meta: model = models.OperateLog - fields = '__all__' + fields = ( + 'user', 'action', 'resource_type', 'resource', + 'remote_addr', 'datetime' + ) class PasswordChangeLogSerializer(serializers.ModelSerializer): class Meta: model = models.PasswordChangeLog - fields = '__all__' + fields = ( + 'user', 'change_by', 'remote_addr', 'datetime' + ) class SessionAuditSerializer(serializers.ModelSerializer): @@ -47,3 +53,11 @@ class SessionAuditSerializer(serializers.ModelSerializer): model = Session fields = '__all__' + +class CommandExecutionSerializer(serializers.ModelSerializer): + class Meta: + model = CommandExecution + fields = ( + 'hosts', 'run_as', 'command', 'user', 'is_finished', + 'date_start', 'result', 'is_success' + ) diff --git a/apps/audits/urls/api_urls.py b/apps/audits/urls/api_urls.py index 86312dfe3..be622a357 100644 --- a/apps/audits/urls/api_urls.py +++ b/apps/audits/urls/api_urls.py @@ -13,6 +13,9 @@ app_name = "audits" router = DefaultRouter() router.register(r'ftp-logs', api.FTPLogViewSet, 'ftp-log') router.register(r'login-logs', api.UserLoginLogViewSet, 'login-log') +router.register(r'operate-logs', api.OperateLogViewSet, 'operate-log') +router.register(r'password-change-logs', api.PasswordChangeLogViewSet, 'password-change-log') +router.register(r'command-execution-logs', api.CommandExecutionViewSet, 'command-execution-log') urlpatterns = [ ] diff --git a/apps/common/api.py b/apps/common/api.py index d69540cfd..7ecd77122 100644 --- a/apps/common/api.py +++ b/apps/common/api.py @@ -9,10 +9,12 @@ from django.views.decorators.csrf import csrf_exempt from rest_framework.views import APIView from rest_framework.response import Response from rest_framework import generics, serializers +from rest_framework.viewsets import GenericViewSet from .http import HttpResponseTemporaryRedirect from .const import KEY_CACHE_RESOURCES_ID from .utils import get_logger +from .mixins import CommonApiMixin __all__ = [ 'LogTailApi', 'ResourcesIDCacheApi', @@ -100,3 +102,7 @@ def redirect_plural_name_api(request, *args, **kwargs): full_path = org_full_path.replace(resource, resource+"s", 1) logger.debug("Redirect {} => {}".format(org_full_path, full_path)) return HttpResponseTemporaryRedirect(full_path) + + +class CommonGenericViewSet(CommonApiMixin, GenericViewSet): + pass diff --git a/apps/common/drf/filters.py b/apps/common/drf/filters.py index 029538e8a..8d091b7d8 100644 --- a/apps/common/drf/filters.py +++ b/apps/common/drf/filters.py @@ -1,6 +1,5 @@ # -*- coding: utf-8 -*- # -import coreapi from rest_framework import filters from rest_framework.fields import DateTimeField from rest_framework.serializers import ValidationError @@ -146,3 +145,10 @@ class CustomFilter(filters.BaseFilterBackend): def filter_queryset(self, request, queryset, view): return queryset + + +def current_user_filter(user_field='user'): + class CurrentUserFilter(filters.BaseFilterBackend): + def filter_queryset(self, request, queryset, view): + return queryset.filter(**{user_field: request.user}) + return CurrentUserFilter