Commit 08e17884 authored by ibuler's avatar ibuler

[Feature] 打算拆分下载和上传为独立模块,时间有限暂时放弃

parent 160b01ec
......@@ -9,6 +9,7 @@ from common.utils import validate_ssh_private_key, ssh_pubkey_gen, ssh_key_gen,
logger = get_logger(__file__)
from rest_framework import serializers
class AssetCreateForm(forms.ModelForm):
......@@ -240,7 +241,7 @@ class SystemUserForm(forms.ModelForm):
fields = [
'name', 'username', 'protocol', 'auto_generate_key',
'password', 'private_key_file', 'auto_push', 'sudo',
'comment', 'shell', 'cluster'
'comment', 'shell', 'cluster', 'priority',
]
widgets = {
'name': forms.TextInput(attrs={'placeholder': _('Name')}),
......@@ -254,6 +255,7 @@ class SystemUserForm(forms.ModelForm):
'username': '* required',
'cluster': 'If auto push checked, system user will be create at cluster assets',
'auto_push': 'Auto push system user to asset',
'priority': 'High level will be using login asset as default, if user was granted more than 2 system user',
}
......@@ -261,7 +263,7 @@ class SystemUserUpdateForm(forms.ModelForm):
class Meta:
model = SystemUser
fields = [
'name', 'username', 'protocol',
'name', 'username', 'protocol', 'priority',
'sudo', 'comment', 'shell', 'cluster'
]
widgets = {
......@@ -275,6 +277,7 @@ class SystemUserUpdateForm(forms.ModelForm):
'name': '* required',
'username': '* required',
'cluster': 'If auto push checked, then push system user to that cluster assets',
'priority': 'High level will be using login asset as default, if user was granted more than 2 system user',
}
......
......@@ -7,6 +7,7 @@ import logging
import uuid
from hashlib import md5
import sshpubkeys
from django.db import models
from django.utils.translation import ugettext_lazy as _
from django.conf import settings
......@@ -27,7 +28,8 @@ class AssetUser(models.Model):
_private_key = models.TextField(max_length=4096, blank=True, null=True, verbose_name=_('SSH private key'), validators=[private_key_validator, ])
_public_key = models.TextField(max_length=4096, blank=True, verbose_name=_('SSH public key'))
comment = models.TextField(blank=True, verbose_name=_('Comment'))
date_created = models.DateTimeField(auto_now_add=True, null=True)
date_created = models.DateTimeField(auto_now_add=True)
date_updated = models.DateTimeField(auto_now=True)
created_by = models.CharField(max_length=32, null=True, verbose_name=_('Created by'))
@property
......@@ -45,16 +47,21 @@ class AssetUser(models.Model):
@property
def private_key(self):
if self._private_key:
key_str = signer.unsign(self._private_key)
return ssh_key_string_to_obj(key_str, password=self.password)
else:
return None
return signer.unsign(self._private_key)
@private_key.setter
def private_key(self, private_key_raw):
raise AttributeError("Using set_auth do that")
# self._private_key = signer.sign(private_key_raw)
@property
def private_key_obj(self):
if self._private_key:
key_str = signer.unsign(self._private_key)
return ssh_key_string_to_obj(key_str, password=self.password)
else:
return None
@property
def private_key_file(self):
if not self.private_key:
......@@ -74,6 +81,15 @@ class AssetUser(models.Model):
def public_key(self):
return signer.unsign(self._public_key)
@property
def public_key_obj(self):
if self.public_key:
try:
return sshpubkeys.SSHKey(self.public_key)
except TabError:
pass
return None
def set_auth(self, password=None, private_key=None, public_key=None):
update_fields = []
if password:
......@@ -170,6 +186,7 @@ class SystemUser(AssetUser):
('K', 'Public key'),
)
cluster = models.ManyToManyField('assets.Cluster', verbose_name=_("Cluster"))
priority = models.IntegerField(default=10, verbose_name=_("Priority")) # Todo: If user granted more priority user, default will be login as the hign
protocol = models.CharField(max_length=16, choices=PROTOCOL_CHOICES, default='ssh', verbose_name=_('Protocol'))
auto_push = models.BooleanField(default=True, verbose_name=_('Auto push'))
sudo = models.TextField(default='/sbin/ifconfig', verbose_name=_('Sudo'))
......@@ -205,6 +222,7 @@ class SystemUser(AssetUser):
'name': self.name,
'username': self.username,
'protocol': self.protocol,
'priority': self.priority,
'auto_push': self.auto_push,
}
......
......@@ -115,7 +115,7 @@ class SystemUserSerializer(serializers.ModelSerializer):
class AssetSystemUserSerializer(serializers.ModelSerializer):
class Meta:
model = SystemUser
fields = ('id', 'name', 'username', 'protocol', 'comment')
fields = ('id', 'name', 'username', 'priority', 'protocol', 'comment',)
class SystemUserUpdateAssetsSerializer(serializers.ModelSerializer):
......
......@@ -37,6 +37,7 @@
<h3>{% trans 'Basic' %}</h3>
{% bootstrap_field form.name layout="horizontal" %}
{% bootstrap_field form.username layout="horizontal" %}
{% bootstrap_field form.priority layout="horizontal" %}
{% bootstrap_field form.protocol layout="horizontal" %}
{% bootstrap_field form.cluster layout="horizontal" %}
......@@ -49,7 +50,6 @@
{{ form.auto_generate_key}}
</div>
</div>
</div>
<div class="auth-fields">
{% bootstrap_field form.private_key_file layout="horizontal" %}
......
......@@ -64,7 +64,9 @@
{% block custom_foot_js %}
<script>
$(document).ready(function () {
$('.select2').select2();
$('.select2').select2({
allowClear: true
});
$("#tags").select2({
tags: true,
maximumSelectionLength: 8 //最多能够选择的个数
......
......@@ -123,7 +123,7 @@ class AdminUserAssetsView(AdminUserRequiredMixin, SingleObjectMixin, ListView):
class AdminUserDeleteView(AdminUserRequiredMixin, DeleteView):
model = AdminUser
template_name = 'assets/delete_confirm.html'
template_name = 'delete_confirm.html'
success_url = reverse_lazy('assets:admin-user-list')
......@@ -9,12 +9,13 @@ import chardet
from io import StringIO
from django.conf import settings
from django.core.exceptions import ImproperlyConfigured, FieldDoesNotExist
from django.utils.translation import ugettext_lazy as _
from django.views.generic import TemplateView, ListView, View
from django.views.generic.edit import CreateView, DeleteView, FormView, UpdateView
from django.urls import reverse_lazy
from django.views.generic.detail import DetailView, SingleObjectMixin
from django.http import HttpResponse, JsonResponse, HttpResponseRedirect
from django.http import HttpResponse, JsonResponse, HttpResponseRedirect, Http404
from django.views.decorators.csrf import csrf_protect, csrf_exempt
from django.utils.decorators import method_decorator
from django.core.cache import cache
......@@ -22,8 +23,10 @@ from django.utils import timezone
from django.contrib.auth.mixins import LoginRequiredMixin
from django.shortcuts import get_object_or_404, redirect, reverse
from common.mixins import JSONResponseMixin
from common.utils import get_object_or_none
from common.imexp import ModelExportView
from .. import forms
from ..models import Asset, AssetGroup, AdminUser, Cluster, SystemUser
from ..hands import AdminUserRequiredMixin
......@@ -169,7 +172,7 @@ class AssetUpdateView(AdminUserRequiredMixin, UpdateView):
class AssetDeleteView(AdminUserRequiredMixin, DeleteView):
model = Asset
template_name = 'assets/delete_confirm.html'
template_name = 'delete_confirm.html'
success_url = reverse_lazy('assets:asset-list')
......@@ -193,46 +196,11 @@ class AssetDetailView(DetailView):
@method_decorator(csrf_exempt, name='dispatch')
class AssetExportView(View):
def get(self, request):
spm = request.GET.get('spm', '')
assets_id_default = [Asset.objects.first().id] if Asset.objects.first() else [1]
assets_id = cache.get(spm, assets_id_default)
fields = [
field for field in Asset._meta.fields
if field.name not in [
'date_created'
]
]
filename = 'assets-{}.csv'.format(
timezone.localtime(timezone.now()).strftime('%Y-%m-%d_%H-%M-%S'))
response = HttpResponse(content_type='text/csv')
response['Content-Disposition'] = 'attachment; filename="%s"' % filename
response.write(codecs.BOM_UTF8)
assets = Asset.objects.filter(id__in=assets_id)
writer = csv.writer(response, dialect='excel',
quoting=csv.QUOTE_MINIMAL)
header = [field.verbose_name for field in fields]
header.append(_('Asset groups'))
writer.writerow(header)
for asset in assets:
groups = ','.join([group.name for group in asset.groups.all()])
data = [getattr(asset, field.name) for field in fields]
data.append(groups)
writer.writerow(data)
return response
def post(self, request, *args, **kwargs):
try:
assets_id = json.loads(request.body).get('assets_id', [])
except ValueError:
return HttpResponse('Json object not valid', status=400)
spm = uuid.uuid4().hex
cache.set(spm, assets_id, 300)
url = reverse_lazy('assets:asset-export') + '?spm=%s' % spm
return JsonResponse({'redirect': url})
class AssetExportView(ModelExportView):
filename_prefix = 'jumpserver'
redirect_url = reverse_lazy('assets:asset-export')
model = Asset
fields = ('hostname', 'ip')
class BulkImportAssetView(AdminUserRequiredMixin, JSONResponseMixin, FormView):
......
......@@ -97,5 +97,5 @@ class ClusterAssetsView(AdminUserRequiredMixin, DetailView):
class ClusterDeleteView(AdminUserRequiredMixin, DeleteView):
model = Cluster
template_name = 'assets/delete_confirm.html'
template_name = 'delete_confirm.html'
success_url = reverse_lazy('assets:cluster-list')
......@@ -101,6 +101,6 @@ class AssetGroupUpdateView(AdminUserRequiredMixin, UpdateView):
class AssetGroupDeleteView(AdminUserRequiredMixin, DeleteView):
template_name = 'assets/delete_confirm.html'
template_name = 'delete_confirm.html'
model = AssetGroup
success_url = reverse_lazy('assets:asset-group-list')
......@@ -99,7 +99,7 @@ class SystemUserDetailView(AdminUserRequiredMixin, DetailView):
class SystemUserDeleteView(AdminUserRequiredMixin, DeleteView):
model = SystemUser
template_name = 'assets/delete_confirm.html'
template_name = 'delete_confirm.html'
success_url = reverse_lazy('assets:system-user-list')
......
# -*- coding: utf-8 -*-
#
import codecs
import csv
import uuid
import json
from io import StringIO
import warnings
import chardet
from django import forms
from django.utils import timezone
from django.views import View
from django.core.cache import cache
from django.core.exceptions import ImproperlyConfigured, FieldDoesNotExist
from django.utils.encoding import force_text
from django.http import Http404, HttpResponseRedirect, HttpResponse, JsonResponse
class ModelExportPostMixin:
"""
将用户post上来的数据转存到cache, 生成一个uuid, redirect 到GET URL
"""
redirect_url = None
error_message = 'Json object not valid'
keyword = 'spm'
cache_key = None
request = None
def get_redirect_url(self):
if self.redirect_url:
# Forcing possible reverse_lazy evaluation
url = force_text(self.redirect_url)
else:
msg = "No URL to redirect to. Provide a redirect_url."
raise ImproperlyConfigured(msg)
sep = "?" if url.find('?') else '&'
url = '{}{}{}={}'.format(url, sep, self.keyword, self.cache_key)
return url
def save_objects_id_to_cache(self, objects_id):
self.cache_key = uuid.uuid4().hex
cache.set(self.cache_key, objects_id, 300)
return self.cache_key
def get_objects_id_from_request(self):
try:
objects_id = json.loads(self.request.body)
except ValueError:
raise Http404(self.error_message)
return objects_id
def get_redirect_response(self):
objects_id = self.get_objects_id_from_request()
self.save_objects_id_to_cache(objects_id)
url = self.get_redirect_url()
return HttpResponseRedirect(redirect_to=url)
# View need implement it
# def post(self, request, *args, **kwargs):
# self.request = request
# return self.get_redirect_response()
class MethodField:
def __init__(self, name, verbose_name=None):
self.name = name
self.verbose_name = verbose_name
if self.verbose_name is None:
self.verbose_name = name
class FieldCheckMeta(type):
def __new__(cls, name, bases, attrs):
error = cls.validate_fields(attrs)
if not error:
return super().__new__(cls, name, bases, attrs)
else:
raise AttributeError(error)
@staticmethod
def validate_fields(attrs):
model = attrs.get('model')
fields = attrs.get('fields')
if model is None or fields in ('__all__', None):
return None
all_attr = [attr for attr in dir(model) if not attr.startswith('_')]
invalid_fields = []
for field in fields:
if field not in all_attr:
invalid_fields.append(field)
if not invalid_fields:
return None
error = 'model {} is not have `{}` attr, check `fields` setting'.format(
model._meta.model_name, ', '.join(invalid_fields)
)
return error
class ModelFieldsMixin(metaclass=FieldCheckMeta):
model = None
fields = None
exclude = None
errors = None
__cleaned_fields_name = None
__is_valid = False
__defined_fields_name = None
def get_define_fields_name(self):
"""
Calculate fields, fields may be `__all__`, `(field1, field2)` or
set `exclude` so do that
:return: => list
"""
if self.__defined_fields_name:
return self.__defined_fields_name
all_fields = [field.name for field in self.model._meta.fields]
if self.fields == '__all__':
return all_fields
elif self.fields:
return self.fields
elif self.exclude:
return list(set(all_fields) - set(self.exclude))
else:
return []
def get_field(self, field_name):
try:
return self.model._meta.get_field(field_name)
except FieldDoesNotExist:
attr = getattr(self.model, field_name)
if hasattr(attr, 'verbose_name'):
verbose_name = getattr(attr, 'verbose_name')
else:
verbose_name = field_name
return MethodField(field_name, verbose_name)
def get_fields(self, cleaned_fields_name):
"""
Get fields by fields name
:param cleaned_fields_name:
:return:
"""
fields = []
for name in cleaned_fields_name:
fields.append(self.get_field(name))
return fields
def get_define_fields(self):
fields_name = self.get_define_fields_name()
return self.get_fields(fields_name)
def valid_field_name(self, field_name):
if not hasattr(self.model, field_name):
msg = "{} not has `{}` attr".format(self.model._meta.model_name, field_name)
raise AttributeError(msg)
elif field_name not in self.get_define_fields_name():
msg = '{} not allowed by server'.format(field_name)
raise AttributeError(msg)
def is_valid(self, fields, ignore_exception=True):
self.__cleaned_fields_name = []
self.errors = {}
for field_name in fields:
try:
self.valid_field_name(field_name)
self.__cleaned_fields_name.append(field_name)
except AttributeError as e:
if not ignore_exception:
self.errors[field_name] = str(e)
if self.errors:
self.__is_valid = False
return False
else:
self.__is_valid = True
return True
@property
def field_verbose_name_mapping(self):
mapping = {}
for field in self.get_define_fields():
mapping[field.verbose_name] = field.name
return mapping
@property
def cleaned_fields(self):
if self.__cleaned_fields_name is None:
raise AttributeError("Run `is_valid` first")
if not self.__is_valid:
warnings.warn("Is not valid, result may be not complete")
return self.get_fields(self.__cleaned_fields_name)
class ModelExportGetMixin(ModelFieldsMixin):
filename_prefix = 'jumpserver'
response = None
writer = None
model = None
objects_id = None
queryset = None
keyword = 'spm'
def get_filename(self):
now = timezone.localtime(timezone.now()).strftime('%Y-%m-%d_%H-%M-%S')
filename = '{}-{}-{}.csv'.format(
self.filename_prefix, self.model._meta.model_name, now
)
return filename
def get_objects_id(self):
cache_key = self.request.GET.get(self.keyword)
self.objects_id = cache.get(cache_key, [])
return self.objects_id
def get_queryset(self):
queryset = None
if self.queryset:
queryset = self.queryset
elif self.queryset is None:
queryset = self.model._meta.default_manager.all()
if queryset is None:
raise AttributeError("Get queryset failed, set `queryset` or `model`")
objects_id = self.get_objects_id()
queryset_filtered = queryset.filter(id__in=objects_id)
return queryset_filtered
def initial_csv_response(self):
filename = self.get_filename()
self.response = HttpResponse(content_type='text/csv')
self.response['Content-Disposition'] = 'attachment; filename="{}"'.format(filename)
self.response.write(codecs.BOM_UTF8)
self.writer = csv.writer(self.response, dialect='excel', quoting=csv.QUOTE_MINIMAL)
header = []
for field in self.get_define_fields():
if hasattr(field, 'verbose_name'):
header.append(getattr(field, 'verbose_name'))
else:
header.append(getattr(field, 'name'))
self.writer.writerow(header)
def make_csv_response(self):
self.initial_csv_response()
queryset = self.get_queryset()
for instance in queryset:
data = [getattr(instance, field.name) for field in self.get_define_fields()]
self.writer.writerow(data)
return self.response
class FileForm(forms.Form):
file = forms.FileField()
class ModelImportPostMixin(ModelFieldsMixin):
form_context = "file"
csv_data = None
form_class = FileForm
stream = None
def get_form(self):
form = self.form_class(self.request.POST)
if form.is_valid():
raise Http404("Form is not valid")
return form
def get_stream(self):
self.stream = self.get_form().cleaned_data[self.form_context]
return self.stream
def get_csv_data(self, stream=None):
if stream is None:
stream = self.stream
result = chardet.detect(stream.read())
stream.seek(0)
raw_data = stream.read().decode(result['encoding'])\
.strip(codecs.BOM_UTF8.decode())
csv_file = StringIO(raw_data)
reader = csv.reader(csv_file)
csv_data = [row for row in reader]
self.csv_data = csv_data
return csv_data
def cleaned_post_fields(self):
fields = []
header = self.csv_data[0]
fields_name = [self.field_verbose_name_mapping.get(v) for v in header]
for name in fields_name:
if name in self.get_define_fields():
fields.append(self.get_field(name))
else:
fields.append(None)
return fields
def create_or_update(self):
stream = self.get_stream()
csv_data = self.get_csv_data(stream)
cleaned_fields = self.cleaned_post_fields()
class ModelImportView(ModelImportPostMixin):
def post(self, request, *args, **kwargs):
return self.create_or_update()
class ModelExportView(ModelExportPostMixin, ModelExportGetMixin, View):
model = None
filename_prefix = 'jumpserver'
def post(self, request, *args, **kwargs):
return self.get_redirect_response()
def get(self, request, *args, **kwargs):
self.request = request
response = self.make_csv_response()
return response
......@@ -1067,13 +1067,13 @@ msgstr "配置"
msgid "Location"
msgstr "位置"
#: assets/templates/assets/delete_confirm.html:6
#: perms/templates/perms/delete_confirm.html:6
#: assets/templates/delete_confirm.html:6
#: perms/templates/delete_confirm.html:6
#: users/templates/users/user_delete_confirm.html:6
msgid "Confirm delete"
msgstr "确认删除"
#: assets/templates/assets/delete_confirm.html:11
#: assets/templates/delete_confirm.html:11
msgid "Are you sure delete"
msgstr "您确定删除吗?"
......
......@@ -19,7 +19,7 @@ from .hands import AdminUserRequiredMixin, User, UserGroup, SystemUser, \
Asset, AssetGroup
from .models import AssetPermission
from .forms import AssetPermissionForm
from .utils import associate_system_users_and_assets
# from .utils import associate_system_users_and_assets
class AssetPermissionListView(AdminUserRequiredMixin, ListView):
......@@ -87,15 +87,15 @@ class AssetPermissionCreateView(AdminUserRequiredMixin,
'successfully.'.format(url=url, name=self.object.name))
return success_message
def form_valid(self, form):
assets = form.cleaned_data['assets']
asset_groups = form.cleaned_data['asset_groups']
system_users = form.cleaned_data['system_users']
associate_system_users_and_assets(system_users, assets, asset_groups)
response = super(AssetPermissionCreateView, self).form_valid(form)
self.object.created_by = self.request.user.name
self.object.save()
return response
# Todo: When create push system user
# def form_valid(self, form):
# assets = form.cleaned_data['assets']
# asset_groups = form.cleaned_data['asset_groups']
# system_users = form.cleaned_data['system_users']
# response = super(AssetPermissionCreateView, self).form_valid(form)
# self.object.created_by = self.request.user.name
# self.object.save()
# return response
class AssetPermissionUpdateView(AdminUserRequiredMixin, UpdateView):
......@@ -150,7 +150,7 @@ class AssetPermissionDetailView(AdminUserRequiredMixin, DetailView):
class AssetPermissionDeleteView(AdminUserRequiredMixin, DeleteView):
model = AssetPermission
template_name = 'perms/delete_confirm.html'
template_name = 'delete_confirm.html'
success_url = reverse_lazy('perms:asset-permission-list')
......
{% load i18n %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{% trans 'Confirm delete' %}</title>
</head>
<body>
<form action="" method="post">
{% csrf_token %}
<p>{% trans 'Are you sure delete' %} <b>{{ object.name }} </b> ?</p>
<input type="submit" value="Confirm" />
</form>
</body>
</html>
\ No newline at end of file
......@@ -2,7 +2,7 @@
{% load i18n %}
{% load static %}
{% load common_tags %}
{% block content_left_head %}
{% block custom_head_css_js %}
<link href="{% static "css/plugins/footable/footable.core.css" %}" rel="stylesheet">
<link href="{% static 'css/plugins/datepicker/datepicker3.css' %}" rel="stylesheet">
<style>
......@@ -12,6 +12,10 @@
</style>
{% endblock %}
{% block content_left_head %}
123
{% endblock %}
{% block table_search %}
<form id="search_form" method="get" action="" class="pull-right form-inline">
<div class="form-group" id="date">
......@@ -26,7 +30,7 @@
<select class="select2 form-control" name="username">
<option value="">{% trans 'User' %}</option>
{% for u in user_list %}
<option value="{{ u }}" {% if user == u %} selected {% endif %}>{{ u }}</option>
<option value="{{ u }}" {% if u == username %} selected {% endif %}>{{ u }}</option>
{% endfor %}
</select>
</div>
......
......@@ -2,7 +2,7 @@
{% load i18n %}
{% load static %}
{% load terminal_tags %}
{% block content_left_head %}
{% block custom_head_css_js %}
<link href="{% static 'css/plugins/datepicker/datepicker3.css' %}" rel="stylesheet">
<style>
#search_btn {
......@@ -11,6 +11,9 @@
</style>
{% endblock %}
{% block content_left_head %}
{% endblock %}
{% block table_search %}
<form id="search_form" method="get" action="" class="pull-right form-inline">
......@@ -26,7 +29,7 @@
<select class="select2 form-control" name="user">
<option value="">{% trans 'User' %}</option>
{% for u in user_list %}
<option value="{{ u }}" {% if u == user %} selected {% endif %}>{{ u }}</option>
<option value="{{ u }}" {% if u == username %} selected {% endif %}>{{ u }}</option>
{% endfor %}
</select>
</div>
......@@ -106,10 +109,17 @@
function success() {
window.setTimeout(function () {
window.location.reload()
}, 300)
}, 1000)
}
var success_message = '{% trans "Terminate task send, waiting ..." %}';
var the_url = "{% url 'api-terminal:tasks-list' %}";
APIUpdateAttr({url: the_url, method: 'POST', body: JSON.stringify(data), success: success, success_message: 'Terminate success'});
APIUpdateAttr({
url: the_url,
method: 'POST',
body: JSON.stringify(data),
success: success,
success_message: success_message
});
}
$(document).ready(function() {
$('table').DataTable({
......@@ -118,7 +128,9 @@
"bInfo" : false,
"order": []
});
$('.select2').select2();
$('.select2').select2({
dropdownAutoWidth: true
});
$('#date .input-daterange').datepicker({
dateFormat: 'mm/dd/yy',
keyboardNavigation: false,
......@@ -135,12 +147,6 @@
terminal: terminal_id
};
terminateSession(data)
}).on('click', '#btn_bulk_update', function () {
var data = [];
$('.cbx-term:checked').each(function () {
data.push({proxy_log_id: $(this).attr('value')})
});
terminateSession(data)
})
</script>
{% endblock %}
......
......@@ -2,7 +2,6 @@
{% load i18n static %}
{% block custom_head_css_js %}
{{ block.super }}
<style>
div.dataTables_wrapper div.dataTables_filter,
.dataTables_length {
......@@ -15,9 +14,10 @@
#modal .modal-body { max-height: 200px; }
</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="terminal_list_table" >
<thead>
<tr>
......@@ -40,12 +40,11 @@
</tbody>
</table>
{% include 'terminal/terminal_modal_accept.html' %}
{% endblock %}
{% block custom_foot_js %}
<script src="{% static 'js/jquery.form.min.js' %}"></script>
<script>
$(document).ready(function(){
function initTable() {
var options = {
ele: $('#terminal_list_table'),
buttons: [],
......@@ -100,8 +99,11 @@ $(document).ready(function(){
op_html: $('#actions').html()
};
jumpserver.initDataTable(options);
}
$(document).ready(function(){
initTable();
$('#btn_terminal_accept').click(function () {
}).on('click', '#btn-confirm',function () {
var $form = $('#form_terminal_accept');
function success(data, textStatus, jqXHR) {
if (data.success === true) {
......@@ -111,8 +113,6 @@ $(document).ready(function(){
}
}
$form.ajaxSubmit({success: success});
})
}).on('click', '.btn-del', function(){
var $this = $(this);
var id = $this.data('id');
......@@ -124,8 +124,7 @@ $(document).ready(function(){
var $this = $(this);
var terminal_id = $this.data('id');
var the_url = "{% url 'api-terminal:terminal-detail' pk=DEFAULT_PK %}".replace('{{ DEFAULT_PK }}', terminal_id);
var post_url = $('#form_terminal_accept').attr('action').replace('{{ DEFAULT_PK }}', terminal_id);
console.log(post_url);
var post_url = "{% url 'terminal:terminal-accept' pk=DEFAULT_PK %}".replace('{{ DEFAULT_PK }}', terminal_id);
$.ajax({
url: the_url,
method: 'GET',
......
......@@ -5,7 +5,7 @@
{% block modal_title%}{% trans "Accept terminal registration" %}{% endblock %}
{% block modal_body %}
{% load bootstrap3 %}
<form action="{% url 'terminal:terminal-accept' pk="{{ DEFAULT_PK }}" %}" method="post" class="form-horizontal" id="form_terminal_accept" enctype="multipart/form-data">
<form action="" method="post" class="form-horizontal" id="form_terminal_accept" enctype="multipart/form-data">
{% csrf_token %}
<p class="alert alert-danger" id="modal-error" style="display: none"></p>
{% bootstrap_field form.name layout="horizontal" %}
......@@ -16,4 +16,4 @@
</form>
{% endblock %}
{% block modal_confirm_id %}btn_terminal_accept{% endblock %}
\ No newline at end of file
{% block modal_confirm_id %}btn-confirm{% endblock %}
\ No newline at end of file
......@@ -10,8 +10,8 @@ from .. import api
app_name = 'terminal'
router = routers.DefaultRouter()
router.register(r'v1/terminal/(?P<terminal>[0-9]+)?/?status', api.StatusViewSet, 'terminal-status')
router.register(r'v1/terminal/(?P<terminal>[0-9]+)?/?sessions', api.SessionViewSet, 'terminal-sessions')
router.register(r'v1/terminal/(?P<terminal>[a-zA-Z0-9\-]{36})?/?status', api.StatusViewSet, 'terminal-status')
router.register(r'v1/terminal/(?P<terminal>a-zA-Z0-9\-]{36})?/?sessions', api.SessionViewSet, 'terminal-sessions')
router.register(r'v1/tasks', api.TaskViewSet, 'tasks')
router.register(r'v1/terminal', api.TerminalViewSet, 'terminal')
router.register(r'v1/command', api.CommandViewSet, 'command')
......
......@@ -70,7 +70,7 @@ class CommandListView(ListView):
'command': self.command,
'date_from': self.date_from_s,
'date_to': self.date_to_s,
'user': self.user,
'username': self.user,
'asset': self.asset,
'system_user': self.system_user,
}
......
......@@ -78,7 +78,7 @@ class SessionListView(AdminUserRequiredMixin, ListView):
'system_user_list': utils.get_system_user_list_from_cache(),
'date_from': self.date_from_s,
'date_to': self.date_to_s,
'user': self.user,
'username': self.user,
'asset': self.asset,
'system_user': self.system_user,
}
......@@ -122,6 +122,7 @@ class SessionOfflineListView(SessionListView):
class SessionDetailView(SingleObjectMixin, ListView):
template_name = 'terminal/session_detail.html'
model = Session
object = None
def get_queryset(self):
self.object = self.get_object()
......
......@@ -62,14 +62,14 @@ class TerminalDetailView(LoginRequiredMixin, DetailView):
class TerminalDeleteView(AdminUserRequiredMixin, DeleteView):
model = Terminal
template_name = 'assets/delete_confirm.html'
template_name = 'delete_confirm.html'
success_url = reverse_lazy('terminal:terminal-list')
class TerminalAcceptView(AdminUserRequiredMixin, JSONResponseMixin, UpdateView):
model = Terminal
form_class = TerminalForm
template_name = 'Terminal/terminal_modal_test.html'
template_name = 'terminal/terminal_modal_accept.html'
def form_valid(self, form):
terminal = form.save()
......
......@@ -49,5 +49,4 @@ class LoginLog(models.Model):
datetime = models.DateTimeField(auto_now_add=True, verbose_name=_('Date login'))
class Meta:
db_table = 'login_log'
ordering = ['-datetime', 'username']
......@@ -38,9 +38,9 @@ class User(AbstractUser):
phone = models.CharField(max_length=20, blank=True, null=True, verbose_name=_('Phone'))
enable_otp = models.BooleanField(default=False, verbose_name=_('Enable OTP'))
secret_key_otp = models.CharField(max_length=16, blank=True)
# Todo: private_key may be not used
_private_key = models.CharField(max_length=5000, blank=True, verbose_name=_('ssh private key'))
_public_key = models.CharField(max_length=5000, blank=True, verbose_name=_('ssh public key'))
# Todo: Auto generate key, let user download
_private_key = models.CharField(max_length=5000, blank=True, verbose_name=_('Private key'))
_public_key = models.CharField(max_length=5000, blank=True, verbose_name=_('Public key'))
comment = models.TextField(max_length=200, blank=True, verbose_name=_('Comment'))
is_first_login = models.BooleanField(default=False)
date_expired = models.DateTimeField(default=date_expired_default, blank=True, null=True, verbose_name=_('Date expired'))
......
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