[Update] 更新index view

pull/828/merge
ibuler 2017-12-04 20:15:47 +08:00
parent 71a3079221
commit a5f9735906
50 changed files with 227 additions and 1583 deletions

View File

@ -1 +0,0 @@

View File

@ -1,92 +0,0 @@
# ~*~ coding: utf-8 ~*~
#
from __future__ import absolute_import, unicode_literals
from rest_framework import generics, viewsets
from rest_framework_bulk import BulkModelViewSet
from audits.backends import command_store, record_store
from audits.backends.command.serializers import CommandLogSerializer
from audits.backends.record.serializers import RecordSerializer
from . import models, serializers
from .hands import IsSuperUserOrAppUser, IsAppUser
class ProxyLogReceiveView(generics.CreateAPIView):
queryset = models.ProxyLog.objects.all()
serializer_class = serializers.ProxyLogSerializer
permission_classes = (IsAppUser,)
def get_serializer(self, *args, **kwargs):
kwargs['data']['terminal'] = self.request.user.terminal.name
return super(ProxyLogReceiveView, self).get_serializer(*args, **kwargs)
class ProxyLogViewSet(viewsets.ModelViewSet):
"""User proxy to backend server need call this api.
params: {
"username": "",
"name": "",
"hostname": "",
"ip": "",
"terminal": "",
"login_type": "",
"system_user": "",
"was_failed": "",
"date_start": ""
}
"""
queryset = models.ProxyLog.objects.all()
serializer_class = serializers.ProxyLogSerializer
permission_classes = (IsSuperUserOrAppUser,)
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,)
class RecordLogViewSet(BulkModelViewSet):
"""接受app发送来的record log, 格式如下
{
"proxy_log_id": 23,
"output": "d2hvbWFp", # base64.b64encode(s)
"timestamp": 1485238673.0
}
"""
serializer_class = RecordSerializer
permission_classes = (IsSuperUserOrAppUser,)
def get_queryset(self):
filter_kwargs = {}
proxy_log_id = self.request.query_params.get('proxy_log_id')
data_from_ts = self.request.query_params.get('date_from_ts')
if proxy_log_id:
filter_kwargs['proxy_log_id'] = proxy_log_id
if data_from_ts:
filter_kwargs['date_from_ts'] = data_from_ts
if filter_kwargs:
return record_store.filter(**filter_kwargs)
else:
return record_store.all()

View File

@ -1,7 +0,0 @@
from __future__ import unicode_literals
from django.apps import AppConfig
class AuditsConfig(AppConfig):
name = 'audits'

View File

@ -1,10 +0,0 @@
from importlib import import_module
from django.conf import settings
command_engine = import_module(settings.COMMAND_STORE_BACKEND)
command_store = command_engine.CommandStore()
record_engine = import_module(settings.RECORD_STORE_BACKEND)
record_store = record_engine.RecordStore()
from .command.serializers import CommandLogSerializer

View File

@ -1,19 +0,0 @@
# 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_ts=None, date_to_ts=None, user='',
asset='', system_user='', command='', proxy_log_id=0):
pass

View File

@ -1,45 +0,0 @@
# ~*~ 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=0):
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()

View File

@ -1,21 +0,0 @@
# ~*~ 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 create(self, validated_data):
try:
output = validated_data['output']
validated_data['output'] = base64.b64decode(output)
except IndexError:
pass
return command_store.save(**dict(validated_data))

View File

@ -1,2 +0,0 @@
# ~*~ coding: utf-8 ~*~

View File

@ -1,14 +0,0 @@
# coding: utf-8
import abc
class RecordBase(object):
__metaclass__ = abc.ABCMeta
@abc.abstractmethod
def save(self, proxy_log_id, output, timestamp):
pass
@abc.abstractmethod
def filter(self, date_from_ts=None, proxy_log_id=None):
pass

View File

@ -1,31 +0,0 @@
# ~*~ coding: utf-8 ~*~
from .base import RecordBase
from audits.models import RecordLog
class RecordStore(RecordBase):
model = RecordLog
queryset = []
def save(self, proxy_log_id, output, timestamp):
return self.model.objects.create(
proxy_log_id=proxy_log_id, output=output, timestamp=timestamp
)
def filter(self, date_from_ts=None, proxy_log_id=''):
filter_kwargs = {}
if date_from_ts:
filter_kwargs['timestamp__gte'] = date_from_ts
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.all()

View File

@ -1,20 +0,0 @@
# ~*~ coding: utf-8 ~*~
import base64
from rest_framework import serializers
from audits.models import RecordLog
from audits.backends import record_store
class RecordSerializer(serializers.ModelSerializer):
"""使用这个类作为基础Command Log Serializer类, 用来序列化"""
class Meta:
model = RecordLog
fields = '__all__'
def create(self, validated_data):
try:
output = validated_data['output']
validated_data['output'] = base64.b64decode(output)
except IndexError:
pass
return record_store.save(**dict(validated_data))

View File

@ -1,8 +0,0 @@
# ~*~ coding: utf-8 ~*~
#
from users.utils import AdminUserRequiredMixin
from users.models import User
from assets.models import Asset, SystemUser
from users.permissions import IsSuperUserOrAppUser, IsAppUser
from terminal.models import Terminal

View File

@ -1,99 +0,0 @@
# -*- coding: utf-8 -*-
#
from __future__ import unicode_literals
import uuid
from django.db import models
from django.utils.translation import ugettext_lazy as _
class LoginLog(models.Model):
LOGIN_TYPE_CHOICE = (
('W', 'Web'),
('ST', 'SSH Terminal'),
('WT', 'Web Terminal')
)
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
username = models.CharField(max_length=20, verbose_name=_('Username'))
name = models.CharField(max_length=20, blank=True, verbose_name=_('Name'))
login_type = models.CharField(choices=LOGIN_TYPE_CHOICE, max_length=2,
verbose_name=_('Login type'))
login_ip = models.GenericIPAddressField(verbose_name=_('Login ip'))
login_city = models.CharField(max_length=254, blank=True, null=True,
verbose_name=_('Login city'))
user_agent = models.CharField(max_length=254, blank=True, null=True,
verbose_name=_('User agent'))
date_login = models.DateTimeField(auto_now_add=True,
verbose_name=_('Date login'))
class Meta:
db_table = 'login_log'
ordering = ['-date_login', 'username']
class ProxyLog(models.Model):
LOGIN_TYPE_CHOICE = (
('ST', 'SSH Terminal'),
('WT', 'Web Terminal'),
)
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
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'))
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' % (self.user, self.asset, self.system_user)
def commands(self):
from audits.backends import command_store
return command_store.filter(proxy_log_id=self.id)
class Meta:
ordering = ['-date_start', 'user']
class CommandLog(models.Model):
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
proxy_log_id = models.IntegerField(db_index=True)
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.TextField(max_length=767, blank=True)
output = models.TextField(blank=True)
timestamp = models.FloatField(db_index=True)
def __unicode__(self):
return '%s: %s' % (self.id, self.command)
class Meta:
ordering = ['command_no', 'command']
class RecordLog(models.Model):
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
proxy_log_id = models.IntegerField(db_index=True)
output = models.TextField(verbose_name=_('Output'))
timestamp = models.FloatField(db_index=True)
def __unicode__(self):
return 'Record: %s' % self.proxy_log_id
class Meta:
ordering = ['timestamp']

View File

@ -1,28 +0,0 @@
# -*- coding: utf-8 -*-
#
from __future__ import absolute_import, unicode_literals
from rest_framework import serializers
from common.utils import timesince
from . import models
class ProxyLogSerializer(serializers.ModelSerializer):
time = serializers.SerializerMethodField()
command_length = serializers.SerializerMethodField()
class Meta:
model = models.ProxyLog
fields = '__all__'
@staticmethod
def get_time(obj):
if not obj.is_finished:
return ''
else:
return timesince(obj.date_start, since=obj.date_finished)
@staticmethod
def get_command_length(obj):
return 2

View File

@ -1,109 +0,0 @@
{% extends '_base_list.html' %}
{% load i18n %}
{% load static %}
{% load common_tags %}
{% block content_left_head %}
<link href="{% static "css/plugins/footable/footable.core.css" %}" rel="stylesheet">
<link href="{% static 'css/plugins/datepicker/datepicker3.css' %}" rel="stylesheet">
<style>
#search_btn {
margin-bottom: 0;
}
</style>
{% endblock %}
{% block table_search %}
<form id="search_form" method="get" action="" class="pull-right form-inline">
<div class="form-group" id="date">
<div class="input-daterange input-group" id="datepicker">
<span class="input-group-addon"><i class="fa fa-calendar"></i></span>
<input type="text" class="input-sm form-control" style="width: 100px;" name="date_from" value="{{ date_from }}">
<span class="input-group-addon">to</span>
<input type="text" class="input-sm form-control" style="width: 100px;" name="date_to" value="{{ date_to }}">
</div>
</div>
<div class="input-group">
<select class="select2 form-control" name="username">
<option value="">{% trans 'User' %}</option>
{% for u in user_list %}
<option value="{{ u.username }}" {% if username == u.username %} selected {% endif %}>{{ u.username }}</option>
{% endfor %}
</select>
</div>
<div class="input-group">
<select class="select2 form-control" name="ip">
<option value="">{% trans 'Asset' %}</option>
{% for a in asset_list %}
<option value="{{ a.ip }}" {% if ip == a.ip %} selected {% endif %}>{{ a.ip }}</option>
{% endfor %}
</select>
</div>
<div class="input-group">
<select class="select2 form-control" name="system_user">
<option value="">{% trans 'System user' %}</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="command" placeholder="Command" value="{{ command }}">
</div>
<div class="input-group">
<div class="input-group-btn">
<button id='search_btn' type="submit" class="btn btn-sm btn-primary">
搜索
</button>
</div>
</div>
</form>
{% endblock %}
{% block table_container %}
<table class="footable table table-stripped toggle-arrow-tiny" data-page="false">
<thead>
<tr>
<th data-toggle="true">ID</th>
<th>Command</th>
<th>Username</th>
<th>IP</th>
<th>System user</th>
<th>Proxy log</th>
<th>Datetime</th>
<th data-hide="all">Output</th>
</tr>
</thead>
<tbody>
{% for command in command_list %}
<tr>
<td>{{ command.id }}</td>
<td>{{ command.command }}</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>
</table>
{% endblock %}
{% block custom_foot_js %}
<script src="{% static "js/plugins/footable/footable.all.min.js" %}"></script>
<script src="{% static 'js/plugins/datepicker/bootstrap-datepicker.js' %}"></script>
<script>
$(document).ready(function () {
$('.footable').footable();
$('.select2').select2();
$('#date .input-daterange').datepicker({
dateFormat: 'mm/dd/yy',
keyboardNavigation: false,
forceParse: false,
autoclose: true
});
});
</script>
{% endblock %}

View File

@ -1,101 +0,0 @@
{% extends '_base_list.html' %}
{% load i18n %}
{% load static %}
{% load common_tags %}
{% block content_left_head %}
<link href="{% static 'css/plugins/datepicker/datepicker3.css' %}" rel="stylesheet">
<style>
#search_btn {
margin-bottom: 0;
}
</style>
{% endblock %}
{% block table_search %}
<form id="search_form" method="get" action="" class="pull-right form-inline">
<div class="form-group" id="date">
<div class="input-daterange input-group" id="datepicker">
<span class="input-group-addon"><i class="fa fa-calendar"></i></span>
<input type="text" class="input-sm form-control" style="width: 100px;" name="date_from" value="{{ date_from }}">
<span class="input-group-addon">to</span>
<input type="text" class="input-sm form-control" style="width: 100px;" name="date_to" value="{{ date_to }}">
</div>
</div>
<div class="input-group">
<select class="select2 form-control" name="username">
<option value="">{% trans 'Select user' %}</option>
{% for user in user_list %}
<option value="{{ user.username }}" {% if user.username == username %} selected {% endif %}>{{ user.username }}</option>
{% endfor %}
</select>
</div>
<div class="input-group">
<input type="text" class="form-control input-sm" name="keyword" placeholder="Search" value="{{ keyword }}">
</div>
<div class="input-group">
<div class="input-group-btn">
<button id='search_btn' type="submit" class="btn btn-sm btn-primary">
搜索
</button>
</div>
</div>
</form>
{% endblock %}
{% block table_head %}
<th class="text-center">{% trans 'ID' %}</th>
<th class="text-center">{% trans 'Username' %}</th>
<th class="text-center">{% trans 'Name' %}</th>
<th class="text-center">{% trans 'Type' %}</th>
<th class="text-center">{% trans 'UA' %}</th>
<th class="text-center">{% trans 'IP' %}</th>
<th class="text-center">{% trans 'City' %}</th>
<th class="text-center">{% trans 'Date' %}</th>
{% endblock %}
{% block table_body %}
{% for login_log in login_log_list %}
<tr class="gradeX">
<td class="text-center">
{{ login_log.id }}
{# <a href="{% url 'audits:proxy-log-detail' pk=login_log.id %}">{{ login_log.id }}</a>#}
</td>
<td class="text-center">{{ login_log.username }}</td>
<td class="text-center">{{ login_log.name }}</td>
<td class="text-center">{{ login_log.get_login_type_display }}</td>
{% if login_log.login_type == 'W' %}
<td class="text-center">
<span href="javascript:void(0);" data-toggle="tooltips" title="{{ login_log.user_agent }}">{{ login_log.user_agent | truncatechars:20 }}</span>
</td>
{% else %}
<td class="text-center">{{ login_log.terminal }}</td>
{% endif %}
<td class="text-center">{{ login_log.login_ip }}</td>
<td class="text-center">{{ login_log.login_city }}</td>
<td class="text-center">{{ login_log.date_login }}</td>
</tr>
{% endfor %}
{% endblock %}
{% block custom_foot_js %}
<script src="{% static 'js/plugins/datepicker/bootstrap-datepicker.js' %}"></script>
<script>
$(document).ready(function() {
$('table').DataTable({
"searching": false,
"bInfo" : false,
"paging": false,
"order": []
});
$('#date .input-daterange').datepicker({
dateFormat: 'mm/dd/yy',
keyboardNavigation: false,
forceParse: false,
autoclose: true
});
$('.select2').select2();
})
</script>
{% endblock %}

View File

@ -1,58 +0,0 @@
{% load static %}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="renderer" content="webkit">
{% include '_head_css_js.html' %}
<link href="{% static 'css/bootstrap.min.css' %}" rel="stylesheet">
<link href="{% static "css/plugins/footable/footable.core.css" %}" rel="stylesheet">
<script src="{% static 'js/jquery-2.1.1.js' %}"></script>
<script src="{% static 'js/plugins/sweetalert/sweetalert.min.js' %}"></script>
<script src="{% static 'js/bootstrap.min.js' %}"></script>
</head>
<body>
<div class="wrapper wrapper-content animated fadeInRight">
<div class="tab-content">
<div class="ibox-content">
<input type="text" class="form-control input-sm m-b-xs" id="filter"
placeholder="Search in table">
<table class="footable table table-stripped toggle-arrow-tiny" data-page-size="10" data-filter=#filter>
<thead>
<tr>
<th data-toggle="true">ID</th>
<th>Command</th>
<th data-hide="all">Output</th>
<th>Datetime</th>
</tr>
</thead>
<tbody class="table_body">
{% for command in object_list %}
<tr>
<td>{{ command.command_no }}</td>
<td>{{ command.command }}</td>
<td>{{ command.output_decode |safe }}</td>
<td>{{ command.datetime }}</td>
</tr>
{% endfor %}
</tbody>
<tfoot>
<tr>
<td colspan="5">
<ul class="pagination pull-right"></ul>
</td>
</tr>
</tfoot>
</table>
</div>
</div>
</div>
</body>
<script src="{% static "js/plugins/footable/footable.all.min.js" %}"></script>
<script>
$(document).ready(function () {
$('.footable').footable();
});
</script>
</html>

View File

@ -1,85 +0,0 @@
{% extends 'base.html' %}
{% load static %}
{% load i18n %}
{% load common_tags %}
{% block custom_head_css_js %}
<link href="{% static "css/plugins/footable/footable.core.css" %}" rel="stylesheet">
{% endblock %}
{% block content %}
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
<div class="col-sm-12">
<div class="ibox float-e-margins">
<div class="panel-options">
<ul class="nav nav-tabs">
<li class="active">
<a href="#" class="text-center"> {% trans 'Proxy log detail' %} </a>
</li>
</ul>
</div>
<div class="tab-content">
<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>
<div class="ibox-tools">
<a class="collapse-link">
<i class="fa fa-chevron-up"></i>
</a>
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
<i class="fa fa-wrench"></i>
</a>
<ul class="dropdown-menu dropdown-user">
</ul>
<a class="close-link">
<i class="fa fa-times"></i>
</a>
</div>
</div>
<div class="ibox-content">
<table id="command-log" class="footable table table-stripped toggle-arrow-tiny" data-page-size="10">
<thead>
<tr>
<th data-toggle="true">ID</th>
<th>Command</th>
<th data-hide="all">Output</th>
<th>Datetime</th>
</tr>
</thead>
<tbody>
{% for command in object_list %}
<tr>
<td>{{ command.command_no }}</td>
<td>{{ command.command }}</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>
<tfoot>
<tr>
<td colspan="5">
<ul class="pagination pull-right"></ul>
</td>
</tr>
</tfoot>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block custom_foot_js %}
<script src="{% static "js/plugins/footable/footable.all.min.js" %}"></script>
<script>
$(document).ready(function () {
$('.footable').footable();
});
</script>
{% endblock %}

View File

@ -1,159 +0,0 @@
{% extends '_base_list.html' %}
{% load i18n %}
{% load static %}
{% block content_left_head %}
<link href="{% static 'css/plugins/datepicker/datepicker3.css' %}" rel="stylesheet">
<style>
#search_btn {
margin-bottom: 0;
}
</style>
{% endblock %}
{% block table_search %}
<form id="search_form" method="get" action="" class="pull-right form-inline">
<div class="form-group" id="date">
<div class="input-daterange input-group" id="datepicker">
<span class="input-group-addon"><i class="fa fa-calendar"></i></span>
<input type="text" class="input-sm form-control" style="width: 100px;" name="date_from" value="{{ date_from }}">
<span class="input-group-addon">to</span>
<input type="text" class="input-sm form-control" style="width: 100px;" name="date_to" value="{{ date_to }}">
</div>
</div>
<div class="input-group">
<select class="select2 form-control" name="username">
<option value="">{% trans 'User' %}</option>
{% for u in user_list %}
<option value="{{ u }}" {% if u == username %} selected {% endif %}>{{ u }}</option>
{% endfor %}
</select>
</div>
<div class="input-group">
<select class="select2 form-control" name="ip">
<option value="">{% trans 'Asset' %}</option>
{% for a in asset_list %}
<option value="{{ a }}" {% if a == ip %} selected {% endif %}>{{ a }}</option>
{% endfor %}
</select>
</div>
<div class="input-group">
<select class="select2 form-control" name="system_user">
<option value="">{% trans 'System user' %}</option>
{% for su in system_user_list %}
<option value="{{ su }}" {% if su == system_user %} selected {% endif %}>{{ su }}</option>
{% endfor %}
</select>
</div>
<div class="input-group">
<input type="text" class="form-control input-sm" name="keyword" placeholder="Keyword" value="{{ keyword }}">
</div>
<div class="input-group">
<div class="input-group-btn">
<button id='search_btn' type="submit" class="btn btn-sm btn-primary">
搜索
</button>
</div>
</div>
</form>
{% endblock %}
{% block table_head %}
<th class="text-center"></th>
<th class="text-center">{% trans 'ID' %}</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 'Terminal' %}</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 'Play' %}</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"><input type="checkbox" class="cbx-term" value="{{ proxy_log.id }}"></td>
<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.terminal }}</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">
<a class="btn-term" value="{{ proxy_log.id }}"><i class="fa fa-times text-danger"></i></a>
</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 %}
<script src="{% static 'js/plugins/datepicker/bootstrap-datepicker.js' %}"></script>
<script>
function terminateConnection(data) {
function success() {
window.setTimeout(function () {
window.location.reload()
}, 300)
}
var the_url = "";
APIUpdateAttr({url: the_url, method: 'POST', body: JSON.stringify(data), success: success, success_message: 'Terminate success'});
}
$(document).ready(function() {
$('table').DataTable({
"searching": false,
"paging": false,
"bInfo" : false,
"order": []
});
$('.select2').select2();
$('#date .input-daterange').datepicker({
dateFormat: 'mm/dd/yy',
keyboardNavigation: false,
forceParse: false,
autoclose: true
});
}).on('click', '.btn-term', function () {
var $this = $(this);
var proxy_log_id = $this.attr('value');
var data = {
proxy_log_id: proxy_log_id
};
terminateConnection(data)
}).on('click', '#btn_bulk_update', function () {
var data = [];
$('.cbx-term:checked').each(function () {
data.push({proxy_log_id: $(this).attr('value')})
});
terminateConnection(data)
})
</script>
{% endblock %}

View File

@ -1,173 +0,0 @@
{% extends '_base_list.html' %}
{% load i18n %}
{% load static %}
{% block content_left_head %}
<link href="{% static 'css/plugins/datepicker/datepicker3.css' %}" rel="stylesheet">
<style>
#search_btn {
margin-bottom: 0;
}
</style>
{% endblock %}
{% block table_search %}
<form id="search_form" method="get" action="" class="pull-right form-inline">
<div class="form-group" id="date">
<div class="input-daterange input-group" id="datepicker">
<span class="input-group-addon"><i class="fa fa-calendar"></i></span>
<input type="text" class="input-sm form-control" style="width: 100px;" name="date_from" value="{{ date_from }}">
<span class="input-group-addon">to</span>
<input type="text" class="input-sm form-control" style="width: 100px;" name="date_to" value="{{ date_to }}">
</div>
</div>
<div class="input-group">
<select class="select2 form-control" name="username">
<option value="">{% trans 'User' %}</option>
{% for u in user_list %}
<option value="{{ u }}" {% if u == username %} selected {% endif %}>{{ u }}</option>
{% endfor %}
</select>
</div>
<div class="input-group">
<select class="select2 form-control" name="ip">
<option value="">{% trans 'Asset' %}</option>
{% for a in asset_list %}
<option value="{{ a }}" {% if a == ip %} selected {% endif %}>{{ a }}</option>
{% endfor %}
</select>
</div>
<div class="input-group">
<select class="select2 form-control" name="system_user">
<option value="">{% trans 'System user' %}</option>
{% for su in system_user_list %}
<option value="{{ su }}" {% if su == system_user %} selected {% endif %}>{{ su }}</option>
{% endfor %}
</select>
</div>
<div class="input-group">
<input type="text" class="form-control input-sm" name="keyword" placeholder="Keyword" value="{{ keyword }}">
</div>
<div class="input-group">
<div class="input-group-btn">
<button id='search_btn' type="submit" class="btn btn-sm btn-primary">
搜索
</button>
</div>
</div>
</form>
{% endblock %}
{% block table_head %}
<th class="text-center"></th>
<th class="text-center">{% trans 'ID' %}</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 'Terminal' %}</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 'Monitor' %}</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"><input type="checkbox" class="cbx-term" value="{{ proxy_log.id }}"></td>
<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.terminal }}</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">
<a class="btn-term" value="{{ proxy_log.id }}"><i class="fa fa-times text-danger"></i></a>
</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 content_bottom_left %}
<div id="actions">
<div class="input-group">
<select class="form-control m-b" style="width: auto" id="slct_bulk_update">
<option value="terminate">{% trans 'Terminate selected' %}</option>
</select>
<div class="input-group-btn pull-left" style="padding-left: 5px;">
<button id='btn_bulk_update' style="height: 32px;" class="btn btn-sm btn-primary">
{% trans 'Submit' %}
</button>
</div>
</div>
</div>
{% endblock %}
{% block custom_foot_js %}
<script src="{% static 'js/plugins/datepicker/bootstrap-datepicker.js' %}"></script>
<script>
function terminateConnection(data) {
function success() {
window.setTimeout(function () {
window.location.reload()
}, 300)
}
var the_url = "";
APIUpdateAttr({url: the_url, method: 'POST', body: JSON.stringify(data), success: success, success_message: 'Terminate success'});
}
$(document).ready(function() {
$('table').DataTable({
"searching": false,
"paging": false,
"bInfo" : false,
"order": []
});
$('.select2').select2();
$('#date .input-daterange').datepicker({
dateFormat: 'mm/dd/yy',
keyboardNavigation: false,
forceParse: false,
autoclose: true
});
}).on('click', '.btn-term', function () {
var $this = $(this);
var proxy_log_id = $this.attr('value');
var data = {
proxy_log_id: proxy_log_id
};
terminateConnection(data)
}).on('click', '#btn_bulk_update', function () {
var data = [];
$('.cbx-term:checked').each(function () {
data.push({proxy_log_id: $(this).attr('value')})
});
terminateConnection(data)
})
</script>
{% endblock %}

View File

@ -1,3 +0,0 @@
from django.test import TestCase
# Create your tests here.

View File

@ -1 +0,0 @@

View File

@ -1,18 +0,0 @@
from django.conf.urls import url
from rest_framework import routers
from .. import api
app_name = 'audits'
router = routers.DefaultRouter()
router.register(r'v1/proxy-log', api.ProxyLogViewSet, 'proxy-log')
router.register(r'v1/command-log', api.CommandLogViewSet, 'command-log')
router.register(r'v1/replay-log', api.RecordLogViewSet, 'replay-log')
urlpatterns = [
url(r'^v1/proxy-log/receive/$', api.ProxyLogReceiveView.as_view(),
name='proxy-log-receive'),
]
urlpatterns += router.urls

View File

@ -1,19 +0,0 @@
from django.conf.urls import url
from .. import views
app_name = 'audits'
urlpatterns = [
url(r'^proxy-log-offline/$', views.ProxyLogOfflineListView.as_view(),
name='proxy-log-offline-list'),
url(r'^proxy-log-online/$', views.ProxyLogOnlineListView.as_view(),
name='proxy-log-online-list'),
url(r'^proxy-log/(?P<pk>[0-9a-zA-Z\-]+)/$', views.ProxyLogDetailView.as_view(),
name='proxy-log-detail'),
url(r'^command-log/$', views.CommandLogListView.as_view(),
name='command-log-list'),
url(r'^login-log/$', views.LoginLogListView.as_view(),
name='login-log-list'),
]

View File

@ -1,49 +0,0 @@
# ~*~ coding: utf-8 ~*~
#
from __future__ import unicode_literals
import requests
import ipaddress
from .models import LoginLog
def validate_ip(ip):
try:
ipaddress.ip_address(ip)
return True
except ValueError:
print('valid error')
return False
def write_login_log(username, name='', login_type='',
login_ip='', user_agent=''):
if not (login_ip and validate_ip(login_ip)):
login_ip = '0.0.0.0'
if not name:
name = username
login_city = get_ip_city(login_ip)
LoginLog.objects.create(username=username, name=name, login_type=login_type,
login_ip=login_ip, login_city=login_city, user_agent=user_agent)
def get_ip_city(ip, timeout=10):
# Taobao ip api: http://ip.taobao.com//service/getIpInfo.php?ip=8.8.8.8
# Sina ip api: http://int.dpool.sina.com.cn/iplookup/iplookup.php?ip=8.8.8.8&format=json
url = 'http://int.dpool.sina.com.cn/iplookup/iplookup.php?ip=%s&format=json' % ip
try:
r = requests.get(url, timeout=timeout)
except requests.Timeout:
r = None
city = 'Unknown'
if r and r.status_code == 200:
try:
data = r.json()
if not isinstance(data, int) and data['ret'] == 1:
city = data['country'] + ' ' + data['city']
except ValueError:
pass
return city

View File

@ -1,272 +0,0 @@
# ~*~ coding: utf-8 ~*~
#
import time
from datetime import datetime
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):
model = ProxyLog
template_name = 'audits/proxy_log_online_list.html'
context_object_name = 'proxy_log_list'
paginate_by = settings.CONFIG.DISPLAY_PER_PAGE
keyword = username = hostname = 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())
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 = self.request.GET.get('keyword', '')
self.username = self.request.GET.get('username')
self.ip = self.request.GET.get('ip')
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)
filter_kwargs = {}
if self.date_from_s:
date_from = datetime.strptime(self.date_from_s, self.date_format)
date_from = 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 = date_to.replace(tzinfo=timezone.get_current_timezone())
filter_kwargs['date_start__lt'] = date_to
if self.username:
filter_kwargs['user'] = self.username
if self.ip:
filter_kwargs['asset'] = self.ip
if self.system_user:
filter_kwargs['system_user'] = self.system_user
if self.keyword:
self.queryset = self.queryset.filter(
Q(user__icontains=self.keyword) |
Q(asset__icontains=self.keyword) |
Q(system_user__icontains=self.keyword)).distinct()
if filter_kwargs:
self.queryset = self.queryset.filter(**filter_kwargs)
return self.queryset
def get_context_data(self, **kwargs):
context = {
'app': _('Audits'),
'action': _('Proxy log list'),
'user_list': set(
list(ProxyLog.objects.values_list('user', flat=True))),
'asset_list': set(
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,
'system_user': self.system_user,
}
kwargs.update(context)
return super(ProxyLogListView, self).get_context_data(**kwargs)
class ProxyLogOfflineListView(ProxyLogListView):
template_name = 'audits/proxy_log_offline_list.html'
def get_queryset(self):
queryset = super(ProxyLogOfflineListView, self).get_queryset()
queryset = queryset.filter(is_finished=True)
return queryset
def get_context_data(self, **kwargs):
context = {
'action': _('Proxy log offline list'),
}
kwargs.update(context)
return super(ProxyLogOfflineListView, self).get_context_data(**kwargs)
class ProxyLogOnlineListView(ProxyLogListView):
template_name = 'audits/proxy_log_online_list.html'
def get_queryset(self):
queryset = super(ProxyLogOnlineListView, self).get_queryset()
queryset = queryset.filter(is_finished=False)
return queryset
def get_context_data(self, **kwargs):
context = {
'action': _('Proxy log online list'),
}
kwargs.update(context)
return super(ProxyLogOnlineListView, self).get_context_data(**kwargs)
class ProxyLogDetailView(AdminUserRequiredMixin,
SingleObjectMixin,
ListView):
template_name = 'audits/proxy_log_detail.html'
context_object_name = 'proxy_log'
object = ''
def get(self, request, *args, **kwargs):
self.object = self.get_object(queryset=ProxyLog.objects.all())
return super(ProxyLogDetailView, self).get(request, *args, **kwargs)
def get_queryset(self):
return list(command_store.filter(proxy_log_id=self.object.id))
def get_context_data(self, **kwargs):
context = {
'app': 'Audits',
'action': 'Proxy log detail',
}
kwargs.update(context)
return super(ProxyLogDetailView, self).get_context_data(**kwargs)
# class ProxyLogCommandsListView(AdminUserRequiredMixin,
# SingleObjectMixin,
# ListView):
# template_name = 'audits/proxy_log_commands_list_modal.html'
# object = ''
#
# def get(self, request, *args, **kwargs):
# self.object = self.get_object(queryset=ProxyLog.objects.all())
# return super(ProxyLogCommandsListView, self).\
# get(request, *args, **kwargs)
#
# def get_queryset(self):
# return list(self.object.command_log.all())
class CommandLogListView(AdminUserRequiredMixin, ListView):
template_name = 'audits/command_log_list.html'
paginate_by = settings.CONFIG.DISPLAY_PER_PAGE
context_object_name = 'command_list'
username = ip = 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())
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.username = self.request.GET.get('username')
self.ip = self.request.GET.get('ip')
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)
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.username:
filter_kwargs['user'] = self.username
if self.ip:
filter_kwargs['asset'] = self.ip
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):
context = {
'app': _('Audits'),
'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('username'),
'command': self.command,
'date_from': self.date_from_s,
'date_to': self.date_to_s,
'username': self.username,
'ip': self.ip,
'system_user': self.system_user,
}
kwargs.update(context)
return super(CommandLogListView, self).get_context_data(**kwargs)
class LoginLogListView(AdminUserRequiredMixin, ListView):
model = LoginLog
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):
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(LoginLogListView, self).get_queryset()
self.keyword = keyword = self.request.GET.get('keyword', '')
self.username = username = self.request.GET.get('username', '')
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)
if date_from_s:
date_from = timezone.datetime.strptime(date_from_s, '%m/%d/%Y')
self.queryset = self.queryset.filter(date_login__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_login__lt=date_to)
if username:
self.queryset = self.queryset.filter(username=username)
if keyword:
self.queryset = self.queryset.filter(
Q(username__contains=keyword) |
Q(name__icontains=keyword) |
Q(login_ip=keyword)).distinct()
return self.queryset
def get_context_data(self, **kwargs):
context = {
'app': _('Audits'),
'action': _('Proxy log list'),
'user_list': User.objects.all().order_by('username'),
'keyword': self.keyword,
'date_from': self.date_from_s,
'date_to': self.date_to_s,
'username': self.username,
}
kwargs.update(context)
return super(LoginLogListView, self).get_context_data(**kwargs)

View File

@ -3,7 +3,6 @@
from django import template from django import template
from django.utils import timezone from django.utils import timezone
from django.utils.html import escape from django.utils.html import escape
from audits.backends import command_store
register = template.Library() register = template.Library()
@ -70,6 +69,6 @@ def to_html(s):
return escape(s).replace('\n', '<br />') return escape(s).replace('\n', '<br />')
@register.filter # @register.filter
def proxy_log_commands(log_id): # def proxy_log_commands(log_id):
return command_store.filter(proxy_log_id=log_id) # return 1

View File

@ -1,4 +1,2 @@
from __future__ import absolute_import, unicode_literals from __future__ import absolute_import, unicode_literals
from django.shortcuts import render
from django.views.generic import TemplateView

View File

@ -9,7 +9,7 @@ from django.shortcuts import HttpResponse
DEMO_MODE = os.environ.get("DEMO_MODE", "") DEMO_MODE = os.environ.get("DEMO_MODE", "")
SAFE_URL = r'^/users/login|^/api/terminal/v1/.*|/api/audits/.*|/api/users/v1/auth/|/api/users/v1/profile/' SAFE_URL = r'^/users/login|^/api/terminal/v1/.*|/api/terminal/.*|/api/users/v1/auth/|/api/users/v1/profile/'
class TimezoneMiddleware(MiddlewareMixin): class TimezoneMiddleware(MiddlewareMixin):

View File

@ -59,7 +59,7 @@ INSTALLED_APPS = [
'assets.apps.AssetsConfig', 'assets.apps.AssetsConfig',
'perms.apps.PermsConfig', 'perms.apps.PermsConfig',
'ops.apps.OpsConfig', 'ops.apps.OpsConfig',
'audits.apps.AuditsConfig', # 'audits.apps.AuditsConfig',
'common.apps.CommonConfig', 'common.apps.CommonConfig',
'terminal.apps.ApplicationsConfig', 'terminal.apps.ApplicationsConfig',
'rest_framework', 'rest_framework',
@ -364,7 +364,7 @@ CAPTCHA_NOISE_FUNCTIONS = ('captcha.helpers.noise_dots',)
CAPTCHA_TEST_MODE = CONFIG.CAPTCHA_TEST_MODE CAPTCHA_TEST_MODE = CONFIG.CAPTCHA_TEST_MODE
COMMAND_STORE_BACKEND = 'terminal.backends.command.db' COMMAND_STORE_BACKEND = 'terminal.backends.command.db'
RECORD_STORE_BACKEND = 'terminal.backends.replay.db' REPLAY_STORE_BACKEND = 'terminal.backends.replay.db'
# Django bootstrap3 setting, more see http://django-bootstrap3.readthedocs.io/en/latest/settings.html # Django bootstrap3 setting, more see http://django-bootstrap3.readthedocs.io/en/latest/settings.html

View File

@ -16,7 +16,7 @@ urlpatterns = [
url(r'^users/', include('users.urls.views_urls', namespace='users')), url(r'^users/', include('users.urls.views_urls', namespace='users')),
url(r'^assets/', include('assets.urls.views_urls', namespace='assets')), url(r'^assets/', include('assets.urls.views_urls', namespace='assets')),
url(r'^perms/', include('perms.urls.views_urls', namespace='perms')), url(r'^perms/', include('perms.urls.views_urls', namespace='perms')),
url(r'^audits/', include('audits.urls.views_urls', namespace='audits')), # url(r'^audits/', include('audits.urls.views_urls', namespace='audits')),
url(r'^terminal/', include('terminal.urls.views_urls', namespace='terminal')), url(r'^terminal/', include('terminal.urls.views_urls', namespace='terminal')),
url(r'^ops/', include('ops.urls.view_urls', namespace='ops')), url(r'^ops/', include('ops.urls.view_urls', namespace='ops')),
@ -24,7 +24,7 @@ urlpatterns = [
url(r'^api/users/', include('users.urls.api_urls', namespace='api-users')), url(r'^api/users/', include('users.urls.api_urls', namespace='api-users')),
url(r'^api/assets/', include('assets.urls.api_urls', namespace='api-assets')), url(r'^api/assets/', include('assets.urls.api_urls', namespace='api-assets')),
url(r'^api/perms/', include('perms.urls.api_urls', namespace='api-perms')), url(r'^api/perms/', include('perms.urls.api_urls', namespace='api-perms')),
url(r'^api/audits/', include('audits.urls.api_urls', namespace='api-audits')), # url(r'^api/audits/', include('audits.urls.api_urls', namespace='api-audits')),
url(r'^api/terminal/', include('terminal.urls.api_urls', namespace='api-terminal')), url(r'^api/terminal/', include('terminal.urls.api_urls', namespace='api-terminal')),
url(r'^api/ops/', include('ops.urls.api_urls', namespace='api-ops')), url(r'^api/ops/', include('ops.urls.api_urls', namespace='api-ops')),
url(r'^captcha/', include('captcha.urls')), url(r'^captcha/', include('captcha.urls')),

View File

@ -6,70 +6,146 @@ from django.shortcuts import redirect
from users.models import User from users.models import User
from assets.models import Asset from assets.models import Asset
from audits.models import ProxyLog from terminal.models import Session
class IndexView(LoginRequiredMixin, TemplateView): class IndexView(LoginRequiredMixin, TemplateView):
template_name = 'index.html' template_name = 'index.html'
session_week = None
session_month = None
session_month_dates = []
session_month_dates_archive = []
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
if not request.user.is_superuser: if not request.user.is_superuser:
return redirect('assets:user-asset-list') return redirect('assets:user-asset-list')
return super(IndexView, self).get(request, *args, **kwargs) return super(IndexView, self).get(request, *args, **kwargs)
def get_context_data(self, **kwargs): @staticmethod
seven_days_ago = timezone.now() - timezone.timedelta(days=7) def get_user_count():
month_ago = timezone.now() - timezone.timedelta(days=30) return User.objects.filter(role__in=('Admin', 'User')).count()
proxy_log_seven_days = ProxyLog.objects.filter(date_start__gt=seven_days_ago, is_failed=False)
proxy_log_month = ProxyLog.objects.filter(date_start__gt=month_ago, is_failed=False) @staticmethod
month_dates = proxy_log_month.dates('date_start', 'day') def get_asset_count():
month_total_visit = [ProxyLog.objects.filter(date_start__date=d) for d in month_dates] return Asset.objects.all().count()
month_str = [d.strftime('%m-%d') for d in month_dates] or ['0']
month_total_visit_count = [p.count() for p in month_total_visit] or [0] @staticmethod
month_user = [p.values('user').distinct().count() for p in month_total_visit] or [0] def get_online_user_count():
month_asset = [p.values('asset').distinct().count() for p in month_total_visit] or [0] return len(set(Session.objects.filter(is_finished=False).values_list('user', flat=True)))
month_user_active = User.objects.filter(last_login__gt=month_ago).count()
month_user_inactive = User.objects.filter(last_login__lt=month_ago).count() @staticmethod
month_user_disabled = User.objects.filter(is_active=False).count() def get_online_session_count():
month_asset_active = proxy_log_month.values('asset').distinct().count() return Session.objects.filter(is_finished=False).count()
month_asset_inactive = Asset.objects.all().count() - month_asset_active
month_asset_disabled = Asset.objects.filter(is_active=False).count() def get_top5_user_a_week(self):
week_asset_hot_ten = list(proxy_log_seven_days.values('asset').annotate(total=Count('asset')).order_by('-total')[:10]) return self.session_week.values('user').annotate(total=Count('user')).order_by('-total')[:5]
for p in week_asset_hot_ten:
last_login = ProxyLog.objects.filter(asset=p['asset']).order_by('date_start').last() def get_week_login_user_count(self):
p['last'] = last_login return self.session_week.values('user').distinct().count()
last_login_ten = ProxyLog.objects.all().order_by('-date_start')[:10]
for p in last_login_ten: def get_week_login_asset_count(self):
return self.session_week.values('asset').distinct().count()
def get_month_day_metrics(self):
month_str = [d.strftime('%m-%d') for d in self.session_month_dates] or ['0']
return month_str
def get_month_login_metrics(self):
return [self.session_month.filter(date_start__date=d).count()
for d in self.session_month_dates]
def get_month_active_user_metrics(self):
if self.session_month_dates_archive:
return [q.values('user').distinct().count()
for q in self.session_month_dates_archive]
else:
return [0]
def get_month_active_asset_metrics(self):
if self.session_month_dates_archive:
return [q.values('asset').distinct().count()
for q in self.session_month_dates_archive]
else:
return [0]
def get_month_active_user_total(self):
return self.session_month.values('user').distinct().count()
def get_month_inactive_user_total(self):
return User.objects.all().count() - self.get_month_active_user_total()
def get_month_active_asset_total(self):
return self.session_month.values('asset').distinct().count()
def get_month_inactive_asset_total(self):
return Asset.objects.all().count() - self.get_month_active_asset_total()
@staticmethod
def get_user_disabled_total():
return User.objects.filter(is_active=False).count()
@staticmethod
def get_asset_disabled_total():
return Asset.objects.filter(is_active=False).count()
def get_week_top10_asset(self):
assets = list(self.session_week.values('asset').annotate(total=Count('asset')).order_by('-total')[:10])
for asset in assets:
last_login = self.session_week.filter(asset=asset["asset"]).order_by('date_start').last()
asset['last'] = last_login
print(asset)
return assets
def get_week_top10_user(self):
users = list(self.session_week.values('user').annotate(
total=Count('asset')).order_by('-total')[:10])
for user in users:
last_login = self.session_week.filter(user=user["user"]).order_by('date_start').last()
user['last'] = last_login
return users
def get_last10_sessions(self):
sessions = self.session_week.order_by('-date_start')[:10]
for session in sessions:
try: try:
p.avatar_url = User.objects.get(username=p.user).avatar_url() session.avatar_url = User.objects.get(username=session.user).avatar_url()
except User.DoesNotExist: except User.DoesNotExist:
p.avatar_url = User.objects.first().avatar_url() session.avatar_url = User.objects.first().avatar_url()
week_user_hot_ten = list(proxy_log_seven_days.values('user').annotate(total=Count('user')).order_by('-total')[:10]) return sessions
for p in week_user_hot_ten:
last_login = ProxyLog.objects.filter(user=p['user']).order_by('date_start').last() def get_context_data(self, **kwargs):
p['last'] = last_login week_ago = timezone.now() - timezone.timedelta(weeks=1)
month_ago = timezone.now() - timezone.timedelta(days=30)
self.session_week = Session.objects.filter(date_start__gt=week_ago)
self.session_month = Session.objects.filter(date_start__gt=month_ago)
self.session_month_dates = self.session_month.dates('date_start', 'day')
self.session_month_dates_archive = [
self.session_month.filter(date_start__date=d)
for d in self.session_month_dates
]
context = { context = {
'assets_count': Asset.objects.count(), 'assets_count': self.get_asset_count(),
'users_count': User.objects.filter(role__in=('Admin', 'User')).count(), 'users_count': self.get_user_count(),
'online_user_count': ProxyLog.objects.filter(is_finished=False).values('user').distinct().count(), 'online_user_count': self.get_online_user_count(),
'online_asset_count': ProxyLog.objects.filter(is_finished=False).values('asset').distinct().count(), 'online_asset_count': self.get_online_session_count(),
'user_visit_count_weekly': proxy_log_seven_days.values('user').distinct().count(), 'user_visit_count_weekly': self.get_week_login_user_count(),
'asset_visit_count_weekly': proxy_log_seven_days.count(), 'asset_visit_count_weekly': self.get_week_login_asset_count(),
'user_visit_count_top_five': proxy_log_seven_days.values('user').annotate(total=Count('user')).order_by('-total')[:5], 'user_visit_count_top_five': self.get_top5_user_a_week(),
'month_str': month_str, 'month_str': self.get_month_day_metrics(),
'month_total_visit_count': month_total_visit_count, 'month_total_visit_count': self.get_month_login_metrics(),
'month_user': month_user, 'month_user': self.get_month_active_user_metrics(),
'mouth_asset': month_asset, 'mouth_asset': self.get_month_active_asset_metrics(),
'month_user_active': month_user_active, 'month_user_active': self.get_month_active_user_total(),
'month_user_inactive': month_user_inactive, 'month_user_inactive': self.get_month_inactive_user_total(),
'month_user_disabled': month_user_disabled, 'month_user_disabled': self.get_user_disabled_total(),
'month_asset_active': month_asset_active, 'month_asset_active': self.get_month_active_asset_total(),
'month_asset_inactive': month_asset_inactive, 'month_asset_inactive': self.get_month_inactive_asset_total(),
'month_asset_disabled': month_asset_disabled, 'month_asset_disabled': self.get_asset_disabled_total(),
'week_asset_hot_ten': week_asset_hot_ten, 'week_asset_hot_ten': self.get_week_top10_asset(),
'last_login_ten': last_login_ten, 'last_login_ten': self.get_last10_sessions(),
'week_user_hot_ten': week_user_hot_ten, 'week_user_hot_ten': self.get_week_top10_user(),
} }
kwargs.update(context) kwargs.update(context)

View File

@ -35,7 +35,7 @@
<h5>在线用户</h5> <h5>在线用户</h5>
</div> </div>
<div class="ibox-content"> <div class="ibox-content">
<h1 class="no-margins"><a href="{% url 'audits:proxy-log-online-list' %}"> <span id="online_users"></span>{{ online_user_count }}</a></h1> <h1 class="no-margins"><a href="{% url 'terminal:session-online-list' %}"> <span id="online_users"></span>{{ online_user_count }}</a></h1>
<small>Online user</small> <small>Online user</small>
</div> </div>
</div> </div>
@ -45,11 +45,11 @@
<div class="ibox float-e-margins"> <div class="ibox float-e-margins">
<div class="ibox-title"> <div class="ibox-title">
<span class="label label-danger pull-right">Connected</span> <span class="label label-danger pull-right">Connected</span>
<h5>已连接服务器</h5> <h5>在线会话</h5>
</div> </div>
<div class="ibox-content"> <div class="ibox-content">
<h1 class="no-margins"><a href="{% url 'audits:proxy-log-online-list' %}"> <span id="online_hosts"></span>{{ online_asset_count }}</a></h1> <h1 class="no-margins"><a href="{% url 'terminal:session-online-list' %}"> <span id="online_hosts"></span>{{ online_asset_count }}</a></h1>
<small>Connected host</small> <small>Online sessions</small>
</div> </div>
</div> </div>
</div> </div>

View File

@ -1,10 +1,8 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
import base64
from collections import OrderedDict from collections import OrderedDict
import copy import copy
import logging import logging
import tarfile
import os import os
from rest_framework import viewsets, serializers from rest_framework import viewsets, serializers
@ -18,7 +16,7 @@ from common.utils import get_object_or_none
from .models import Terminal, Status, Session, Task from .models import Terminal, Status, Session, Task
from .serializers import TerminalSerializer, StatusSerializer, \ from .serializers import TerminalSerializer, StatusSerializer, \
SessionSerializer, TaskSerializer SessionSerializer, TaskSerializer
from .hands import IsSuperUserOrAppUser, IsAppUser, ProxyLog, \ from .hands import IsSuperUserOrAppUser, IsAppUser, \
IsSuperUserOrAppUserOrUserReadonly IsSuperUserOrAppUserOrUserReadonly
from .backends import get_command_store, get_replay_store, SessionCommandSerializer from .backends import get_command_store, get_replay_store, SessionCommandSerializer
@ -221,7 +219,9 @@ class CommandViewSet(viewsets.ViewSet):
else: else:
return Response("Save error", status=500) return Response("Save error", status=500)
else: else:
return Response({"msg": "Not valid: {}".format(serializer.errors)}, status=401) msg = "Not valid: {}".format(serializer.errors)
logger.error(msg)
return Response({"msg": msg}, status=401)
def list(self, request, *args, **kwargs): def list(self, request, *args, **kwargs):
queryset = list(self.command_store.all()) queryset = list(self.command_store.all())

View File

@ -12,6 +12,6 @@ def get_command_store():
def get_replay_store(): def get_replay_store():
replay_engine = import_module(settings.RECORD_STORE_BACKEND) replay_engine = import_module(settings.REPLAY_STORE_BACKEND)
replay_store = replay_engine.RecordStore() replay_store = replay_engine.ReplayStore()
return replay_store return replay_store

View File

@ -2,7 +2,7 @@
import abc import abc
class RecordBase(object): class ReplayBase(object):
__metaclass__ = abc.ABCMeta __metaclass__ = abc.ABCMeta
@abc.abstractmethod @abc.abstractmethod

View File

@ -1,31 +1,8 @@
# ~*~ coding: utf-8 ~*~ # ~*~ coding: utf-8 ~*~
from .base import RecordBase from .base import ReplayBase
from audits.models import RecordLog
class RecordStore(RecordBase): class ReplayStore(ReplayBase):
model = RecordLog pass
queryset = []
def save(self, proxy_log_id, output, timestamp):
return self.model.objects.create(
proxy_log_id=proxy_log_id, output=output, timestamp=timestamp
)
def filter(self, date_from_ts=None, proxy_log_id=''):
filter_kwargs = {}
if date_from_ts:
filter_kwargs['timestamp__gte'] = date_from_ts
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.all()

View File

@ -1,20 +0,0 @@
# ~*~ coding: utf-8 ~*~
import base64
from rest_framework import serializers
from audits.models import RecordLog
from audits.backends import record_store
class RecordSerializer(serializers.ModelSerializer):
"""使用这个类作为基础Command Log Serializer类, 用来序列化"""
class Meta:
model = RecordLog
fields = '__all__'
def create(self, validated_data):
try:
output = validated_data['output']
validated_data['output'] = base64.b64decode(output)
except IndexError:
pass
return record_store.save(**dict(validated_data))

View File

@ -4,5 +4,4 @@
from users.models import User from users.models import User
from users.permissions import IsSuperUserOrAppUser, IsAppUser, \ from users.permissions import IsSuperUserOrAppUser, IsAppUser, \
IsSuperUserOrAppUserOrUserReadonly IsSuperUserOrAppUserOrUserReadonly
from audits.models import ProxyLog
from users.utils import AdminUserRequiredMixin from users.utils import AdminUserRequiredMixin

View File

@ -13,6 +13,7 @@ ASSETS_CACHE_KEY = "terminal__session__assets"
USERS_CACHE_KEY = "terminal__session__users" USERS_CACHE_KEY = "terminal__session__users"
SYSTEM_USER_CACHE_KEY = "terminal__session__system_users" SYSTEM_USER_CACHE_KEY = "terminal__session__system_users"
CACHE_REFRESH_INTERVAL = 10 CACHE_REFRESH_INTERVAL = 10
RUNNING = False
# Todo: 定期清理上报history # Todo: 定期清理上报history
@ -46,7 +47,16 @@ def set_cache():
def main(): def main():
global RUNNING
if RUNNING:
return
threads = []
thread = threading.Thread(target=set_cache) thread = threading.Thread(target=set_cache)
thread.start() threads.append(thread)
for t in threads:
t.daemon = True
t.start()
RUNNING = True
main() main()

View File

@ -9,7 +9,7 @@ from rest_framework_bulk import BulkModelViewSet
from django_filters.rest_framework import DjangoFilterBackend from django_filters.rest_framework import DjangoFilterBackend
from . import serializers from . import serializers
from .hands import write_login_log_async from .tasks import write_login_log_async
from .models import User, UserGroup from .models import User, UserGroup
from .permissions import IsSuperUser, IsValidUser, IsCurrentUserOrReadOnly from .permissions import IsSuperUser, IsValidUser, IsCurrentUserOrReadOnly
from .utils import check_user_valid, generate_token from .utils import check_user_valid, generate_token

View File

@ -10,8 +10,8 @@
:license: GPL v2, see LICENSE for more details. :license: GPL v2, see LICENSE for more details.
""" """
from terminal.models import Terminal # from terminal.models import Terminal
from audits.tasks import write_login_log_async # from audits.tasks import write_login_log_async
from users.models import User # from users.models import User
# from perms.models import AssetPermission # from perms.models import AssetPermission
# from perms.utils import get_user_granted_assets, get_user_granted_asset_groups # from perms.utils import get_user_granted_assets, get_user_granted_asset_groups

View File

@ -8,7 +8,7 @@ from django.utils.translation import ugettext_lazy as _
from rest_framework.authtoken.models import Token from rest_framework.authtoken.models import Token
from . import User from . import User
__all__ = ['AccessKey', 'PrivateToken'] __all__ = ['AccessKey', 'PrivateToken', 'LoginLog']
class AccessKey(models.Model): class AccessKey(models.Model):
@ -34,3 +34,22 @@ class PrivateToken(Token):
class Meta: class Meta:
verbose_name = _('Private Token') verbose_name = _('Private Token')
class LoginLog(models.Model):
LOGIN_TYPE_CHOICE = (
('W', 'Web'),
('ST', 'SSH Terminal'),
('WT', 'Web Terminal')
)
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
username = models.CharField(max_length=20, verbose_name=_('Username'))
type = models.CharField(choices=LOGIN_TYPE_CHOICE, max_length=2, verbose_name=_('Login type'))
ip = models.GenericIPAddressField(verbose_name=_('Login ip'))
city = models.CharField(max_length=254, blank=True, null=True, verbose_name=_('Login city'))
user_agent = models.CharField(max_length=254, blank=True, null=True, verbose_name=_('User agent'))
datetime = models.DateTimeField(auto_now_add=True, verbose_name=_('Date login'))
class Meta:
db_table = 'login_log'
ordering = ['-datetime', 'username']

View File

@ -24,9 +24,8 @@ class UserGroup(NoDeleteModelMixin):
verbose_name=_('Date created')) verbose_name=_('Date created'))
created_by = models.CharField(max_length=100) created_by = models.CharField(max_length=100)
def __unicode__(self): def __str__(self):
return self.name return self.name
__str__ = __unicode__
def delete(self, using=None, keep_parents=False): def delete(self, using=None, keep_parents=False):
if self.name != 'Default': if self.name != 'Default':

View File

@ -1,5 +1,4 @@
#!/usr/bin/env python # -*- coding: utf-8 -*-
# ~*~ coding: utf-8 ~*~
# #
from celery import shared_task from celery import shared_task

View File

@ -5,7 +5,8 @@ import base64
import logging import logging
import uuid import uuid
from paramiko.rsakey import RSAKey import requests
import ipaddress
from django.conf import settings from django.conf import settings
from django.contrib.auth.mixins import UserPassesTestMixin from django.contrib.auth.mixins import UserPassesTestMixin
from django.contrib.auth import authenticate from django.contrib.auth import authenticate
@ -14,13 +15,7 @@ from django.core.cache import cache
from common.tasks import send_mail_async from common.tasks import send_mail_async
from common.utils import reverse, get_object_or_none from common.utils import reverse, get_object_or_none
from .models import User from .models import User, LoginLog
# try:
# from io import StringIO
# except ImportError:
# from StringIO import StringIO
logger = logging.getLogger('jumpserver') logger = logging.getLogger('jumpserver')
@ -172,3 +167,40 @@ def generate_token(request, user):
return token return token
def validate_ip(ip):
try:
ipaddress.ip_address(ip)
return True
except ValueError:
pass
return False
def write_login_log(username, type='', ip='', user_agent=''):
if not (ip and validate_ip(ip)):
ip = '0.0.0.0'
city = get_ip_city(ip)
LoginLog.objects.create(
username=username, type=type,
ip=ip, city=city, user_agent=user_agent
)
def get_ip_city(ip, timeout=10):
# Taobao ip api: http://ip.taobao.com//service/getIpInfo.php?ip=8.8.8.8
# Sina ip api: http://int.dpool.sina.com.cn/iplookup/iplookup.php?ip=8.8.8.8&format=json
url = 'http://int.dpool.sina.com.cn/iplookup/iplookup.php?ip=%s&format=json' % ip
try:
r = requests.get(url, timeout=timeout)
except requests.Timeout:
r = None
city = 'Unknown'
if r and r.status_code == 200:
try:
data = r.json()
if not isinstance(data, int) and data['ret'] == 1:
city = data['country'] + ' ' + data['city']
except ValueError:
pass
return city

View File

@ -21,7 +21,7 @@ from django.conf import settings
from common.utils import get_object_or_none from common.utils import get_object_or_none
from ..models import User from ..models import User
from ..utils import send_reset_password_mail from ..utils import send_reset_password_mail
from ..hands import write_login_log_async from ..tasks import write_login_log_async
from .. import forms from .. import forms