Commit 688bfa55 authored by ibuler's avatar ibuler

Merge with audits

parents f88c1490 589d0d0f
......@@ -3,6 +3,7 @@ from __future__ import unicode_literals, absolute_import
import functools
from django.db import models
from django.core import serializers
import logging
from django.utils.translation import ugettext_lazy as _
......@@ -322,8 +323,8 @@ class Asset(models.Model):
created_by = models.CharField(max_length=32, null=True, blank=True, verbose_name=_('Created by'))
is_active = models.BooleanField(default=True, verbose_name=_('Is active'))
date_created = models.DateTimeField(auto_now_add=True, null=True, blank=True, verbose_name=_('Date added'))
comment = models.TextField(max_length=128, null=True, blank=True, verbose_name=_('Comment'))
tags = models.ManyToManyField('Tag', verbose_name='标签集合', blank=True)
comment = models.TextField(max_length=128, default='', blank=True, verbose_name=_('Comment'))
tags = models.ManyToManyField('Tag', blank=True, verbose_name=_('Tags'))
def __unicode__(self):
return '%(ip)s:%(port)s' % {'ip': self.ip, 'port': self.port}
......@@ -336,6 +337,9 @@ class Asset(models.Model):
return True, ''
return False, warning
def json(self):
pass
class Meta:
db_table = 'asset'
unique_together = ('ip', 'port')
......
......@@ -65,7 +65,6 @@ class AssetCreateView(AdminUserRequiredMixin,CreateAssetTagsMiXin,CreateView):
print(form.errors)
return super(AssetCreateView, self).form_invalid(form)
def get_context_data(self, **kwargs):
context = {
'app': 'Assets',
......@@ -75,21 +74,24 @@ class AssetCreateView(AdminUserRequiredMixin,CreateAssetTagsMiXin,CreateView):
return super(AssetCreateView, self).get_context_data(**kwargs)
class AssetModalCreateView(AdminUserRequiredMixin,CreateAssetTagsMiXin,ListView):
class AssetModalCreateView(AdminUserRequiredMixin, CreateAssetTagsMiXin, ListView):
model = Asset
# tag_type = 'asset'
form_class = AssetCreateForm
template_name = 'assets/asset_modal_update.html'
success_url = reverse_lazy('assets:asset-list')
def get_queryset(self):
self.queryset = super(AssetModalCreateView,self).get_queryset()
self.s = self.request.GET.get('plain_id_lists')
if "," in str(self.s):
self.plain_id_lists = [int(x) for x in self.s.split(',')]
self.plain_id_lists = [int(x) for x in self.s.split(',')]
else:
self.plain_id_lists = [self.s]
return self.queryset
def get_context_data(self, **kwargs):
asset_on_list = Asset.objects.filter(id__in = self.plain_id_lists)
context = {
......@@ -102,7 +104,8 @@ class AssetModalCreateView(AdminUserRequiredMixin,CreateAssetTagsMiXin,ListView)
kwargs.update(context)
return super(AssetModalCreateView, self).get_context_data(**kwargs)
class AssetUpdateView(AdminUserRequiredMixin,UpdateAssetTagsMiXin,UpdateView):
class AssetUpdateView(AdminUserRequiredMixin, UpdateAssetTagsMiXin, UpdateView):
model = Asset
form_class = AssetCreateForm
template_name = 'assets/asset_update.html'
......@@ -144,13 +147,13 @@ class AssetDetailView(DetailView):
kwargs.update(context)
return super(AssetDetailView, self).get_context_data(**kwargs)
class AssetModalListView(AdminUserRequiredMixin, ListView):
paginate_by = settings.CONFIG.DISPLAY_PER_PAGE
model = Asset
context_object_name = 'asset_modal_list'
template_name = 'assets/asset_modal_list.html'
def get_context_data(self, **kwargs):
group_id = self.request.GET.get('group_id')
tag_id = self.request.GET.get('tag_id')
......@@ -160,7 +163,7 @@ class AssetModalListView(AdminUserRequiredMixin, ListView):
self.plain_id_lists = [int(x) for x in self.s.split(',')]
else:
self.plain_id_lists = [self.s]
print plain_id_lists
if plain_id_lists:
context = {
'all_assets':plain_id_lists
......@@ -179,6 +182,7 @@ class AssetModalListView(AdminUserRequiredMixin, ListView):
kwargs.update(context)
return super(AssetModalListView, self).get_context_data(**kwargs)
class AssetGroupCreateView(AdminUserRequiredMixin, CreateView):
model = AssetGroup
form_class = AssetGroupForm
......@@ -197,7 +201,6 @@ class AssetGroupCreateView(AdminUserRequiredMixin, CreateView):
kwargs.update(context)
return super(AssetGroupCreateView, self).get_context_data(**kwargs)
def form_valid(self, form):
asset_group = form.save()
assets_id_list = self.request.POST.getlist('assets', [])
......@@ -207,6 +210,7 @@ class AssetGroupCreateView(AdminUserRequiredMixin, CreateView):
asset_group.save()
return super(AssetGroupCreateView, self).form_valid(form)
class AssetGroupListView(AdminUserRequiredMixin, ListView):
model = AssetGroup
paginate_by = settings.CONFIG.DISPLAY_PER_PAGE
......@@ -255,6 +259,7 @@ class AssetGroupDetailView(SingleObjectMixin, AdminUserRequiredMixin, ListView):
kwargs.update(context)
return super(AssetGroupDetailView, self).get_context_data(**kwargs)
class AssetGroupUpdateView(AdminUserRequiredMixin, UpdateView):
model = AssetGroup
form_class = AssetGroupForm
......@@ -334,6 +339,7 @@ class IDCCreateView(AdminUserRequiredMixin, CreateView):
# IDC_add_success_next(user)
return super(IDCCreateView, self).form_valid(form)
class IDCUpdateView(AdminUserRequiredMixin, UpdateView):
model = IDC
form_class = IDCForm
......@@ -365,7 +371,6 @@ class IDCDeleteView(AdminUserRequiredMixin, DeleteView):
success_url = reverse_lazy('assets:idc-list')
class AdminUserListView(AdminUserRequiredMixin, ListView):
model = AdminUser
paginate_by = settings.CONFIG.DISPLAY_PER_PAGE
......
......@@ -2,11 +2,11 @@
#
from __future__ import absolute_import, unicode_literals
from rest_framework import generics
from rest_framework.views import APIView, Response
import serializers
from .models import ProxyLog, CommandLog
from . import models, serializers
from .hands import IsSuperUserOrTerminalUser, Terminal
......@@ -30,7 +30,7 @@ class ProxyLogListCreateApi(generics.ListCreateAPIView):
}
"""
queryset = ProxyLog.objects.all()
queryset = models.ProxyLog.objects.all()
serializer_class = serializers.ProxyLogSerializer
permission_classes = (IsSuperUserOrTerminalUser,)
......@@ -40,12 +40,23 @@ class ProxyLogListCreateApi(generics.ListCreateAPIView):
class ProxyLogDetailApi(generics.RetrieveUpdateDestroyAPIView):
queryset = ProxyLog.objects.all()
queryset = models.ProxyLog.objects.all()
serializer_class = serializers.ProxyLogSerializer
permission_classes = (IsSuperUserOrTerminalUser,)
class CommandLogCreateApi(generics.ListCreateAPIView):
queryset = CommandLog.objects.all()
class CommandLogListCreateApi(generics.ListCreateAPIView):
queryset = models.CommandLog.objects.all()
serializer_class = serializers.CommandLogSerializer
permission_classes = (IsSuperUserOrTerminalUser,)
# class CommandLogTitleApi(APIView):
# def get(self, request):
# response = [
# {"name": "command_no", "title": "ID", "type": "number"},
# {"name": "command", "title": "Title", "visible": True, "filterable": True},
# {"name": "datetime", "title": "Datetime", "type"},
# {"name": "output", "title": "Output", "filterable": True},
# ]
#
\ No newline at end of file
......@@ -2,6 +2,7 @@
#
from __future__ import unicode_literals
import base64
from django.db import models
from django.utils.translation import ugettext_lazy as _
......@@ -53,6 +54,17 @@ class ProxyLog(models.Model):
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]
class Meta:
db_table = 'proxy_log'
ordering = ['-date_start', 'username']
......@@ -68,6 +80,10 @@ class CommandLog(models.Model):
def __unicode__(self):
return '%s: %s' % (self.id, self.command)
@property
def output_decode(self):
return base64.b64decode(self.output).replace('\n', '<br />')
class Meta:
db_table = 'command_log'
ordering = ['command_no', 'command']
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
#
from __future__ import absolute_import, unicode_literals
from rest_framework import serializers
import models
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 = ['id', 'name', 'username', 'hostname', 'ip', 'system_user', 'login_type', 'terminal',
'log_file', 'was_failed', 'is_finished', 'date_start', 'time', 'command_length', "commands_dict"]
@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 len(obj.command_log.all())
class CommandLogSerializer(serializers.ModelSerializer):
class Meta:
model = models.CommandLog
{% extends '_base_list.html' %}
{% load i18n %}
{% load static %}
{% load common_tags %}
{% block content_left_head %}
{# <a href="{% url 'perms:asset-permission-create' %}" class="btn btn-sm btn-primary "> {% trans "Create permission" %} </a>#}
<link href="{% static "css/plugins/footable/footable.core.css" %}" rel="stylesheet">
{% 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>Datetime</th>
<th data-hide="all">Output</th>
</tr>
</thead>
<tbody>
{% for command in command_list %}
<tr>
<td>{{ command.command_no }}</td>
<td>{{ command.command }}</td>
<td>{{ command.proxy_log.username }}</td>
<td>{{ command.proxy_log.ip }}</td>
<td>{{ command.datetime }}</td>
<td>{{ command.output_decode |safe }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}
{% block custom_foot_js %}
<script src="{% static "js/plugins/footable/footable.all.min.js" %}"></script>
<script>
$(document).ready(function () {
$('.footable').footable();
});
</script>
{% endblock %}
{% 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>
{% extends 'base.html' %}
{% load common_tags %}
{% load static %}
{% load i18n %}
{% 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-7" 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>{{ 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>
<div class="col-sm-5" style="padding-left: 0;padding-right: 0">
<div class="ibox float-e-margins">
<div class="ibox-title">
<span style="float: left">{% trans 'Detail' %} <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 class="table2 table-stripped toggle-arrow-tiny" data-page-size="8">
</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 %}
{% extends '_base_list.html' %}
{% load i18n static %}
{% block custom_head_css_js %}
{{ block.super }}
<link href="{% static "css/plugins/footable/footable.core.css" %}" rel="stylesheet">
<link href="{% static "css/plugins/layer/layer.css" %}" rel="stylesheet">
<style>
div.dataTables_wrapper div.dataTables_filter,
.dataTables_length {
float: right !important;
}
div.dataTables_wrapper div.dataTables_filter {
margin-left: 15px;
}
</style>
{% endblock %}
{% block table_search %}{% endblock %}
{% block table_container %}
{#<div class="uc pull-left m-l-5 m-r-5"><a href="{% url "users:user-create" %}" class="btn btn-sm btn-primary"> {% trans "Create user" %} </a></div>#}
<table class="table table-striped table-bordered table-hover " id="proxy_log_list_table" >
<thead>
<tr>
<th class="text-center">
<div class="checkbox checkbox-default">
<input type="checkbox" class="ipt_check_all">
</div>
</th>
<th class="text-center">{% trans 'Username' %}</th>
<th class="text-center">{% trans 'IP' %}</th>
<th class="text-center">{% trans 'System user' %}</th>
{# <th class="text-center">{% trans 'Login type' %}</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 'Date start' %}</th>
<th class="text-center">{% trans 'Time' %}</th>
<th class="text-center">{% trans 'Action' %}</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
{% endblock %}
{% block custom_foot_js %}
<script src="{% static 'js/jquery.form.min.js' %}"></script>
<script src="{% static "js/plugins/layer/layer.js" %}"></script>
<script>
$(document).ready(function(){
var options = {
ele: $('#proxy_log_list_table'),
columnDefs: [
{targets: 1, createdCell: function (td, cellData, rowData) {
var detail_btn = '<a href="{% url "users:user-detail" pk=99991937 %}">' + cellData + '</a>';
$(td).html(detail_btn.replace('99991937', rowData.id));
}},
{targets: 4, createdCell: function (td, cellData, rowData) {
if (cellData) {
$(td).html('<a url="{% url "audits:proxy-log-commands-list" pk=99991938 %}" class="commands">99991937</a>'
.replace('99991937', cellData)
.replace('99991938',rowData.id))
}
}},
{targets: 5, createdCell: function (td, cellData) {
if (cellData) {
$(td).html('<i class="fa fa-times text-danger"></i>')
} else {
$(td).html('<i class="fa fa-check text-navy"></i>')
}
}},
{targets: 6, createdCell: function (td, cellData) {
if (!cellData) {
$(td).html('<i class="fa fa-times text-danger"></i>')
} else {
$(td).html('<i class="fa fa-check text-navy"></i>')
}
}},
{targets: 9, createdCell: function (td, cellData, rowData) {
var detail_btn = '<a href="{% url "audits:proxy-log-detail" pk=99991937 %}" class="btn btn-xs btn-info">{% trans "Detail" %}</a>'
.replace('99991937', cellData);
var delete_btn = '<a class="btn btn-xs btn-danger m-l-xs btn_delete" data-uid="99991937" data-name="99991938">{% trans "Delete" %}</a>'
.replace('99991937', cellData)
.replace('99991938', rowData.name);
$(td).html(detail_btn + delete_btn)
}}
],
ajax_url: '{% url "audits:proxy-log-list-create-api" %}',
columns: [{data: function(){return ""}}, {data: "name" }, {data: "ip"},
{data: "system_user"}, {data: "command_length"}, {data: 'was_failed'},
{data: "is_finished"}, {data: "date_start"}, {data: 'time'}, {data: 'id'}],
op_html: $('#actions').html()
};
jumpserver.initDataTable(options);
}).on('click', '.commands', function () {
var url = $(this).attr('url');
layer.open({
type: 2,
title: '很多时候,我们想最大化看,比如像这个页面。',
shadeClose: true,
shade: false,
maxmin: true, //开启最大化最小化按钮
area: ['893px', '600px'],
content: url
});
})
</script>
{% endblock %}
......@@ -7,11 +7,15 @@ import views
app_name = 'audits'
urlpatterns = [
url(r'^proxy-log$', views.ProxyLogListView.as_view(), name='proxy-log-list'),
url(r'^proxy-log/(?P<pk>\d+)$', views.ProxyLogDetailView.as_view(), name='proxy-log-detail'),
url(r'^proxy-log/(?P<pk>\d+)/commands$', views.ProxyLogCommandsListView.as_view(), name='proxy-log-commands-list'),
url(r'^command-log$', views.CommandLogListView.as_view(), name='command-log-list'),
]
urlpatterns += [
url(r'^v1/proxy-log/$', api.ProxyLogListCreateApi.as_view(), name='proxy-log-list-create-api'),
url(r'^v1/proxy-log/(?P<pk>\d+)/$', api.ProxyLogDetailApi.as_view(), name='proxy-log-detail-api'),
url(r'^v1/command-log/$', api.CommandLogCreateApi.as_view(), name='command-log-create-api'),
url(r'^v1/command-log/$', api.CommandLogListCreateApi.as_view(), name='command-log-create-list-api'),
]
# ~*~ coding: utf-8 ~*~
#
from users.utils import AdminUserRequiredMixin
from django.shortcuts import render
# ~*~ coding: utf-8 ~*~
#
# Create your views here.
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.urls import reverse_lazy
from django.conf import settings
from .models import ProxyLog, CommandLog
from .utils import AdminUserRequiredMixin
class ProxyLogListView(TemplateView):
template_name = 'audits/proxy_log_list.html'
def get_context_data(self, **kwargs):
context = super(ProxyLogListView, self).get_context_data(**kwargs)
context.update({'app': _('Audits'), 'action': _('Proxy log list')})
return context
class ProxyLogDetailView(AdminUserRequiredMixin, SingleObjectMixin, ListView):
template_name = 'audits/proxy_log_detail.html'
context_object_name = 'proxy_log'
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(self.object.command_log.all())
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'
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):
model = CommandLog
template_name = 'audits/command_log_list.html'
paginate_by = settings.CONFIG.DISPLAY_PER_PAGE
context_object_name = 'command_list'
def get_queryset(self):
# Todo: Default order by lose asset connection num
self.queryset = super(CommandLogListView, self).get_queryset()
self.keyword = keyword = self.request.GET.get('keyword', '')
self.sort = sort = self.request.GET.get('sort', '-datetime')
if keyword:
self.queryset = self.queryset.filter()
if sort:
self.queryset = self.queryset.order_by(sort)
return self.queryset
def get_context_data(self, **kwargs):
context = {
'app': 'Audits',
'action': 'Command log list'
}
kwargs.update(context)
return super(CommandLogListView, self).get_context_data(**kwargs)
......@@ -6,6 +6,7 @@ from six import string_types
from itertools import chain
import string
import logging
import datetime
from itsdangerous import Signer, TimedJSONWebSignatureSerializer, JSONWebSignatureSerializer, TimestampSigner, \
BadSignature, SignatureExpired
......@@ -133,3 +134,34 @@ def int_seq(seq):
return map(int, seq)
except ValueError:
return seq
def timesince(dt, since='', default="just now"):
"""
Returns string representing "time since" e.g.
3 days, 5 hours.
"""
if since is '':
since = datetime.datetime.utcnow()
if since is None:
return default
diff = since - dt
periods = (
(diff.days / 365, "year", "years"),
(diff.days / 30, "month", "months"),
(diff.days / 7, "week", "weeks"),
(diff.days, "day", "days"),
(diff.seconds / 3600, "hour", "hours"),
(diff.seconds / 60, "minute", "minutes"),
(diff.seconds, "second", "seconds"),
)
for period, singular, plural in periods:
if period:
return "%d %s" % (period, singular if period == 1 else plural)
return default
......@@ -24,7 +24,7 @@ urlpatterns = [
url(r'^$', TemplateView.as_view(template_name='base.html'), name='index'),
url(r'^(api/)?users/', include('users.urls')),
url(r'^assets/', include('assets.urls')),
url(r'^perms/', include('perms.urls')),
url(r'^(api/)?perms/', include('perms.urls')),
url(r'^(api/)?audits/', include('audits.urls')),
url(r'^(api/)?terminal/', include('terminal.urls')),
]
......
# ~*~ coding: utf-8 ~*~
#
from rest_framework.views import APIView, Response
from users.backends import IsValidUser
from .utils import get_user_granted_assets, get_user_granted_asset_groups
class UserAssetsGrantedApi(APIView):
permission_classes = (IsValidUser,)
def get(self, request, *args, **kwargs):
assets_json = []
user = request.user
if user:
assets = get_user_granted_assets(user)
for asset, system_users in assets.items():
assets_json.append({
'id': asset.id,
'hostname': asset.hostname,
'ip': asset.ip,
'port': asset.port,
'system_users': [
{
'id': system_user.id,
'name': system_user.name,
'username': system_user.username,
} for system_user in system_users
],
'comment': asset.comment
})
return Response(assets_json, status=200)
# ~*~ coding: utf-8 ~*~
#
from django.db import models
from django.utils.translation import ugettext_lazy as _
from users.utils import AdminUserRequiredMixin
# from users.backends import IsValdiUser
from users.models import User, UserGroup
from assets.models import Asset, AssetGroup, SystemUser
# -*- coding: utf-8 -*-
#
......@@ -2,6 +2,7 @@
from django.conf.urls import url
import views
import api
app_name = 'perms'
......@@ -20,3 +21,8 @@ urlpatterns = [
name='asset-permission-asset-list'),
]
urlpatterns += [
url(r'^v1/user/assets/granted/$', api.UserAssetsGrantedApi.as_view(),
name='user-assets-granted'),
]
This diff is collapsed.
@font-face {
font-family: 'footable';
src: url('fonts/footable.eot');
src: url('fonts/footable.eot?#iefix') format('embedded-opentype'), url('fonts/footable.woff') format('woff'), url('fonts/footable.ttf') format('truetype'), url('fonts/footable.svg#footable') format('svg');
font-weight: normal;
font-style: normal;
}
@media screen and (-webkit-min-device-pixel-ratio: 0) {
@font-face {
font-family: 'footable';
src: url('fonts/footable.svg#footable') format('svg');
font-weight: normal;
font-style: normal;
}
}
.footable {
width: 100%;
/** SORTING **/
/** PAGINATION **/
}
.footable.breakpoint > tbody > tr.footable-detail-show > td {
border-bottom: none;
}
.footable.breakpoint > tbody > tr.footable-detail-show > td > span.footable-toggle:before {
content: "\e001";
}
.footable.breakpoint > tbody > tr:hover:not(.footable-row-detail) {
cursor: pointer;
}
.footable.breakpoint > tbody > tr > td.footable-cell-detail {
background: #eee;
border-top: none;
}
.footable.breakpoint > tbody > tr > td > span.footable-toggle {
display: inline-block;
font-family: 'footable';
speak: none;
font-style: normal;
font-weight: normal;
font-variant: normal;
text-transform: none;
-webkit-font-smoothing: antialiased;
padding-right: 5px;
font-size: 14px;
color: #888888;
}
.footable.breakpoint > tbody > tr > td > span.footable-toggle:before {
content: "\e000";
}
.footable.breakpoint.toggle-circle > tbody > tr.footable-detail-show > td > span.footable-toggle:before {
content: "\e005";
}
.footable.breakpoint.toggle-circle > tbody > tr > td > span.footable-toggle:before {
content: "\e004";
}
.footable.breakpoint.toggle-circle-filled > tbody > tr.footable-detail-show > td > span.footable-toggle:before {
content: "\e003";
}
.footable.breakpoint.toggle-circle-filled > tbody > tr > td > span.footable-toggle:before {
content: "\e002";
}
.footable.breakpoint.toggle-square > tbody > tr.footable-detail-show > td > span.footable-toggle:before {
content: "\e007";
}
.footable.breakpoint.toggle-square > tbody > tr > td > span.footable-toggle:before {
content: "\e006";
}
.footable.breakpoint.toggle-square-filled > tbody > tr.footable-detail-show > td > span.footable-toggle:before {
content: "\e009";
}
.footable.breakpoint.toggle-square-filled > tbody > tr > td > span.footable-toggle:before {
content: "\e008";
}
.footable.breakpoint.toggle-arrow > tbody > tr.footable-detail-show > td > span.footable-toggle:before {
content: "\e00f";
}
.footable.breakpoint.toggle-arrow > tbody > tr > td > span.footable-toggle:before {
content: "\e011";
}
.footable.breakpoint.toggle-arrow-small > tbody > tr.footable-detail-show > td > span.footable-toggle:before {
content: "\e013";
}
.footable.breakpoint.toggle-arrow-small > tbody > tr > td > span.footable-toggle:before {
content: "\e015";
}
.footable.breakpoint.toggle-arrow-circle > tbody > tr.footable-detail-show > td > span.footable-toggle:before {
content: "\e01b";
}
.footable.breakpoint.toggle-arrow-circle > tbody > tr > td > span.footable-toggle:before {
content: "\e01d";
}
.footable.breakpoint.toggle-arrow-circle-filled > tbody > tr.footable-detail-show > td > span.footable-toggle:before {
content: "\e00b";
}
.footable.breakpoint.toggle-arrow-circle-filled > tbody > tr > td > span.footable-toggle:before {
content: "\e00d";
}
.footable.breakpoint.toggle-arrow-tiny > tbody > tr.footable-detail-show > td > span.footable-toggle:before {
content: "\e01f";
}
.footable.breakpoint.toggle-arrow-tiny > tbody > tr > td > span.footable-toggle:before {
content: "\e021";
}
.footable.breakpoint.toggle-arrow-alt > tbody > tr.footable-detail-show > td > span.footable-toggle:before {
content: "\e017";
}
.footable.breakpoint.toggle-arrow-alt > tbody > tr > td > span.footable-toggle:before {
content: "\e019";
}
.footable.breakpoint.toggle-medium > tbody > tr > td > span.footable-toggle {
font-size: 18px;
}
.footable.breakpoint.toggle-large > tbody > tr > td > span.footable-toggle {
font-size: 24px;
}
.footable > thead > tr > th {
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: -moz-none;
-ms-user-select: none;
user-select: none;
}
.footable > thead > tr > th.footable-sortable:hover {
cursor: pointer;
}
.footable > thead > tr > th.footable-sorted > span.footable-sort-indicator:before {
content: "\e013";
}
.footable > thead > tr > th.footable-sorted-desc > span.footable-sort-indicator:before {
content: "\e012";
}
.footable > thead > tr > th > span.footable-sort-indicator {
display: inline-block;
font-family: 'footable';
speak: none;
font-style: normal;
font-weight: normal;
font-variant: normal;
text-transform: none;
-webkit-font-smoothing: antialiased;
padding-left: 5px;
}
.footable > thead > tr > th > span.footable-sort-indicator:before {
content: "\e022";
}
.footable > tfoot .pagination {
margin: 0;
}
.footable.no-paging .hide-if-no-paging {
display: none;
}
.footable-row-detail-inner {
display: table;
}
.footable-row-detail-row {
display: table-row;
line-height: 1.5em;
}
.footable-row-detail-group {
display: block;
line-height: 2em;
font-size: 1.2em;
font-weight: bold;
}
.footable-row-detail-name {
display: table-cell;
font-weight: bold;
padding-right: 0.5em;
}
.footable-row-detail-value {
display: table-cell;
}
.footable-odd {
background-color: #f7f7f7;
}
This diff is collapsed.
......@@ -197,7 +197,7 @@ function APIUpdateAttr(props) {
type: props.method || "PATCH",
data: props.body,
contentType: props.content_type || "application/json; charset=utf-8",
dataType: props.data_type || "json",
dataType: props.data_type || "json"
}).done(function(data, textStatue, jqXHR) {
if (typeof props.success === 'function') {
return props.success(data);
......@@ -215,28 +215,37 @@ function APIUpdateAttr(props) {
}
// Sweet Alert for Delete
function objectDelete(obj, name, url){
function objectDelete(obj, name, url) {
var $this = $(this);
function doDelete() {
var uid = $this.data('uid');
var body = {};
var success = function() {
swal('Deleted!', "[ "+name+"]"+" has been deleted ", "success");
$(obj).parent().parent().remove();
};
var fail = function() {
swal("Failed", "Delete"+"[ "+name+" ]"+"failed", "error");
};
APIUpdateAttr({
url: url,
body: JSON.stringify(body),
method: 'DELETE',
success: success,
error: fail
});
}
swal({
title: 'Are you sure delete ?',
text: "【" + name + "】",
text: " [" + name + "] ",
type: "warning",
showCancelButton: true,
cancelButtonText: 'Cancel',
confirmButtonColor: "#DD6B55",
confirmButtonText: 'Yes, delete it!',
confirmButtonText: 'Confirm',
closeOnConfirm: false
}, function () {
$.ajax({
type : "post",
url : url,
data : {
},
dataType : "text",
success : function(data) {
swal('Deleted!' , "【"+name+"】"+"has been deleted.", "success");
$(obj).parent().parent().remove();
}
});
doDelete()
});
}
......@@ -279,7 +288,7 @@ jumpserver.initDataTable = function (options) {
$(td).html('<div class="checkbox checkbox-default"><input type="checkbox" class="ipt_check"><label></label></div>');
}
},
{className: 'text-center', targets: '_all'},
{className: 'text-center', targets: '_all'}
];
columnDefs = options.columnDefs ? options.columnDefs.concat(columnDefs) : columnDefs;
var table = ele.DataTable({
......@@ -342,6 +351,6 @@ jumpserver.initDataTable = function (options) {
jumpserver.checked = false;
table.rows().deselect();
}
})
});
return table;
}
};
This diff is collapsed.
This diff is collapsed.
/*! layer弹层组件拓展类 */
;!function(){layer.use("skin/layer.ext.css",function(){layer.layui_layer_extendlayerextjs=!0});var a=layer.cache||{},b=function(b){return a.skin?" "+a.skin+" "+a.skin+"-"+b:""};layer.prompt=function(a,c){a=a||{},"function"==typeof a&&(c=a);var d,e=2==a.formType?'<textarea class="layui-layer-input">'+(a.value||"")+"</textarea>":function(){return'<input type="'+(1==a.formType?"password":"text")+'" class="layui-layer-input" value="'+(a.value||"")+'">'}();return layer.open($.extend({btn:["&#x786E;&#x5B9A;","&#x53D6;&#x6D88;"],content:e,skin:"layui-layer-prompt"+b("prompt"),success:function(a){d=a.find(".layui-layer-input"),d.focus()},yes:function(b){var e=d.val();""===e?d.focus():e.length>(a.maxlength||500)?layer.tips("&#x6700;&#x591A;&#x8F93;&#x5165;"+(a.maxlength||500)+"&#x4E2A;&#x5B57;&#x6570;",d,{tips:1}):c&&c(e,b,d)}},a))},layer.tab=function(a){a=a||{};var c=a.tab||{};return layer.open($.extend({type:1,skin:"layui-layer-tab"+b("tab"),title:function(){var a=c.length,b=1,d="";if(a>0)for(d='<span class="layui-layer-tabnow">'+c[0].title+"</span>";a>b;b++)d+="<span>"+c[b].title+"</span>";return d}(),content:'<ul class="layui-layer-tabmain">'+function(){var a=c.length,b=1,d="";if(a>0)for(d='<li class="layui-layer-tabli xubox_tab_layer">'+(c[0].content||"no content")+"</li>";a>b;b++)d+='<li class="layui-layer-tabli">'+(c[b].content||"no content")+"</li>";return d}()+"</ul>",success:function(a){var b=a.find(".layui-layer-title").children(),c=a.find(".layui-layer-tabmain").children();b.on("mousedown",function(a){a.stopPropagation?a.stopPropagation():a.cancelBubble=!0;var b=$(this),d=b.index();b.addClass("layui-layer-tabnow").siblings().removeClass("layui-layer-tabnow"),c.eq(d).show().siblings().hide()})}},a))},layer.photos=function(a,c,d){function e(a,b,c){var d=new Image;d.onload=function(){d.onload=null,b(d)},d.onerror=function(a){d.onerror=null,c(a)},d.src=a}var f={};if(a=a||{},a.photos){var g=a.photos.constructor===Object,h=g?a.photos:{},i=h.data||[],j=h.start||0;if(f.imgIndex=j+1,g){if(0===i.length)return void layer.msg("&#x6CA1;&#x6709;&#x56FE;&#x7247;")}else{var k=$(a.photos),l=k.find(a.img||"img");if(0===l.length)return;if(c||k.find(h.img||"img").each(function(b){var c=$(this);i.push({alt:c.attr("alt"),pid:c.attr("layer-pid"),src:c.attr("layer-src")||c.attr("src"),thumb:c.attr("src")}),c.on("click",function(){layer.photos($.extend(a,{photos:{start:b,data:i,tab:a.tab},full:a.full}),!0)})}),!c)return}f.imgprev=function(a){f.imgIndex--,f.imgIndex<1&&(f.imgIndex=i.length),f.tabimg(a)},f.imgnext=function(a,b){f.imgIndex++,f.imgIndex>i.length&&(f.imgIndex=1,b)||f.tabimg(a)},f.keyup=function(a){if(!f.end){var b=a.keyCode;a.preventDefault(),37===b?f.imgprev(!0):39===b?f.imgnext(!0):27===b&&layer.close(f.index)}},f.tabimg=function(b){i.length<=1||(h.start=f.imgIndex-1,layer.close(f.index),layer.photos(a,!0,b))},f.event=function(){f.bigimg.hover(function(){f.imgsee.show()},function(){f.imgsee.hide()}),f.bigimg.find(".layui-layer-imgprev").on("click",function(a){a.preventDefault(),f.imgprev()}),f.bigimg.find(".layui-layer-imgnext").on("click",function(a){a.preventDefault(),f.imgnext()}),$(document).on("keyup",f.keyup)},f.loadi=layer.load(1,{shade:"shade"in a?!1:.9,scrollbar:!1}),e(i[j].src,function(c){layer.close(f.loadi),f.index=layer.open($.extend({type:1,area:function(){var b=[c.width,c.height],d=[$(window).width()-100,$(window).height()-100];return!a.full&&b[0]>d[0]&&(b[0]=d[0],b[1]=b[0]*d[1]/b[0]),[b[0]+"px",b[1]+"px"]}(),title:!1,shade:.9,shadeClose:!0,closeBtn:!1,move:".layui-layer-phimg img",moveType:1,scrollbar:!1,moveOut:!0,shift:5*Math.random()|0,skin:"layui-layer-photos"+b("photos"),content:'<div class="layui-layer-phimg"><img src="'+i[j].src+'" alt="'+(i[j].alt||"")+'" layer-pid="'+i[j].pid+'"><div class="layui-layer-imgsee">'+(i.length>1?'<span class="layui-layer-imguide"><a href="javascript:;" class="layui-layer-iconext layui-layer-imgprev"></a><a href="javascript:;" class="layui-layer-iconext layui-layer-imgnext"></a></span>':"")+'<div class="layui-layer-imgbar" style="display:'+(d?"block":"")+'"><span class="layui-layer-imgtit"><a href="javascript:;">'+(i[j].alt||"")+"</a><em>"+f.imgIndex+"/"+i.length+"</em></span></div></div></div>",success:function(b,c){f.bigimg=b.find(".layui-layer-phimg"),f.imgsee=b.find(".layui-layer-imguide,.layui-layer-imgbar"),f.event(b),a.tab&&a.tab(i[j],b)},end:function(){f.end=!0,$(document).off("keyup",f.keyup)}},a))},function(){layer.close(f.loadi),layer.msg("&#x5F53;&#x524D;&#x56FE;&#x7247;&#x5730;&#x5740;&#x5F02;&#x5E38;<br>&#x662F;&#x5426;&#x7EE7;&#x7EED;&#x67E5;&#x770B;&#x4E0B;&#x4E00;&#x5F20;&#xFF1F;",{time:3e4,btn:["下一张","不看了"],yes:function(){i.length>1&&f.imgnext(!0,!0)}})})}}}();
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
/*!
@Name: layer拓展样式
@Date: 2012.12.13
@Author: 贤心
@blog: sentsin.com
*/.layui-layer-imgbar,.layui-layer-imgtit a,.layui-layer-tab .layui-layer-title span{text-overflow:ellipsis;white-space:nowrap}.layui-layer-iconext{background:url(default/icon-ext.png) no-repeat}html #layui_layer_skinlayerextcss{display:none;position:absolute;width:1989px}.layui-layer-prompt .layui-layer-input{display:block;width:220px;height:30px;margin:0 auto;line-height:30px;padding:0 5px;border:1px solid #ccc;box-shadow:1px 1px 5px rgba(0,0,0,.1) inset;color:#333}.layui-layer-prompt textarea.layui-layer-input{width:300px;height:100px;line-height:20px}.layui-layer-tab{box-shadow:1px 1px 50px rgba(0,0,0,.4)}.layui-layer-tab .layui-layer-title{padding-left:0;border-bottom:1px solid #ccc;background-color:#eee;overflow:visible}.layui-layer-tab .layui-layer-title span{position:relative;float:left;min-width:80px;max-width:260px;padding:0 20px;text-align:center;cursor:default;overflow:hidden}.layui-layer-tab .layui-layer-title span.layui-layer-tabnow{height:43px;border-left:1px solid #ccc;border-right:1px solid #ccc;background-color:#fff;z-index:10}.layui-layer-tab .layui-layer-title span:first-child{border-left:none}.layui-layer-tabmain{line-height:24px;clear:both}.layui-layer-tabmain .layui-layer-tabli{display:none}.layui-layer-tabmain .layui-layer-tabli.xubox_tab_layer{display:block}.xubox_tabclose{position:absolute;right:10px;top:5px;cursor:pointer}.layui-layer-photos{-webkit-animation-duration:1s;animation-duration:1s;background:url(default/xubox_loading1.gif) center center no-repeat #000}.layui-layer-photos .layui-layer-content{overflow:hidden;text-align:center}.layui-layer-photos .layui-layer-phimg img{position:relative;width:100%;display:inline-block;*display:inline;*zoom:1;vertical-align:top}.layui-layer-imgbar,.layui-layer-imguide{display:none}.layui-layer-imgnext,.layui-layer-imgprev{position:absolute;top:50%;width:27px;_width:44px;height:44px;margin-top:-22px;outline:0;blr:expression(this.onFocus=this.blur())}.layui-layer-imgprev{left:10px;background-position:-5px -5px;_background-position:-70px -5px}.layui-layer-imgprev:hover{background-position:-33px -5px;_background-position:-120px -5px}.layui-layer-imgnext{right:10px;_right:8px;background-position:-5px -50px;_background-position:-70px -50px}.layui-layer-imgnext:hover{background-position:-33px -50px;_background-position:-120px -50px}.layui-layer-imgbar{position:absolute;left:0;bottom:0;width:100%;height:32px;line-height:32px;background-color:rgba(0,0,0,.8);background-color:#000\9;filter:Alpha(opacity=80);color:#fff;overflow:hidden;font-size:0}.layui-layer-imgtit *{display:inline-block;*display:inline;*zoom:1;vertical-align:top;font-size:12px}.layui-layer-imgtit a{max-width:65%;overflow:hidden;color:#fff}.layui-layer-imgtit a:hover{color:#fff;text-decoration:underline}.layui-layer-imgtit em{padding-left:10px;font-style:normal}
\ No newline at end of file
......@@ -39,10 +39,23 @@
<i class="fa fa-desktop"></i><span class="nav-label">{% trans 'Terminal' %}</span><span class="label label-info pull-right"></span>
</a>
</li>
<li id="">
<a href="">
<i class="fa fa-files-o"></i><span class="nav-label">{% trans 'Audits' %}</span><span class="label label-info pull-right"></span>
</a>
<li id="audits">
<a href="#"><i class="fa fa-files-o"></i> <span class="nav-label">{% trans 'Audits' %}</span><span class="fa arrow"></span></a>
<ul class="nav nav-second-level">
<li id="proxy-log">
<a href="{% url 'audits:proxy-log-list' %}">{% trans 'Proxy log' %}</a>
</li>
<li id="command-log">
<a href="{% url 'audits:command-log-list' %}">{% trans 'Command log' %}</a>
</li>
<li id="login">
<a href="{% url 'perms:asset-permission-list' %}">{% trans 'Login log' %}</a>
</li>
<li id="admin">
<a href="{% url 'perms:asset-permission-list' %}">{% trans 'Admin log' %}</a>
</li>
</ul>
</li>
<li id="">
<a href="#">
......
# -*- coding: utf-8 -*-
#
from rest_framework.generics import ListCreateAPIView, CreateAPIView
from rest_framework.generics import ListCreateAPIView, RetrieveUpdateDestroyAPIView
from rest_framework.views import APIView, Response
from rest_framework.permissions import AllowAny
......@@ -11,7 +11,7 @@ from .serializers import TerminalSerializer, TerminalHeatbeatSerializer
from .hands import IsSuperUserOrTerminalUser
class TerminalApi(ListCreateAPIView):
class TerminalCreateListApi(ListCreateAPIView):
queryset = Terminal.objects.all()
serializer_class = TerminalSerializer
permission_classes = (AllowAny,)
......@@ -21,26 +21,32 @@ class TerminalApi(ListCreateAPIView):
if name:
terminal = get_object_or_none(Terminal, name=name)
if terminal:
if terminal.is_accepted and terminal.is_active:
if terminal.is_active:
return Response(data={'data': {'name': name, 'id': terminal.id},
'msg': 'Success'},
status=200)
else:
return Response(data={'data': {'name': name, 'ip': terminal.ip},
'msg': 'Need admin accept or active it'},
'msg': 'Need admin active it'},
status=203)
else:
ip = request.META.get('X-Real-IP') or request.META.get('REMOTE_ADDR')
terminal = Terminal.objects.create(name=name, ip=ip)
return Response(data={'data': {'name': name, 'ip': terminal.ip},
'msg': 'Need admin accept or active it'},
status=204)
'msg': 'Need admin active it'},
status=201)
else:
return Response(data={'msg': 'Secrete key invalid'}, status=401)
class TerminalHeatbeatApi(CreateAPIView):
class TerminalHeatbeatApi(ListCreateAPIView):
model = TerminalHeatbeat
serializer_class = TerminalHeatbeatSerializer
permission_classes = (IsSuperUserOrTerminalUser,)
class TerminalApiDetailUpdateDetailApi(RetrieveUpdateDestroyAPIView):
queryset = Terminal.objects.all()
serializer_class = TerminalSerializer
permission_classes = (IsSuperUserOrTerminalUser,)
# ~*~ coding: utf-8 ~*~
#
from django import forms
from django.utils.translation import ungettext_lazy as _
from .models import Terminal
class TerminalForm(forms.ModelForm):
class Meta:
model = Terminal
fields = ['name', 'ip', 'is_active', 'type', 'url', 'comment']
help_texts = {
'url': 'Example: ssh://192.168.1.1:22 or http://jms.jumpserver.org, that user login'
}
......@@ -2,4 +2,5 @@
#
from users.backends import IsSuperUserOrTerminalUser
from audits.models import ProxyLog
......@@ -15,13 +15,10 @@ class Terminal(models.Model):
ip = models.GenericIPAddressField(verbose_name=_('From ip'))
is_active = models.BooleanField(default=False, verbose_name=_('Is active'))
is_bound_ip = models.BooleanField(default=False, verbose_name=_('Is bound ip'))
heatbeat_interval = models.IntegerField(default=60, verbose_name=_('Heatbeat interval'))
type = models.CharField(choices=TYPE_CHOICES, max_length=2, verbose_name=_('Terminal type'))
url = models.CharField(max_length=100, verbose_name=_('URL to login'))
mail_to = models.ManyToManyField(User, verbose_name=_('Mail to'))
is_accepted = models.BooleanField(default=False, verbose_name=_('Is accepted'))
date_created = models.DateTimeField(auto_now_add=True)
comment = models.TextField(verbose_name=_('Comment'))
comment = models.TextField(blank=True, verbose_name=_('Comment'))
def is_valid(self):
return self.is_active and self.is_accepted
......@@ -36,7 +33,7 @@ class Terminal(models.Model):
class Meta:
db_table = 'terminal'
ordering = ['name']
ordering = ['is_active']
class TerminalHeatbeat(models.Model):
......
......@@ -4,13 +4,20 @@
from rest_framework import serializers
from .models import Terminal, TerminalHeatbeat
from .hands import ProxyLog
class TerminalSerializer(serializers.ModelSerializer):
proxy_amount = serializers.SerializerMethodField()
class Meta:
model = Terminal
fields = ['name', 'ip', 'type', 'url', 'comment', 'is_active', 'is_accepted',
'get_type_display']
fields = ['id', 'name', 'ip', 'type', 'url', 'comment', 'is_active',
'get_type_display', 'proxy_amount']
@staticmethod
def get_proxy_amount(obj):
return ProxyLog.objects.filter(terminal=obj.name, is_finished=False).count()
class TerminalHeatbeatSerializer(serializers.ModelSerializer):
......
......@@ -27,7 +27,7 @@
<th class="text-center">{% trans 'Name' %}</th>
<th class="text-center">{% trans 'IP' %}</th>
<th class="text-center">{% trans 'Type' %}</th>
<th class="text-center">{% trans 'url' %}</th>
<th class="text-center">{% trans 'proxy_amount' %}</th>
<th class="text-center">{% trans 'Active' %}</th>
<th class="text-center">{% trans 'Action' %}</th>
</tr>
......@@ -36,44 +36,46 @@
</tbody>
</table>
{% endblock %}
{% block content_bottom_left %}{% endblock %}
{% block custom_foot_js %}
<script src="{% static 'js/jquery.form.min.js' %}"></script>
<script>
$(document).ready(function(){
var options = {
ele: $('#terminal_list_table'),
{# columnDefs: [#}
{# {targets: 1, createdCell: function (td, cellData, rowData) {#}
{# var detail_btn = '<a href="{% url "users:user-detail" pk=99991937 %}">' + cellData + '</a>';#}
{# $(td).html(detail_btn.replace('99991937', rowData.id));#}
{# }}#}
{# {targets: 4, createdCell: function (td, cellData) {#}
{# var innerHtml = cellData.length > 8 ? cellData.substring(0, 8) + '...': cellData;#}
{# $(td).html('<a href="javascript:void(0);" data-toggle="tooltip" title="' + cellData + '">' + innerHtml + '</a>');#}
{# }},#}
{# {targets: 6, createdCell: function (td, cellData) {#}
{# if (!cellData) {#}
{# $(td).html('<i class="fa fa-times text-danger"></i>')#}
{# } else {#}
{# $(td).html('<i class="fa fa-check text-navy"></i>')#}
{# }#}
{# }},#}
{# {targets: 7, createdCell: function (td, cellData, rowData) {#}
{# var update_btn = '<a href="{% url "users:user-update" pk=99991937 %}" class="btn btn-xs btn-info">{% trans "Update" %}</a>'.replace('99991937', cellData);#}
{# var del_btn = '<a class="btn btn-xs btn-danger m-l-xs btn_user_delete" data-uid="99991937">{% trans "Delete" %}</a>'.replace('99991937', cellData);#}
{# if (rowData.id === 1) {#}
{# $(td).html(update_btn)#}
{# } else {#}
{# $(td).html(update_btn + del_btn)#}
{# }}],#}
{# ],#}
columnDefs: [
{targets: 1, createdCell: function (td, cellData, rowData) {
var detail_btn = '<a href="{% url "users:user-detail" pk=99991937 %}">' + cellData + '</a>';
$(td).html(detail_btn.replace('99991937', rowData.id));
}},
{targets: 5, createdCell: function (td, cellData) {
if (!cellData) {
$(td).html('<i class="fa fa-times text-danger"></i>')
} else {
$(td).html('<i class="fa fa-check text-navy"></i>')
}
}},
{targets: 6, createdCell: function (td, cellData, rowData) {
console.log(rowData.name);
var update_btn = '<a href="{% url "terminal:terminal-update" pk=99991937 %}" class="btn btn-xs btn-info">{% trans "Update" %}</a>'
.replace('99991937', cellData);
var delete_btn = '<a class="btn btn-xs btn-danger m-l-xs btn_delete" data-uid="99991937" data-name="99991938">{% trans "Delete" %}</a>'
.replace('99991937', cellData)
.replace('99991938', rowData.name);
$(td).html(update_btn + delete_btn)
}}
],
ajax_url: '{% url "terminal:terminal-list-create-api" %}',
columns: [{data: function(){return ""}}, {data: "name" }, {data: "ip" }, {data: "get_type_display" }, {data: "url" },
{data: "is_active" }, {data: "ip"}],
columns: [{data: function(){return ""}}, {data: "name" }, {data: "ip" }, {data: "get_type_display" },
{data: "proxy_amount"}, {data: "is_active" }, {data: "id"}],
op_html: $('#actions').html()
};
jumpserver.initDataTable(options);
}).on('click', '.btn_delete', function(){
var $this = $(this);
var uid = $this.data('uid');
var name = $(this).data('name');
var the_url = '{% url "terminal:terminal-detail-update-delete-api" pk=99991937 %}'.replace('99991937', uid);
objectDelete($this, name, the_url)
})
</script>
{% endblock %}
{% extends 'base.html' %}
{% load i18n %}
{% load static %}
{% load bootstrap %}
{% block custom_head_css_js %}
<link href="{% static "css/plugins/select2/select2.min.css" %}" rel="stylesheet">
<script src="{% static "js/plugins/select2/select2.full.min.js" %}"></script>
<link href="{% static "css/plugins/datepicker/datepicker3.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="ibox-title">
<h5>{{ action }}</h5>
<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>
<a class="close-link">
<i class="fa fa-times"></i>
</a>
</div>
</div>
<div class="ibox-content">
<form method="post" class="form-horizontal" action="" enctype="multipart/form-data">
{% csrf_token %}
<h3>{% trans 'Info' %}</h3>
{{ form.name|bootstrap_horizontal }}
{{ form.ip|bootstrap_horizontal }}
{{ form.type|bootstrap_horizontal }}
{{ form.url|bootstrap_horizontal }}
<div class="hr-line-dashed"></div>
<h3>{% trans 'Other' %}</h3>
<div class="form-group">
<label for="{{ form.is_active.id_for_label }}" class="col-sm-2 control-label">{% trans 'Active' %}</label>
<div class="col-sm-8">
{{ form.is_active}}
</div>
</div>
{{ form.comment|bootstrap_horizontal }}
<div class="hr-line-dashed"></div>
<div class="form-group">
<div class="col-sm-4 col-sm-offset-2">
<button class="btn btn-white" type="reset">{% trans 'Reset' %}</button>
<button id="submit_button" class="btn btn-primary" type="submit">{% trans 'Submit' %}</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block custom_foot_js %}
<script src="{% static 'js/plugins/datapicker/bootstrap-datepicker.js' %}"></script>
<script>
$(document).ready(function () {
$('.select2').select2();
$('.input-group.date').datepicker({
format: "yyyy-mm-dd",
todayBtn: "linked",
keyboardNavigation: false,
forceParse: false,
calendarWeeks: true,
autoclose: true
});
})
</script>
{% endblock %}
......@@ -11,9 +11,12 @@ app_name = 'terminal'
urlpatterns = [
url(r'^terminal$', views.TerminalListView.as_view(), name='terminal-list'),
url(r'^terminal/(?P<pk>\d+)/update$', views.TerminalUpdateView.as_view(), name='terminal-update'),
]
urlpatterns += [
url(r'^v1/terminal/$', api.TerminalApi.as_view(), name='terminal-list-create-api'),
url(r'^v1/terminal/$', api.TerminalCreateListApi.as_view(), name='terminal-list-create-api'),
url(r'^v1/terminal/(?P<pk>\d+)/$', api.TerminalApiDetailUpdateDetailApi.as_view(),
name='terminal-detail-update-delete-api'),
url(r'^v1/terminal-heatbeat/$', api.TerminalHeatbeatApi.as_view(), name='terminal-heatbeat-api'),
]
# ~*~ coding: utf-8 ~*~
#
from django.views.generic import ListView
from django.views.generic import ListView, UpdateView, DeleteView
from django.utils.translation import ugettext as _
from django.urls import reverse_lazy
from .models import Terminal
from .forms import TerminalForm
class TerminalListView(ListView):
......@@ -15,3 +17,21 @@ class TerminalListView(ListView):
context = super(TerminalListView, self).get_context_data(**kwargs)
context.update({'app': _('Terminal'), 'action': _('Terminal list')})
return context
class TerminalUpdateView(UpdateView):
model = Terminal
form_class = TerminalForm
template_name = 'terminal/terminal_update.html'
success_url = reverse_lazy('terminal:terminal-list')
def get_context_data(self, **kwargs):
context = super(TerminalUpdateView, self).get_context_data(**kwargs)
context.update({'app': _('Terminal'), 'action': _('Update terminal')})
return context
class TerminalDeleteView(DeleteView):
model = Terminal
template_name = 'assets/delete_confirm.html'
success_url = reverse_lazy('terminal:terminal-list')
\ No newline at end of file
......@@ -6,6 +6,7 @@ from rest_framework.compat import is_authenticated
from django.utils.translation import ugettext as _
from common.utils import unsign, get_object_or_none
from .hands import Terminal
......@@ -40,7 +41,7 @@ class TerminalAuthentication(authentication.BaseAuthentication):
else:
raise exceptions.AuthenticationFailed(_('Invalid sign.'))
if not terminal.is_active:
if not terminal or not terminal.is_active:
raise exceptions.AuthenticationFailed(_('Terminal inactive or deleted.'))
terminal.is_authenticated = True
return terminal, None
......
......@@ -10,6 +10,6 @@
:license: GPL v2, see LICENSE for more details.
"""
from terminal.models import Terminal
from perms.models import AssetPermission
from perms.utils import get_user_granted_assets, get_user_granted_asset_groups
from terminal.models import Terminal
\ No newline at end of file
......@@ -10,12 +10,12 @@
<th class="text-center">
<div class="checkbox checkbox-default"><input id="" type="checkbox" class="ipt_check_all"><label></label></div>
</th>
<th class="text-center">{% trans 'Name' %}</a></th>
<th class="text-center">{% trans 'Username' %}</a></th>
<th class="text-center">{% trans 'Name' %}</th>
<th class="text-center">{% trans 'Username' %}</th>
<th class="text-center">{% trans 'Role' %}</th>
<th class="text-center">{% trans 'User group' %}</th>
<th class="text-center">{% trans 'Asset num' %}</th>
<th class="text-center">{% trans 'Active' %}</a></th>
<th class="text-center">{% trans 'Active' %}</th>
</tr>
</thead>
</table>
......
......@@ -92,7 +92,7 @@ $(document).ready(function(){
jumpserver.initDataTable(options);
}).on('click', '#btn_bulk_update', function(){
var action = $('#slct_bulk_update').val();
var $data_table = $('#user_list_table').DataTable()
var $data_table = $('#user_list_table').DataTable();
var id_list = [];
var plain_id_list = [];
$data_table.rows({selected: true}).every(function(){
......@@ -101,7 +101,7 @@ $(document).ready(function(){
});
if (id_list === []) {
return false;
};
}
var the_url = "{% url 'users:user-bulk-update-api' %}";
function doDeactive() {
var body = $.each(id_list, function(index, user_object) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment