Merge with audits

pull/530/head
ibuler 2016-10-27 23:04:02 +08:00
commit 688bfa556c
50 changed files with 1076 additions and 119 deletions

View File

@ -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')

View File

@ -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

View File

@ -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},
# ]
#

View 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']

View File

@ -1,19 +1,33 @@
#!/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

View File

@ -0,0 +1,46 @@
{% 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 %}

View File

@ -0,0 +1,58 @@
{% 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

@ -0,0 +1,109 @@
{% 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 %}

View File

@ -0,0 +1,108 @@
{% 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 %}

View File

@ -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'),
]

View File

@ -1,2 +1,5 @@
# ~*~ coding: utf-8 ~*~
#
from users.utils import AdminUserRequiredMixin

View File

@ -1,3 +1,79 @@
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)

View File

@ -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

View File

@ -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')),
]

View File

@ -1,3 +1,36 @@
# ~*~ 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)

View File

@ -1,12 +1,8 @@
# ~*~ 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

View File

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

View File

@ -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'),
]

Binary file not shown.

View File

@ -0,0 +1,78 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
<svg xmlns="http://www.w3.org/2000/svg">
<metadata>
This is a custom SVG font generated by IcoMoon.
<iconset grid="16"></iconset>
</metadata>
<defs>
<font id="footable" horiz-adv-x="512" >
<font-face units-per-em="512" ascent="480" descent="-32" />
<missing-glyph horiz-adv-x="512" />
<glyph class="hidden" unicode="&#xf000;" d="M0,480L 512 -32L0 -32 z" horiz-adv-x="0" />
<glyph unicode="&#xe000;" d="M 496,288L 320,288 L 320,464 c0,8.836-7.164,16-16,16l-96,0 c-8.836,0-16-7.164-16-16l0-176 L 16,288 c-8.836,0-16-7.164-16-16l0-96
c0-8.836, 7.164-16, 16-16l 176,0 l0-176 c0-8.836, 7.164-16, 16-16l 96,0 c 8.836,0, 16,7.164, 16,16L 320,160 l 176,0 c 8.836,0, 16,7.164, 16,16l0,96
C 512,280.836, 504.836,288, 496,288z" />
<glyph unicode="&#xe001;" d="M0,272l0-96 c0-8.836, 7.164-16, 16-16l 480,0 c 8.836,0, 16,7.164, 16,16l0,96 c0,8.836-7.164,16-16,16L 16,288 C 7.164,288,0,280.836,0,272z" />
<glyph unicode="&#xe002;" d="M 256,480C 114.615,480,0,365.385,0,224s 114.615-256, 256-256s 256,114.615, 256,256S 397.385,480, 256,480z M 288,192l0-128 l-64,0 L 224,192 L 96,192 l0,64
l 128,0 L 224,384 l 64,0 l0-128 l 128,0 l0-64 L 288,192 z" />
<glyph unicode="&#xe003;" d="M 256,480C 114.615,480,0,365.385,0,224s 114.615-256, 256-256s 256,114.615, 256,256S 397.385,480, 256,480z M 416,192L 96,192 l0,64 l 320,0 L 416,192 z" />
<glyph unicode="&#xe004;" d="M 256,480C 114.615,480,0,365.385,0,224s 114.615-256, 256-256s 256,114.615, 256,256S 397.385,480, 256,480z M 256,32
c-106.039,0-192,85.961-192,192c0,106.039, 85.961,192, 192,192c 106.039,0, 192-85.961, 192-192C 448,117.961, 362.039,32, 256,32zM 384,192 L 288,192 L 288,96 L 224,96 L 224,192 L 128,192 L 128,256 L 224,256 L 224,352 L 288,352 L 288,256 L 384,256 Z" />
<glyph unicode="&#xe005;" d="M 256,480C 114.615,480,0,365.385,0,224s 114.615-256, 256-256s 256,114.615, 256,256S 397.385,480, 256,480z M 256,32
c-106.039,0-192,85.961-192,192c0,106.039, 85.961,192, 192,192c 106.039,0, 192-85.961, 192-192C 448,117.961, 362.039,32, 256,32zM 128,256L 384,256L 384,192L 128,192z" />
<glyph unicode="&#xe006;" d="M 256,214.857l0-18.286 q0-4 -2.571-6.571t-6.571-2.571l-64,0 l0-64 q0-4 -2.571-6.571t-6.571-2.571l-18.286,0 q-4,0 -6.571,2.571t-2.571,6.571l0,64 l-64,0 q-4,0 -6.571,2.571t-2.571,6.571l0,18.286 q0,4 2.571,6.571t 6.571,2.571l 64,0 l0,64 q0,4 2.571,6.571t 6.571,2.571l 18.286,0 q 4,0 6.571-2.571t 2.571-6.571l0-64 l 64,0 q 4,0 6.571-2.571t 2.571-6.571zM 292.571,105.143l0,201.143 q0,11.429 -8,19.429t-19.429,8l-201.143,0 q-11.429,0 -19.429-8 t-8-19.429l0-201.143 q0-11.429 8-19.429t 19.429-8l 201.143,0 q 11.429,0 19.429,8t 8,19.429zM 329.143,306.286l0-201.143 q0-26.286 -18.714-45.143t-45.286-18.857l-201.143,0 q-26.571,0 -45.286,18.857t-18.714,45.143l0,201.143 q0,26.571 18.714,45.286t 45.286,18.714l 201.143,0 q 26.571,0 45.286-18.714t 18.714-45.286z" horiz-adv-x="329.143" />
<glyph unicode="&#xe007;" d="M 265.143,370.286q 26.571,0 45.286-18.714t 18.714-45.286l0-201.143 q0-26.286 -18.714-45.143t-45.286-18.857l-201.143,0 q-26.571,0 -45.286,18.857t-18.714,45.143l0,201.143 q0,26.571 18.714,45.286t 45.286,18.714l 201.143,0 zM 292.571,105.143l0,201.143 q0,11.429 -8,19.429t-19.429,8l-201.143,0 q-11.429,0 -19.429-8t-8-19.429l0-201.143 q0-11.429 8-19.429t 19.429-8l 201.143,0 q 11.429,0 19.429,8t 8,19.429z M 246.857,224q 4,0 6.571-2.571t 2.571-6.571l0-18.286 q0-4 -2.571-6.571t-6.571-2.571l-164.571,0 q-4,0 -6.571,2.571t-2.571,6.571l0,18.286 q0,4 2.571,6.571t 6.571,2.571l 164.571,0 z" horiz-adv-x="329.143" />
<glyph unicode="&#xe008;" d="M 365.714,205.714l0,36.571 q0,7.429 -5.429,12.857t-12.857,5.429l-91.429,0 l0,91.429 q0,7.429 -5.429,12.857t-12.857,5.429l-36.571,0 q-7.429,0 -12.857-5.429t-5.429-12.857l0-91.429 l-91.429,0 q-7.429,0 -12.857-5.429t-5.429-12.857l0-36.571 q0-7.429 5.429-12.857t 12.857-5.429l 91.429,0 l0-91.429 q0-7.429 5.429-12.857t 12.857-5.429l 36.571,0 q 7.429,0 12.857,5.429t 5.429,12.857l0,91.429 l 91.429,0 q 7.429,0 12.857,5.429t 5.429,12.857zM 438.857,361.143l0-274.286 q0-34 -24.143-58.143t-58.143-24.143l-274.286,0 q-34,0 -58.143,24.143t-24.143,58.143l0,274.286 q0,34 24.143,58.143t 58.143,24.143l 274.286,0 q 34,0 58.143-24.143t 24.143-58.143z" horiz-adv-x="438.857" />
<glyph unicode="&#xe009;" d="M 365.714,205.714l0,36.571 q0,7.429 -5.429,12.857t-12.857,5.429l-256,0 q-7.429,0 -12.857-5.429t-5.429-12.857l0-36.571 q0-7.429 5.429-12.857t 12.857-5.429l 256,0 q 7.429,0 12.857,5.429t 5.429,12.857zM 438.857,361.143l0-274.286 q0-34 -24.143-58.143t-58.143-24.143l-274.286,0 q-34,0 -58.143,24.143t-24.143,58.143l0,274.286 q0,34 24.143,58.143t 58.143,24.143l 274.286,0 q 34,0 58.143-24.143 t 24.143-58.143z" horiz-adv-x="438.857" />
<glyph unicode="&#xe00a;" d="M 512,224C 512,82.615, 397.385-32, 256-32s -256,114.615, -256,256s 114.615,256, 256,256S 512,365.385, 512,224z M 233.372,374.628
l -128-128.001C 99.124,240.379, 96,232.189, 96,224s 3.124-16.379 9.372-22.627c 12.497-12.497 32.759-12.497, 45.256,0L 224,274.745
L 224,96 c 0-17.673 14.327-32 32-32c 17.673,0, 32,14.327, 32,32l0,178.745 l 73.373-73.373c 12.497-12.497 32.758-12.497, 45.255,0
c 12.497,12.497, 12.497,32.758, 0,45.254l -128,128.001C 266.131,387.124, 245.869,387.124, 233.372,374.628z" />
<glyph unicode="&#xe00b;" d="M 512,224C 512,365.385, 397.385,480, 256,480s -256-114.615, -256-256s 114.615-256, 256-256S 512,82.615, 512,224z M 233.372,73.372
l -128,128.001C 99.124,207.621, 96,215.811, 96,224s 3.124,16.379 9.372,22.627c 12.497,12.497 32.759,12.497, 45.256,0L 224,173.255
L 224,352 c 0,17.673 14.327,32 32,32c 17.673,0, 32-14.327, 32-32l0-178.745 l 73.373,73.373c 12.497,12.497 32.758,12.497, 45.255,0
c 12.497-12.497, 12.497-32.758, 0-45.254l -128-128.001C 266.131,60.876, 245.869,60.876, 233.372,73.372z" />
<glyph unicode="&#xe00c;" d="M 256,480C 397.385,480, 512,365.385, 512,224s -114.615-256, -256-256s -256,114.615, -256,256S 114.615,480, 256,480z M 105.372,201.372
l 128.001-128C 239.621,67.124, 247.811,64, 256,64s 16.379,3.124 22.627,9.372c 12.497,12.497 12.497,32.759,0,45.256L 205.255,192
L 384,192 c 17.673,0 32,14.327 32,32c0,17.673, -14.327,32, -32,32l-178.745,0 l 73.373,73.373c 12.497,12.497 12.497,32.758,0,45.255
c -12.497,12.497, -32.758,12.497, -45.254,0l -128.001-128C 92.876,234.131, 92.876,213.869, 105.372,201.372z" />
<glyph unicode="&#xe00d;" d="M 256,480C 114.615,480,0,365.385,0,224s 114.615-256, 256-256s 256,114.615, 256,256S 397.385,480, 256,480z M 406.628,201.372
l-128.001-128C 272.379,67.124, 264.189,64, 256,64s-16.379,3.124-22.627,9.372c-12.497,12.497-12.497,32.759,0,45.256L 306.745,192
L 128,192 c-17.673,0-32,14.327-32,32c0,17.673, 14.327,32, 32,32l 178.745,0 l-73.373,73.373c-12.497,12.497-12.497,32.758,0,45.255
c 12.497,12.497, 32.758,12.497, 45.254,0l 128.001-128C 419.124,234.131, 419.124,213.869, 406.628,201.372z" />
<glyph unicode="&#xe00e;" d="M0,160L 96,64L 256,224L 416,64L 512,160L 256.001,416 z" />
<glyph unicode="&#xe00f;" d="M 512,288L 416,384L 256,224L 96,384L0,288L 256,32.001 z" />
<glyph unicode="&#xe010;" d="M 320-32L 416,64L 256,224L 416,384L 320,480L 64,224 z" />
<glyph unicode="&#xe011;" d="M 192,480L 96,384L 256,224L 96,64L 192-32L 448,224 z" />
<glyph unicode="&#xe012;" d="M 292.571,132.571q0-7.429 -5.429-12.857t-12.857-5.429l-256,0 q-7.429,0 -12.857,5.429t-5.429,12.857t 5.429,12.857l 128,128q 5.429,5.429 12.857,5.429t 12.857-5.429l 128-128q 5.429-5.429 5.429-12.857z" horiz-adv-x="292.571" />
<glyph unicode="&#xe013;" d="M 292.571,278.857q0-7.429 -5.429-12.857l-128-128q-5.429-5.429 -12.857-5.429t-12.857,5.429l-128,128q-5.429,5.429 -5.429,12.857t 5.429,12.857t 12.857,5.429l 256,0 q 7.429,0 12.857-5.429t 5.429-12.857z" horiz-adv-x="292.571" />
<glyph unicode="&#xe014;" d="M 182.857,352l0-256 q0-7.429 -5.429-12.857t-12.857-5.429t-12.857,5.429l-128,128q-5.429,5.429 -5.429,12.857t 5.429,12.857l 128,128q 5.429,5.429 12.857,5.429t 12.857-5.429t 5.429-12.857z" horiz-adv-x="182.857" />
<glyph unicode="&#xe015;" d="M 164.571,224q0-7.429 -5.429-12.857l-128-128q-5.429-5.429 -12.857-5.429t-12.857,5.429t-5.429,12.857l0,256 q0,7.429 5.429,12.857t 12.857,5.429t 12.857-5.429l 128-128q 5.429-5.429 5.429-12.857z" horiz-adv-x="182.857" />
<glyph unicode="&#xe016;" d="M 256,480L 32-32L 256,64L 480-32 z" />
<glyph unicode="&#xe017;" d="M 256-32L 480,480L 256,384L 32,480 z" />
<glyph unicode="&#xe018;" d="M0,224L 512,0L 416,224L 512,448 z" />
<glyph unicode="&#xe019;" d="M 512,224L0,448L 96,224L0,0 z" />
<glyph unicode="&#xe01a;" d="M 512,224C 512,82.615, 397.385-32, 256-32s -256,114.615, -256,256s 114.615,256, 256,256S 512,365.385, 512,224z M 48,224
c 0-114.875 93.125-208 208-208S 464,109.125, 464,224s -93.125,208, -208,208S 48,338.875, 48,224zM 278.627,374.628l 128-128.001c 12.497-12.496 12.497-32.757 0-45.254c -12.497-12.497 -32.758-12.497,-45.255,0L 288,274.745
L 288,96 c 0-17.673 -14.327-32 -32-32c-17.673,0, -32,14.327, -32,32l0,178.745 l -73.372-73.373c -12.497-12.497 -32.759-12.497,-45.256,0
C 99.124,207.621, 96,215.811, 96,224s 3.124,16.379, 9.372,22.627l 128,128.001C 245.869,387.124, 266.131,387.124, 278.627,374.628z" />
<glyph unicode="&#xe01b;" d="M 512,224C 512,365.385, 397.385,480, 256,480s -256-114.615, -256-256s 114.615-256, 256-256S 512,82.615, 512,224z M 48,224
c 0,114.875 93.125,208 208,208S 464,338.875, 464,224s -93.125-208, -208-208S 48,109.125, 48,224zM 278.627,73.372l 128,128.001c 12.497,12.496 12.497,32.757 0,45.254c -12.497,12.497 -32.758,12.497,-45.255,0L 288,173.255
L 288,352 c 0,17.673 -14.327,32 -32,32c-17.673,0, -32-14.327, -32-32l0-178.745 l -73.372,73.373c -12.497,12.497 -32.759,12.497,-45.256,0
C 99.124,240.379, 96,232.189, 96,224s 3.124-16.379, 9.372-22.627l 128-128.001C 245.869,60.876, 266.131,60.876, 278.627,73.372z" />
<glyph unicode="&#xe01c;" d="M 256,480C 397.385,480, 512,365.385, 512,224s -114.615-256, -256-256s -256,114.615, -256,256S 114.615,480, 256,480z M 256,16
c 114.875,0 208,93.125 208,208S 370.875,432, 256,432s -208-93.125, -208-208S 141.125,16, 256,16zM 105.372,246.627l 128.001,128c 12.496,12.497 32.757,12.497 45.254,0c 12.497-12.497 12.497-32.758,0-45.255L 205.255,256
L 384,256 c 17.673,0 32-14.327 32-32c0-17.673, -14.327-32, -32-32l-178.745,0 l 73.373-73.372c 12.497-12.497 12.497-32.759,0-45.256
C 272.379,67.124, 264.189,64, 256,64s -16.379,3.124, -22.627,9.372l -128.001,128C 92.876,213.869, 92.876,234.131, 105.372,246.627z" />
<glyph unicode="&#xe01d;" d="M 256,480C 114.615,480,0,365.385,0,224s 114.615-256, 256-256s 256,114.615, 256,256S 397.385,480, 256,480z M 256,16
c-114.875,0-208,93.125-208,208S 141.125,432, 256,432s 208-93.125, 208-208S 370.875,16, 256,16zM 406.628,246.627l-128.001,128c-12.496,12.497-32.757,12.497-45.254,0c-12.497-12.497-12.497-32.758,0-45.255L 306.745,256
L 128,256 c-17.673,0-32-14.327-32-32c0-17.673, 14.327-32, 32-32l 178.745,0 l-73.373-73.372c-12.497-12.497-12.497-32.759,0-45.256
C 239.621,67.124, 247.811,64, 256,64s 16.379,3.124, 22.627,9.372l 128.001,128C 419.124,213.869, 419.124,234.131, 406.628,246.627z" />
<glyph unicode="&#xe01e;" d="M 307.143,141.714q0-3.714 -2.857-6.571l-14.286-14.286q-2.857-2.857 -6.571-2.857t-6.571,2.857l-112.286,112.286l-112.286-112.286q-2.857-2.857 -6.571-2.857t-6.571,2.857l-14.286,14.286q-2.857,2.857 -2.857,6.571t 2.857,6.571l 133.143,133.143q 2.857,2.857 6.571,2.857t 6.571-2.857l 133.143-133.143q 2.857-2.857 2.857-6.571z" horiz-adv-x="329.143" />
<glyph unicode="&#xe01f;" d="M 307.143,269.714q0-3.714 -2.857-6.571l-133.143-133.143q-2.857-2.857 -6.571-2.857t-6.571,2.857l-133.143,133.143q-2.857,2.857 -2.857,6.571t 2.857,6.571l 14.286,14.286q 2.857,2.857 6.571,2.857t 6.571-2.857l 112.286-112.286l 112.286,112.286q 2.857,2.857 6.571,2.857t 6.571-2.857l 14.286-14.286q 2.857-2.857 2.857-6.571z" horiz-adv-x="329.143" />
<glyph unicode="&#xe020;" d="M 179.143,324.571q0-3.714 -2.857-6.571l-112.286-112.286l 112.286-112.286q 2.857-2.857 2.857-6.571t-2.857-6.571l-14.286-14.286q-2.857-2.857 -6.571-2.857t-6.571,2.857l-133.143,133.143q-2.857,2.857 -2.857,6.571t 2.857,6.571l 133.143,133.143q 2.857,2.857 6.571,2.857t 6.571-2.857l 14.286-14.286q 2.857-2.857 2.857-6.571z" horiz-adv-x="182.857" />
<glyph unicode="&#xe021;" d="M 170,205.714q0-3.714 -2.857-6.571l-133.143-133.143q-2.857-2.857 -6.571-2.857t-6.571,2.857l-14.286,14.286q-2.857,2.857 -2.857,6.571t 2.857,6.571l 112.286,112.286l-112.286,112.286q-2.857,2.857 -2.857,6.571t 2.857,6.571l 14.286,14.286q 2.857,2.857 6.571,2.857t 6.571-2.857l 133.143-133.143q 2.857-2.857 2.857-6.571z" horiz-adv-x="182.857" />
<glyph unicode="&#xe022;" d="M 292.571,169.143q0-7.429 -5.429-12.857l-128-128q-5.429-5.429 -12.857-5.429t-12.857,5.429l-128,128q-5.429,5.429 -5.429,12.857t 5.429,12.857t 12.857,5.429l 256,0 q 7.429,0 12.857-5.429t 5.429-12.857zM 292.571,278.857q0-7.429 -5.429-12.857t-12.857-5.429l-256,0 q-7.429,0 -12.857,5.429t-5.429,12.857t 5.429,12.857l 128,128q 5.429,5.429 12.857,5.429t 12.857-5.429l 128-128q 5.429-5.429 5.429-12.857z" horiz-adv-x="292.571" />
<glyph unicode="&#x20;" horiz-adv-x="256" />
</font></defs></svg>

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,179 @@
@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;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 701 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

File diff suppressed because one or more lines are too long

View File

@ -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;
}
};

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,2 +0,0 @@
/*! 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)}})})}}}();

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,8 +0,0 @@
/*!
@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}

View 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="#">

View File

@ -1,7 +1,7 @@
# -*- 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,)

17
apps/terminal/forms.py Normal file
View File

@ -0,0 +1,17 @@
# ~*~ 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'
}

View File

@ -2,4 +2,5 @@
#
from users.backends import IsSuperUserOrTerminalUser
from audits.models import ProxyLog

View File

@ -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):

View File

@ -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):

View File

@ -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 %}

View File

@ -0,0 +1,78 @@
{% 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 %}

View File

@ -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'),
]

View File

@ -1,10 +1,12 @@
# ~*~ 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')

View 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

View File

@ -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

View 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>

View File

@ -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) {