Skip to content

Commit

Permalink
审计功能增强 (#1342)
Browse files Browse the repository at this point in the history
* 添加redis/pg创建连接时的超时参数 (#1311)

* 审计功能增强

* 审计功能SQL调整

* 审计功能增强CI依赖文件

* 移除migrations

* 修复登录时用户不存在的bug以及记录登录的浏览器信息

* 无用包移除,额外信息移除ua
  • Loading branch information
weideguo authored Jan 19, 2022
1 parent 47d8e4d commit 0a21e81
Show file tree
Hide file tree
Showing 14 changed files with 859 additions and 37 deletions.
17 changes: 15 additions & 2 deletions common/templates/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -242,11 +242,24 @@
</ul>
<!-- /.nav-third-level -->
</li>
</ul>
<!-- /.nav-second-level -->
</li>
{% endif %}
{% if perms.sql.audit_user %}
<li>
<a href="#"><i class="fa fa-eye fa-fw"></i> 系统审计<span class="fa arrow"></span></a>
<ul class="nav nav-second-level">
<li>
<a href="/audit/">登录审计日志</a>
<a href="/audit/">通用审计</a>
</li>
<li>
<a href="/audit_sqlworkflow/">sql上线审计</a>
</li>
<li>
<a href="/audit_sqlquery/">查询审计</a>
</li>
</ul>
<!-- /.nav-second-level -->
</li>
{% endif %}
{% if perms.sql.menu_document %}
Expand Down
4 changes: 2 additions & 2 deletions sql/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,6 @@ class CloudAccessKeyAdmin(admin.ModelAdmin):
# 登录审计日志
@admin.register(AuditEntry)
class AuditEntryAdmin(admin.ModelAdmin):
list_display = ('user_id', 'user_name', 'action', 'ip', 'action_time')
list_filter = ('user_id', 'user_name', 'action', 'ip')
list_display = ('user_id', 'user_name', 'user_display', 'action', 'extra_info', 'action_time')
list_filter = ('user_id', 'user_name', 'user_display', 'action', 'extra_info')

66 changes: 52 additions & 14 deletions sql/audit_log.py
Original file line number Diff line number Diff line change
@@ -1,30 +1,67 @@
# -*- coding: UTF-8 -*-
import logging
from django.contrib.auth.signals import user_logged_in, user_logged_out, user_login_failed
import datetime
import simplejson as json

from django.dispatch import receiver
from .models import AuditEntry, Users
from django.utils import timezone
from common.utils.permission import superuser_required
from django.http import HttpResponse
import simplejson as json
from common.utils.extend_json_encoder import ExtendJSONEncoder
from django.db.models import Q
from django.contrib.auth.decorators import login_required,permission_required
from django.contrib.auth.signals import user_logged_in, user_logged_out, user_login_failed

from .models import AuditEntry, Users
from common.utils.permission import superuser_required
from common.utils.extend_json_encoder import ExtendJSONEncoder

log = logging.getLogger('default')


@superuser_required
@login_required
def audit_input(request):
"""用户提交的操作信息"""
result = {}
action = request.POST.get('action')
extra_info = request.POST.get('extra_info','')

result['user_id'] = request.user.id
result['user_name'] = request.user.username
result['user_display'] = request.user.display
result['action'] = action
result['extra_info'] = extra_info

audit = AuditEntry(**result)
audit.save()

return HttpResponse(json.dumps(result, cls=ExtendJSONEncoder, bigint_as_string=True),
content_type='application/json')


@permission_required('sql.audit_user', raise_exception=True)
def audit_log(request):
"""获取登录审计日志列表"""
"""获取审计日志列表"""
limit = int(request.POST.get('limit'))
offset = int(request.POST.get('offset'))
limit = offset + limit
limit = limit if limit else None
search = request.POST.get('search', '')
action = request.POST.get('action','')
start_date = request.POST.get('start_date')
end_date = request.POST.get('end_date')

filter_dict = dict()
if start_date and end_date:
end_date = datetime.datetime.strptime(end_date, '%Y-%m-%d') + datetime.timedelta(days=1)
filter_dict['action_time__range'] = (start_date, end_date)
if action:
filter_dict['action'] = action

audit_log_obj = AuditEntry.objects.filter(**filter_dict)
if search:
audit_log_obj = audit_log_obj.filter(Q(user_name__icontains=search) | Q(action__icontains=search)| Q(extra_info__icontains=search))

# 过滤搜索条件
audit_log_obj = AuditEntry.objects.filter(Q(user_name__icontains=search) | Q(action__icontains=search)| Q(ip__icontains=search))
audit_log_count = audit_log_obj.count()
audit_log_list = audit_log_obj.order_by('-action_time')[offset:limit].values("user_id", "user_name", "ip", "action", "action_time")
audit_log_list = audit_log_obj.order_by('-action_time')[offset:limit].values('user_id', 'user_name', 'user_display', 'action', 'extra_info', 'action_time')

# QuerySet 序列化
rows = [row for row in audit_log_list]
Expand All @@ -48,14 +85,14 @@ def get_client_ip(request):
def user_logged_in_callback(sender, request, user, **kwargs):
ip = get_client_ip(request)
now = timezone.now()
AuditEntry.objects.create(action=u'登入', ip=ip, user_id=user.id, user_name=user.username, action_time=now)
AuditEntry.objects.create(action=u'登入', extra_info=ip, user_id=user.id, user_name=user.username, user_display=user.display, action_time=now)


@receiver(user_logged_out)
def user_logged_out_callback(sender, request, user, **kwargs):
ip = get_client_ip(request)
now = timezone.now()
AuditEntry.objects.create(action=u'登出', ip=ip, user_id=user.id, user_name=user.username, action_time=now)
AuditEntry.objects.create(action=u'登出', extra_info=ip, user_id=user.id, user_name=user.username, user_display=user.display, action_time=now)


@receiver(user_login_failed)
Expand All @@ -65,8 +102,9 @@ def user_login_failed_callback(sender, credentials, **kwargs):
user_obj = Users.objects.filter(username=user_name)[0:1]
user_count = user_obj.count()
user_id = 0
user_display = ''
if user_count > 0:
user_id = user_obj[0].id
AuditEntry.objects.create(action=u'登入失败', user_id=user_id, user_name=user_name
, action_time=now)
user_display = user_obj[0].display
AuditEntry.objects.create(action=u'登入失败', user_id=user_id, user_name=user_name, user_display=user_display, action_time=now)

10 changes: 6 additions & 4 deletions sql/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -728,6 +728,7 @@ class Meta:
('archive_apply', '提交归档申请'),
('archive_review', '审核归档申请'),
('archive_mgt', '管理归档申请'),
('audit_user','审计权限'),
)


Expand Down Expand Up @@ -867,9 +868,10 @@ class AuditEntry(models.Model):
登录审计日志
"""
user_id = models.IntegerField('用户ID')
user_name = models.CharField('用户名称', max_length=255, null=True)
user_name = models.CharField('用户名称', max_length=30, null=True)
user_display = models.CharField('用户中文名',max_length=50, null=True)
action = models.CharField('动作', max_length=255)
ip = models.GenericIPAddressField('IP', null=True)
extra_info = models.CharField('额外的信息', max_length=255, null=True)
action_time = models.DateTimeField('操作时间', auto_now_add=True)

class Meta:
Expand All @@ -879,9 +881,9 @@ class Meta:
verbose_name_plural = u'审计日志'

def __unicode__(self):
return '{0} - {1} - {2} - {3} - {4}'.format(self.user_id, self.user_name, self.ip
return '{0} - {1} - {2} - {3} - {4}'.format(self.user_id, self.user_name, self.extra_info
, self.action, self.action_time)

def __str__(self):
return '{0} - {1} - {2} - {3} - {4}'.format(self.user_id, self.user_name, self.ip
return '{0} - {1} - {2} - {3} - {4}'.format(self.user_id, self.user_name, self.extra_info
, self.action, self.action_time)
18 changes: 16 additions & 2 deletions sql/query.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,13 @@ def query(request):

@permission_required('sql.menu_sqlquery', raise_exception=True)
def querylog(request):
return _querylog(request)

@permission_required('sql.audit_user', raise_exception=True)
def querylog_audit(request):
return _querylog(request)

def _querylog(request):
"""
获取sql查询记录
:param request:
Expand All @@ -196,6 +203,8 @@ def querylog(request):
star = True if request.GET.get('star') == 'true' else False
query_log_id = request.GET.get('query_log_id')
search = request.GET.get('search', '')
start_date = request.GET.get('start_date','')
end_date = request.GET.get('end_date','')

# 组合筛选项
filter_dict = dict()
Expand All @@ -205,9 +214,14 @@ def querylog(request):
# 语句别名
if query_log_id:
filter_dict['id'] = query_log_id
# 管理员查看全部数据,普通用户查看自己的数据
if not user.is_superuser:

# 管理员、审计员查看全部数据,普通用户查看自己的数据
if not (user.is_superuser or user.has_perm('sql.audit_user')):
filter_dict['username'] = user.username

if start_date and end_date:
end_date = datetime.datetime.strptime(end_date, '%Y-%m-%d') + datetime.timedelta(days=1)
filter_dict['create_time__range'] = (start_date, end_date)

# 过滤组合筛选项
sql_log = QueryLog.objects.filter(**filter_dict)
Expand Down
11 changes: 9 additions & 2 deletions sql/sql_workflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,13 @@

@permission_required('sql.menu_sqlworkflow', raise_exception=True)
def sql_workflow_list(request):
return _sql_workflow_list(request)

@permission_required('sql.audit_user', raise_exception=True)
def sql_workflow_list_audit(request):
return _sql_workflow_list(request)

def _sql_workflow_list(request):
"""
获取审核列表
:param request:
Expand Down Expand Up @@ -64,8 +71,8 @@ def sql_workflow_list(request):
if start_date and end_date:
end_date = datetime.datetime.strptime(end_date, '%Y-%m-%d') + datetime.timedelta(days=1)
filter_dict['create_time__range'] = (start_date, end_date)
# 管理员,可查看所有工单
if user.is_superuser:
# 管理员,审计员,可查看所有工单
if user.is_superuser or user.has_perm('sql.audit_user'):
pass
# 非管理员,拥有审核权限、资源组粒度执行权限的,可以查看组内所有工单
elif user.has_perm('sql.sql_review') or user.has_perm('sql.sql_execute_for_resource_group'):
Expand Down
95 changes: 90 additions & 5 deletions sql/templates/audit.html
Original file line number Diff line number Diff line change
@@ -1,6 +1,24 @@
{% extends "base.html" %}

{% block content %}
<div id="toolbar" class="form-inline pull-left">
<div class="form-group">
<select id="navStatus" class="form-control selectpicker">
<option value="" selected="selected">动作</option>
{% for action_type in action_types %}
<option value={{ action_type }}>{{ action_type }}</option>
{% endfor %}
</select>
</div>
<div class='form-group'>
<div id="reservation" class="form-control"
style="background: #fff; cursor: pointer; padding: 5px 10px; border: 1px solid #ccc; width: 100%">
<i class="fa fa-calendar"></i>&nbsp;
<span></span> <i class="fa fa-caret-down"></i>
</div>
</div>

</div>
<!-- 表格-->
<div class="table-responsive">
<table id="group-list" data-toggle="table" class="table table-striped table-hover"
Expand All @@ -10,8 +28,58 @@
{% endblock content %}
{% block js %}
{% load static %}
<link href="{% static 'daterangepicker/css/daterangepicker.css' %}" rel="stylesheet" type="text/css"/>
<script src="{% static 'daterangepicker/js/moment.min.js' %}"></script>
<script src="{% static 'daterangepicker/js/daterangepicker.js' %}"></script>
<script src="{% static 'bootstrap-table/js/bootstrap-table-export.min.js' %}"></script>
<script src="{% static 'bootstrap-table/js/tableExport.min.js' %}"></script>
<script>
// 初始化时间控件
$(function () {
let start = moment().subtract(6, 'days');
let end = moment();

function cb(start, end) {
if (start.isValid() && end.isValid()) {
$('#reservation span').html(start.format('YYYY-MM-DD') + ' - ' + end.format('YYYY-MM-DD'));
} else {
$('#reservation span').html('全部数据');
}
}

$('#reservation').daterangepicker({
startDate: start,
endDate: end,
showDropdowns: true,
locale: {
format: "YYYY-MM-DD",// 显示格式
separator: " / ",// 两个日期之间的分割线
// 中文化
applyLabel: "确定",
cancelLabel: "取消",
fromLabel: "开始",
toLabel: "结束",
customRangeLabel: "自定义",
daysOfWeek: ["日", "一", "二", "三", "四", "五", "六"],
monthNames: ["一月", "二月", "三月", "四月", "五月", "六", "七月", "八月", "九月", "十月", "十月", "十一月", "十二月"],
firstDay: 1
},
ranges: {
"全部": [null, null],
"今日": [moment().startOf('day'), moment()],
"昨日": [moment().subtract('days', 1).startOf('day'), moment().subtract('days', 1).endOf('day')],
"最近7日": [moment().subtract('days', 6), moment()],
"最近30日": [moment().subtract('days', 29), moment()],
"本月": [moment().startOf("month"), moment().endOf("month")],
"上个月": [moment().subtract(1, "month").startOf("month"), moment().subtract(1, "month").endOf("month")]
}
}, cb).on('apply.daterangepicker', function (ev, picker) {
grouplist()
});
cb(start, end);
});

</script>
<script>
//获取列表
function grouplist() {
Expand Down Expand Up @@ -51,10 +119,22 @@
//请求服务数据时所传参数
queryParams:
function (params) {
let start_date = $("#reservation").data('daterangepicker').startDate;
let end_date = $("#reservation").data('daterangepicker').endDate;
if (start_date.isValid() && end_date.isValid()) {
start_date = start_date.format('YYYY-MM-DD');
end_date = end_date.format('YYYY-MM-DD')
} else {
start_date = '';
end_date = ''
}
return {
limit: params.limit,
offset: params.offset,
search: params.search
search: params.search,
action: $("#navStatus").val(),
start_date: start_date,
end_date: end_date,
}
},
columns: [{
Expand All @@ -64,12 +144,15 @@
return "<a target=\"_blank\" href=\"/admin/sql/users/" + row.user_id + "/change/\">" + value + "</a>"
}
},{
title: 'IP',
field: 'ip'
title: '中文名',
field: 'user_display'
},{
title: '动作',
field: 'action'
}, {
},{
title: '额外信息',
field: 'extra_info'
},{
title: '操作时间',
field: 'action_time'
}],
Expand All @@ -85,7 +168,9 @@
});

}

$("#navStatus").change(function () {
grouplist();
});
//初始化数据
$(document).ready(function () {
grouplist();
Expand Down
Loading

0 comments on commit 0a21e81

Please sign in to comment.