[Bugfix] 修改了一些issue上的bug

pull/530/head
ibuler 2017-01-20 20:13:22 +08:00
parent 948214cacb
commit 0869931e67
23 changed files with 283 additions and 233 deletions

View File

@ -6,23 +6,6 @@ from .models import IDC, Asset, AssetGroup, AdminUser, SystemUser, Tag
from common.utils import validate_ssh_private_key, ssh_pubkey_gen
# class AssetForm(forms.ModelForm):
# class Meta:
# model = Asset
#
# fields = [
# 'ip', 'other_ip', 'remote_card_ip', 'hostname', 'port', 'groups', 'username', 'password',
# 'idc', 'mac_address', 'brand', 'cpu', 'memory', 'disk', 'os', 'cabinet_no', 'cabinet_pos',
# 'number', 'status', 'type', 'env', 'sn', 'is_active', 'comment', 'admin_user', 'system_users'
# ]
#
# widgets = {
# 'groups': forms.SelectMultiple(attrs={'class': 'select2-groups', 'data-placeholder': _('Select asset groups')}),
# 'system_user': forms.SelectMultiple(attrs={'class': 'select2-system-user', 'data-placeholder': _('Select asset system user')}),
# 'admin_user': forms.SelectMultiple(attrs={'class': 'select2-admin-user', 'data-placeholder': _('Select asset admin user')}),
# }
#
class AssetCreateForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
instance = kwargs.get('instance', None)
@ -142,15 +125,19 @@ class IDCForm(forms.ModelForm):
class AdminUserForm(forms.ModelForm):
# Admin user assets define, let user select, save it in form not in view
assets = forms.ModelMultipleChoiceField(queryset=Asset.objects.all(),
label=_('Asset'),
required=False,
widget=forms.SelectMultiple(
attrs={'class': 'select2', 'data-placeholder': _('Select assets')})
)
assets = forms.ModelMultipleChoiceField(
queryset=Asset.objects.all(),
label=_('Asset'),
required=False,
widget=forms.SelectMultiple(
attrs={'class': 'select2', 'data-placeholder': _('Select assets')})
)
# Form field name can not start with `_`, so redefine it,
password = forms.CharField(widget=forms.PasswordInput, max_length=100, min_length=8, strip=True,
help_text=_('If also set private key, use that first'), required=False)
password = forms.CharField(
widget=forms.PasswordInput, max_length=100,
min_length=8, strip=True, required=False,
help_text=_('If also set private key, use that first'),
)
# Need use upload private key file except paste private key content
private_key_file = forms.FileField(required=False)
@ -173,11 +160,11 @@ class AdminUserForm(forms.ModelForm):
admin_user = super(AdminUserForm, self).save(commit=commit)
password = self.cleaned_data['password']
private_key = self.cleaned_data['private_key_file']
public_key = ssh_pubkey_gen(private_key)
if password:
admin_user.password = password
if private_key:
public_key = ssh_pubkey_gen(private_key)
admin_user.private_key = private_key
admin_user.public_key = public_key
admin_user.save()
@ -196,8 +183,9 @@ class AdminUserForm(forms.ModelForm):
password = self.cleaned_data['password']
private_key_file = self.cleaned_data.get('private_key_file', '')
if not (password or private_key_file):
raise forms.ValidationError(_('Password and private key file must be input one'))
if not self.instance and not (password or private_key_file):
raise forms.ValidationError(
_('Password and private key file must be input one'))
class Meta:
model = AdminUser

View File

@ -27,6 +27,11 @@
</div>
</div>
<div class="ibox-content">
{% if form.non_field_errors %}
<div class="alert alert-danger">
{{ form.non_field_errors }}
</div>
{% endif %}
<form enctype="multipart/form-data" method="post" class="form-horizontal" action="" >
{% csrf_token %}
{{ form.name|bootstrap_horizontal }}

View File

@ -21,6 +21,11 @@
<li class="pull-right">
<a class="btn btn-outline btn-default" href="{% url 'assets:admin-user-update' pk=admin_user.id %}"><i class="fa fa-edit"></i>Update</a>
</li>
<li class="pull-right">
<a class="btn btn-outline btn-danger btn-delete-admin-user">
<i class="fa fa-edit"></i>Delete
</a>
</li>
</ul>
</div>
<div class="tab-content">
@ -311,6 +316,38 @@ $(document).ready(function () {
op_html: $('#actions').html()
};
jumpserver.initDataTable(options);
function adminUserDelete(name, url) {
function doDelete() {
var body = {};
var success = function() {
swal('Deleted!', "[ "+name+"]"+" has been deleted ", "success");
window.location.href="{% url 'assets:idc-list' %}";
};
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 + "] ",
type: "warning",
showCancelButton: true,
cancelButtonText: 'Cancel',
confirmButtonColor: "#DD6B55",
confirmButtonText: 'Confirm',
closeOnConfirm: false
}, function () {
doDelete()
});
}
})
.on('click', '.btn-replace-asset-admin_user', function () {
@ -392,8 +429,14 @@ $(document).ready(function () {
var data = {"assets": assets};
objectRemove($this, name, the_url, data);
}
});
});
})
}).on('click', '.btn-delete-admin-user', function () {
var $this = $(this);
var name = "{{ admin_user.name }}";
var uid = "{{ admin_user.id }}";
var the_url = '{% url "api-assets:admin-user-detail" pk=99991937 %}'.replace('99991937', uid);
var redirect_url = "{% url 'assets:admin-user-list' %}";
objectDelete($this, name, the_url, redirect_url);
})
</script>
{% endblock %}

View File

@ -16,7 +16,6 @@
<div class="panel-options">
<ul class="nav nav-tabs">
<li class="active"><a href="" class="text-center"><i class="fa fa-laptop"></i> {% trans 'Detail' %} </a></li>
<li><a href="" class="text-center"><i class="fa fa-bar-chart-o"></i> {% trans 'Asset group perm' %}</a></li>
<li class="pull-right">
<a class="btn btn-outline btn-default" href="{% url 'assets:asset-group-update' pk=asset_group.id %}"><i class="fa fa-edit"></i>Update</a>
</li>

View File

@ -38,12 +38,12 @@
{{ form.address|bootstrap_horizontal }}
{{ form.contact|bootstrap_horizontal }}
{{ form.phone|bootstrap_horizontal }}
<div class="hr-line-dashed"></div>
<h3 class="widget-head-color-box">IP段</h3>
{{ form.intranet|bootstrap_horizontal }}
{{ form.extranet|bootstrap_horizontal }}
<div class="hr-line-dashed"></div>
<div class="form-group">
<div class="col-sm-4 col-sm-offset-5">

View File

@ -109,7 +109,6 @@
</div>
</div>
</div>
</div>
{% endblock %}
{% block custom_foot_js %}

View File

@ -412,35 +412,20 @@ class IDCDeleteView(AdminUserRequiredMixin, DeleteView):
class AdminUserListView(AdminUserRequiredMixin, TemplateView):
model = AdminUser
# paginate_by = settings.CONFIG.DISPLAY_PER_PAGE
# context_object_name = 'admin_user_list'
template_name = 'assets/admin_user_list.html'
def get_context_data(self, **kwargs):
context = {
'app': _('Assets'),
'action': _('Admin user list'),
# 'keyword': self.request.GET.get('keyword', '')
}
kwargs.update(context)
return super(AdminUserListView, self).get_context_data(**kwargs)
# def get_queryset(self):
# Todo: Default order by lose asset connection num
# self.queryset = super(AdminUserListView, self).get_queryset()
# self.keyword = keyword = self.request.GET.get('keyword', '')
# self.sort = sort = self.request.GET.get('sort', '-date_created')
#
# if keyword:
# self.queryset = self.queryset.filter(Q(name__icontains=keyword) |
# Q(comment__icontains=keyword))
#
# if sort:
# self.queryset = self.queryset.order_by(sort)
# return self.queryset
class AdminUserCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateView):
class AdminUserCreateView(AdminUserRequiredMixin,
SuccessMessageMixin,
CreateView):
model = AdminUser
form_class = forms.AdminUserForm
template_name = 'assets/admin_user_create_update.html'
@ -455,11 +440,12 @@ class AdminUserCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateVie
return super(AdminUserCreateView, self).get_context_data(**kwargs)
def get_success_message(self, cleaned_data):
success_message = _('Create admin user <a href="%s">%s</a> successfully.' %
(
reverse_lazy('assets:admin-user-detail', kwargs={'pk': self.object.pk}),
self.object.name,
))
success_message = _(
'Create admin user <a href="%s">%s</a> successfully.' % (
reverse_lazy('assets:admin-user-detail',
kwargs={'pk': self.object.pk}),
self.object.name,
))
return success_message
def form_invalid(self, form):
@ -480,7 +466,8 @@ class AdminUserUpdateView(AdminUserRequiredMixin, UpdateView):
return super(AdminUserUpdateView, self).get_context_data(**kwargs)
def get_success_url(self):
success_url = reverse_lazy('assets:admin-user-detail', pk=self.object.pk)
success_url = reverse_lazy('assets:admin-user-detail',
kwargs={'pk': self.object.pk})
return success_url
@ -787,7 +774,7 @@ class AssetExportView(View):
return HttpResponse('Json object not valid', status=400)
spm = uuid.uuid4().get_hex()
cache.set(spm, assets_id, 300)
url = reverse('assets:asset-export') + '?spm=%s' % spm
url = reverse_lazy('assets:asset-export') + '?spm=%s' % spm
return JsonResponse({'redirect': url})

View File

@ -84,6 +84,7 @@
$(document).ready(function() {
$('table').DataTable({
"searching": false,
"bInfo" : false,
"paging": false,
"order": []
});

View File

@ -24,17 +24,17 @@
</div>
<div class="input-group">
<select class="select2 form-control" name="username">
<option value="">{% trans 'Select user' %}</option>
<option value="">{% trans 'User' %}</option>
{% for user in user_list %}
<option value="{{ user.username }}" {% if user.username == username %} selected {% endif %}>{{ user.username }}</option>
<option value="{{ user }}" {% if user == username %} selected {% endif %}>{{ user }}</option>
{% endfor %}
</select>
</div>
<div class="input-group">
<select class="select2 form-control" name="ip">
<option value="">{% trans 'Select asset' %}</option>
<option value="">{% trans 'Asset' %}</option>
{% for asset in asset_list %}
<option value="{{ asset.ip }}" {% if asset.ip == ip %} selected {% endif %}>{{ asset.ip }}</option>
<option value="{{ asset }}" {% if asset == ip %} selected {% endif %}>{{ asset }}</option>
{% endfor %}
</select>
</div>
@ -42,12 +42,12 @@
<select class="select2 form-control" name="system_user">
<option value="">{% trans 'System user' %}</option>
{% for su in system_user_list %}
<option value="{{ su.username }}" {% if su.username == system_user %} selected {% endif %}>{{ su.username }}</option>
<option value="{{ su }}" {% if su == system_user %} selected {% endif %}>{{ su }}</option>
{% endfor %}
</select>
</div>
<div class="input-group">
<input type="text" class="form-control input-sm" name="keyword" placeholder="Search" value="{{ keyword }}">
<input type="text" class="form-control input-sm" name="keyword" placeholder="Command" value="{{ keyword }}">
</div>
<div class="input-group">
<div class="input-group-btn">
@ -108,6 +108,7 @@
$('table').DataTable({
"searching": false,
"paging": false,
"bInfo" : false,
"order": []
});
$('.select2').select2();

View File

@ -18,6 +18,9 @@ class ProxyLogListView(AdminUserRequiredMixin, ListView):
model = ProxyLog
template_name = 'audits/proxy_log_list.html'
context_object_name = 'proxy_log_list'
paginate_by = settings.CONFIG.DISPLAY_PER_PAGE
keyword = username = ip = system_user = date_from_s = date_to_s = ''
def get_queryset(self):
date_now = timezone.localtime(timezone.now())
@ -46,20 +49,24 @@ class ProxyLogListView(AdminUserRequiredMixin, ListView):
if system_user:
self.queryset = self.queryset.filter(system_user=system_user)
if keyword:
self.queryset = self.queryset.filter(Q(username__contains=keyword) |
Q(name__icontains=keyword) |
Q(hostname__icontains=keyword) |
Q(ip__icontains=keyword) |
Q(system_user__icontains=keyword)).distinct()
self.queryset = self.queryset.filter(
Q(username__contains=keyword) |
Q(name__icontains=keyword) |
Q(hostname__icontains=keyword) |
Q(ip__icontains=keyword) |
Q(system_user__icontains=keyword)).distinct()
return self.queryset
def get_context_data(self, **kwargs):
context = {
'app': _('Audits'),
'action': _('Proxy log list'),
'user_list': User.objects.all().order_by('username'),
'asset_list': Asset.objects.all().order_by('ip'),
'system_user_list': SystemUser.objects.all().order_by('name'),
'user_list': set(
list(ProxyLog.objects.values_list('username', flat=True))),
'asset_list': set(
list(ProxyLog.objects.values_list('ip', flat=True))),
'system_user_list': set(
list(ProxyLog.objects.values_list('system_user', flat=True))),
'keyword': self.keyword,
'date_from': self.date_from_s,
'date_to': self.date_to_s,
@ -71,9 +78,12 @@ class ProxyLogListView(AdminUserRequiredMixin, ListView):
return super(ProxyLogListView, self).get_context_data(**kwargs)
class ProxyLogDetailView(AdminUserRequiredMixin, SingleObjectMixin, ListView):
class ProxyLogDetailView(AdminUserRequiredMixin,
SingleObjectMixin,
ListView):
template_name = 'audits/proxy_log_detail.html'
context_object_name = 'proxy_log'
object = ''
def get(self, request, *args, **kwargs):
self.object = self.get_object(queryset=ProxyLog.objects.all())
@ -91,12 +101,16 @@ class ProxyLogDetailView(AdminUserRequiredMixin, SingleObjectMixin, ListView):
return super(ProxyLogDetailView, self).get_context_data(**kwargs)
class ProxyLogCommandsListView(AdminUserRequiredMixin, SingleObjectMixin, ListView):
class ProxyLogCommandsListView(AdminUserRequiredMixin,
SingleObjectMixin,
ListView):
template_name = 'audits/proxy_log_commands_list_modal.html'
object = ''
def get(self, request, *args, **kwargs):
self.object = self.get_object(queryset=ProxyLog.objects.all())
return super(ProxyLogCommandsListView, self).get(request, *args, **kwargs)
return super(ProxyLogCommandsListView, self).\
get(request, *args, **kwargs)
def get_queryset(self):
return list(self.object.command_log.all())
@ -107,6 +121,7 @@ class CommandLogListView(AdminUserRequiredMixin, ListView):
template_name = 'audits/command_log_list.html'
paginate_by = settings.CONFIG.DISPLAY_PER_PAGE
context_object_name = 'command_list'
keyword = username = ip = system_user = date_from_s = date_to_s = ''
def get_queryset(self):
date_now = timezone.localtime(timezone.now())
@ -114,29 +129,30 @@ class CommandLogListView(AdminUserRequiredMixin, ListView):
seven_days_ago_s = (date_now-timezone.timedelta(7)).strftime('%m/%d/%Y')
self.queryset = super(CommandLogListView, self).get_queryset()
self.keyword = keyword = self.request.GET.get('keyword', '')
self.sort = sort = self.request.GET.get('sort', '-datetime')
self.username = username = self.request.GET.get('username', '')
self.ip = ip = self.request.GET.get('ip', '')
self.system_user = system_user = self.request.GET.get('system_user', '')
self.date_from_s = date_from_s = self.request.GET.get('date_from', '%s' % seven_days_ago_s)
self.date_to_s = date_to_s = self.request.GET.get('date_to', '%s' % now_s)
self.date_from_s = date_from_s = \
self.request.GET.get('date_from', '%s' % seven_days_ago_s)
self.date_to_s = date_to_s = \
self.request.GET.get('date_to', '%s' % now_s)
if date_from_s:
date_from = timezone.datetime.strptime(date_from_s, '%m/%d/%Y')
self.queryset = self.queryset.filter(datetime__gt=date_from)
if date_to_s:
date_to = timezone.datetime.strptime(date_to_s + ' 23:59:59', '%m/%d/%Y %H:%M:%S')
date_to = timezone.datetime.strptime(
date_to_s + ' 23:59:59', '%m/%d/%Y %H:%M:%S')
self.queryset = self.queryset.filter(datetime__lt=date_to)
if username:
self.queryset = self.queryset.filter(proxy_log__username=username)
if ip:
self.queryset = self.queryset.filter(proxy_log__ip=ip)
if system_user:
self.queryset = self.queryset.filter(proxy_log__system_user=system_user)
self.queryset = self.queryset.filter(
proxy_log__system_user=system_user)
if keyword:
self.queryset = self.queryset.filter(command=keyword)
if sort:
self.queryset = self.queryset.order_by(sort)
return self.queryset
def get_context_data(self, **kwargs):
@ -159,31 +175,39 @@ class CommandLogListView(AdminUserRequiredMixin, ListView):
class LoginLogListView(AdminUserRequiredMixin, ListView):
model = LoginLog
paginate_by = settings.CONFIG.DISPLAY_PER_PAGE
template_name = 'audits/login_log_list.html'
context_object_name = 'login_log_list'
keyword = username = date_from_s = date_to_s = ''
def get_queryset(self):
date_now = timezone.localtime(timezone.now())
now_s = date_now.strftime('%m/%d/%Y')
seven_days_ago_s = (date_now - timezone.timedelta(7)).strftime('%m/%d/%Y')
seven_days_ago_s = (date_now - timezone.timedelta(7))\
.strftime('%m/%d/%Y')
self.queryset = super(LoginLogListView, self).get_queryset()
self.keyword = keyword = self.request.GET.get('keyword', '')
self.username = username = self.request.GET.get('username', '')
self.date_from_s = date_from_s = self.request.GET.get('date_from', '%s' % seven_days_ago_s)
self.date_to_s = date_to_s = self.request.GET.get('date_to', '%s' % now_s)
self.date_from_s = date_from_s = self.request.GET.get(
'date_from', '%s' % seven_days_ago_s)
self.date_to_s = date_to_s = self.request.GET.get(
'date_to', '%s' % now_s)
if date_from_s:
date_from = timezone.datetime.strptime(date_from_s, '%m/%d/%Y')
self.queryset = self.queryset.filter(date_login__gt=date_from)
if date_to_s:
date_to = timezone.datetime.strptime(date_to_s + ' 23:59:59', '%m/%d/%Y %H:%M:%S')
date_to = timezone.datetime.strptime(
date_to_s + ' 23:59:59', '%m/%d/%Y %H:%M:%S')
self.queryset = self.queryset.filter(date_login__lt=date_to)
if username:
self.queryset = self.queryset.filter(username=username)
if keyword:
self.queryset = self.queryset.filter(Q(username__contains=keyword) |
Q(name__icontains=keyword) |
Q(login_ip=keyword)).distinct()
self.queryset = self.queryset.filter(
Q(username__contains=keyword) |
Q(name__icontains=keyword) |
Q(login_ip=keyword)).distinct()
return self.queryset
def get_context_data(self, **kwargs):

View File

@ -26,16 +26,21 @@ class AssetPermissionForm(forms.ModelForm):
'system_users', 'is_active', 'date_expired', 'comment',
]
widgets = {
'users': forms.SelectMultiple(attrs={'class': 'select2',
'data-placeholder': _('Select users')}),
'user_groups': forms.SelectMultiple(attrs={'class': 'select2',
'data-placeholder': _('Select user groups')}),
'assets': forms.SelectMultiple(attrs={'class': 'select2',
'data-placeholder': _('Select assets')}),
'asset_groups': forms.SelectMultiple(attrs={'class': 'select2',
'data-placeholder': _('Select asset groups')}),
'system_users': forms.SelectMultiple(attrs={'class': 'select2',
'data-placeholder': _('Select system users')}),
'users': forms.SelectMultiple(
attrs={'class': 'select2',
'data-placeholder': _('Select users')}),
'user_groups': forms.SelectMultiple(
attrs={'class': 'select2',
'data-placeholder': _('Select user groups')}),
'assets': forms.SelectMultiple(
attrs={'class': 'select2',
'data-placeholder': _('Select assets')}),
'asset_groups': forms.SelectMultiple(
attrs={'class': 'select2',
'data-placeholder': _('Select asset groups')}),
'system_users': forms.SelectMultiple(
attrs={'class': 'select2',
'data-placeholder': _('Select system users')}),
}
help_texts = {
'name': '* required',

View File

@ -31,6 +31,11 @@
<li class="pull-right">
<a class="btn btn-outline btn-default" href="{% url 'perms:asset-permission-update' pk=asset_permission.id %}"><i class="fa fa-edit"></i>Update</a>
</li>
<li class="pull-right">
<a class="btn btn-outline btn-danger btn-delete-perm">
<i class="fa fa-edit"></i>Delete
</a>
</li>
</ul>
</div>
<div class="tab-content">
@ -191,25 +196,15 @@
{% endblock %}
{% block custom_foot_js %}
<script>
{# function switch_user_status(obj) {#}
{# var status = $(obj).prop('checked');#}
{##}
{# $.ajax({#}
{# url: "{% url 'users:user-active-api' pk=user.id %}",#}
{# type: "PUT",#}
{# data: {#}
{# 'is_active': status#}
{# },#}
{# success: function (data, status) {#}
{# console.log(data)#}
{# },#}
{# error: function () {#}
{# console.log('error')#}
{# }#}
{# })#}
{# }#}
$(document).ready(function () {
$('.select2').select2();
});
}).on('click', '.btn-delete-perm', function () {
var $this = $(this);
var name = "{{ asset_permission.name }}";
var uid = "{{ asset_permission.id }}";
var the_url = '{% url "api-perms:asset-permission-detail" pk=99991937 %}'.replace('99991937', uid);
var redirect_url = "{% url 'perms:asset-permission-list' %}";
objectDelete($this, name, the_url, redirect_url);
})
</script>
{% endblock %}

View File

@ -2,7 +2,9 @@
{% load i18n %}
{% load common_tags %}
{% block content_left_head %}
<a href="{% url 'perms:asset-permission-create' %}" class="btn btn-sm btn-primary "> {% trans "Create permission" %} </a>
<a href="{% url 'perms:asset-permission-create' %}" class="btn btn-sm btn-primary">
{% trans "Create permission" %}
</a>
{% endblock %}
{% block table_head %}
@ -39,8 +41,12 @@
{% endif %}
</td>
<td class="text-center">
<a href="{% url 'perms:asset-permission-update' pk=asset_permission.id %}" class="btn btn-xs btn-info">{% trans 'Update' %}</a>
<a href="{% url 'perms:asset-permission-delete' pk=asset_permission.id %}" class="btn btn-xs btn-danger del">{% trans 'Delete' %}</a>
<a href="{% url 'perms:asset-permission-update' pk=asset_permission.id %}"
class="btn btn-xs btn-info">{% trans 'Update' %}
</a>
<a class="btn btn-xs btn-danger btn-del" data-name="{{ asset_permission.name }}"
data-uid="{{ asset_permission.id }}">{% trans 'Delete' %}
</a>
</td>
</tr>
{% endfor %}
@ -54,6 +60,13 @@
"paging": false,
"order": []
})
}).on('click', '.btn-del', function () {
var $this = $(this);
var name = $this.data('name');
var uid = $this.data('uid');
var the_url = '{% url "api-perms:asset-permission-detail" pk=99991937 %}'
.replace('99991937', uid);
objectDelete($this, name, the_url);
})
</script>
{% endblock %}

View File

@ -213,12 +213,16 @@ function APIUpdateAttr(props) {
}
// Sweet Alert for Delete
function objectDelete(obj, name, url) {
function objectDelete(obj, name, url, redirectTo) {
function doDelete() {
var body = {};
var success = function() {
swal('Deleted!', "[ "+name+"]"+" has been deleted ", "success");
$(obj).parent().parent().remove();
if (!redirectTo) {
$(obj).parent().parent().remove();
} else {
window.location.href=redirectTo;
}
};
var fail = function() {
swal("Failed", "Delete"+"[ "+name+" ]"+"failed", "error");

View File

@ -2,7 +2,7 @@
{% if is_paginated %}
<div class="col-sm-4">
<div class="dataTables_info text-center" id="editable_info" role="status" aria-live="polite">
{{ page_obj.start_index }} - {{ page_obj.end_index }} of {{ paginator.count }}
显示第 {{ page_obj.start_index }} 至 {{ page_obj.end_index }} 项结果,共 {{ paginator.count }} 项
</div>
</div>
<div class="col-sm-4">

View File

@ -33,8 +33,9 @@ class UserGroup(NoDeleteModelMixin, Group):
@classmethod
def initial(cls):
group, created = cls.objects.get_or_create(name='Default', comment='Default user group for all user',
created_by='System')
group, created = cls.objects.get_or_create(
name='Default', created_by='System',
comment='Default user group for all user')
return group
@classmethod

View File

@ -3,49 +3,54 @@
{% load static %}
{% load bootstrap %}
{% block form %}
<form method="post" class="form-horizontal" action="" enctype="multipart/form-data">
{% csrf_token %}
<h3>{% trans 'Account' %}</h3>
{{ form.username|bootstrap_horizontal }}
{{ form.name|bootstrap_horizontal }}
{{ form.email|bootstrap_horizontal }}
{{ form.groups|bootstrap_horizontal }}
<div class="hr-line-dashed"></div>
{% block password %} {% endblock %}
<div class="hr-line-dashed"></div>
<h3>{% trans 'Security and Role' %}</h3>
{{ form.role|bootstrap_horizontal }}
<div class="form-group {% if form.date_expired.errors %} has-error {% endif %}" id="date_5">
<label for="{{ form.date_expired.id_for_label }}" class="col-sm-2 control-label">{{ form.date_expired.label }}</label>
<div class="col-sm-9">
<div class="input-group date">
<span class="input-group-addon"><i class="fa fa-calendar"></i></span>
<input id="{{ form.date_expired.id_for_label }}" name="{{ form.date_expired.html_name }}" type="text" class="form-control" value="{{ form.date_expired.value|date:'Y-m-d' }}">
</div>
<span class="help-block ">{{ form.date_expired.errors }}</span>
</div>
</div>
<div class="form-group">
<label for="{{ form.enable_otp.id_for_label }}" class="col-sm-2 control-label">{% trans 'Enable OTP' %}</label>
<div class="col-sm-8">
{{ form.enable_otp }}
</div>
{% if form.non_field_errors %}
<div class="alert alert-danger">
{{ form.non_field_errors }}
</div>
<div class="hr-line-dashed"></div>
<h3>{% trans 'Profile' %}</h3>
{{ form.phone|bootstrap_horizontal }}
{{ form.wechat|bootstrap_horizontal }}
{{ 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>
{% endif %}
<form method="post" class="form-horizontal" action="" enctype="multipart/form-data">
{% csrf_token %}
<h3>{% trans 'Account' %}</h3>
{{ form.username|bootstrap_horizontal }}
{{ form.name|bootstrap_horizontal }}
{{ form.email|bootstrap_horizontal }}
{{ form.groups|bootstrap_horizontal }}
<div class="hr-line-dashed"></div>
{% block password %} {% endblock %}
<div class="hr-line-dashed"></div>
<h3>{% trans 'Security and Role' %}</h3>
{{ form.role|bootstrap_horizontal }}
<div class="form-group {% if form.date_expired.errors %} has-error {% endif %}" id="date_5">
<label for="{{ form.date_expired.id_for_label }}" class="col-sm-2 control-label">{{ form.date_expired.label }}</label>
<div class="col-sm-9">
<div class="input-group date">
<span class="input-group-addon"><i class="fa fa-calendar"></i></span>
<input id="{{ form.date_expired.id_for_label }}" name="{{ form.date_expired.html_name }}" type="text" class="form-control" value="{{ form.date_expired.value|date:'Y-m-d' }}">
</div>
<span class="help-block ">{{ form.date_expired.errors }}</span>
</div>
</div>
</form>
<div class="form-group">
<label for="{{ form.enable_otp.id_for_label }}" class="col-sm-2 control-label">{% trans 'Enable OTP' %}</label>
<div class="col-sm-8">
{{ form.enable_otp }}
</div>
</div>
<div class="hr-line-dashed"></div>
<h3>{% trans 'Profile' %}</h3>
{{ form.phone|bootstrap_horizontal }}
{{ form.wechat|bootstrap_horizontal }}
{{ 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>
{% endblock %}
{% block custom_foot_js %}
<script src="{% static 'js/plugins/datepicker/bootstrap-datepicker.js' %}"></script>

View File

@ -29,6 +29,11 @@
<li class="pull-right">
<a class="btn btn-outline btn-default" href="{% url 'users:user-update' pk=user.id %}"><i class="fa fa-edit"></i>Update</a>
</li>
<li class="pull-right">
<a class="btn btn-outline btn-danger btn-delete-user">
<i class="fa fa-edit"></i>Delete
</a>
</li>
</ul>
</div>
<div class="tab-content">
@ -395,6 +400,13 @@ $(document).ready(function() {
);
};
APIUpdateAttr({ url: the_url, body: JSON.stringify(body), success: success, error: fail});
});
}).on('click', '.btn-delete-user', function () {
var $this = $(this);
var name = "{{ user.name }}";
var uid = "{{ user.id }}";
var the_url = '{% url "api-users:user-detail" pk=99991937 %}'.replace('99991937', uid);
var redirect_url = "{% url 'users:user-list' %}";
objectDelete($this, name, the_url, redirect_url);
})
</script>
{% endblock %}

View File

@ -16,6 +16,11 @@
<h5>{{ action }}</h5>
</div>
<div class="ibox-content">
{% if form.non_field_errors %}
<div class="alert alert-danger">
{{ form.non_field_errors }}
</div>
{% endif %}
<form method="post" class="form-horizontal" action="" >
{% csrf_token %}
{{ form.name|bootstrap_horizontal }}

View File

@ -30,6 +30,11 @@
<li class="pull-right">
<a class="btn btn-outline btn-default" href="{% url 'users:user-group-update' pk=user_group.id %}"><i class="fa fa-edit"></i>Update</a>
</li>
<li class="pull-right">
<a class="btn btn-outline btn-danger btn-delete-user-group">
<i class="fa fa-edit"></i>Delete
</a>
</li>
</ul>
</div>
<div class="tab-content">
@ -184,6 +189,13 @@ $(document).ready(function () {
});
console.log(users);
updateGroupMember(users)
}).on('click', '.btn-delete-user-group', function () {
var $this = $(this);
var name = "{{ user_group.name }}";
var uid = "{{ user_group.id }}";
var the_url = '{% url "api-users:user-group-detail" pk=99991937 %}'.replace('99991937', uid);
var redirect_url = "{% url 'users:user-group-list' %}";
objectDelete($this, name, the_url, redirect_url);
})
</script>
{% endblock %}

View File

@ -33,7 +33,7 @@ class AdminUserRequiredMixin(UserPassesTestMixin):
login_url = reverse_lazy('users:login')
def test_func(self):
return self.request.user.is_staff
return self.request.user.is_superuser
def user_add_success_next(user):
@ -122,61 +122,6 @@ def send_reset_ssh_key_mail(user):
send_mail_async.delay(subject, message, recipient_list, html_message=message)
# def validate_ssh_pk(text):
# """
# Expects a SSH private key as string.
# Returns a boolean and a error message.
# If the text is parsed as private key successfully,
# (True,'') is returned. Otherwise,
# (False, <message describing the error>) is returned.
#
# from https://github.com/githubnemo/SSH-private-key-validator/blob/master/validate.py
#
# """
#
# if not text:
# return False, 'No text given'
#
# startPattern = re.compile("^-----BEGIN [A-Z]+ PRIVATE KEY-----")
# optionPattern = re.compile("^.+: .+")
# contentPattern = re.compile("^([a-zA-Z0-9+/]{64}|[a-zA-Z0-9+/]{1,64}[=]{0,2})$")
# endPattern = re.compile("^-----END [A-Z]+ PRIVATE KEY-----")
#
# def contentState(text):
# for i in range(0, len(text)):
# line = text[i]
#
# if endPattern.match(line):
# if i == len(text) - 1 or len(text[i + 1]) == 0:
# return True, ''
# else:
# return False, 'At end but content coming'
#
# elif not contentPattern.match(line):
# return False, 'Wrong string in content section'
#
# return False, 'No content or missing end line'
#
# def optionState(text):
# for i in range(0, len(text)):
# line = text[i]
#
# if line[-1:] == '\\':
# return optionState(text[i + 2:])
#
# if not optionPattern.match(line):
# return contentState(text[i + 1:])
#
# return False, 'Expected option, found nothing'
# def startState(text):
# if len(text) == 0 or not startPattern.match(text[0]):
# return False, 'Header is wrong'
# return optionState(text[1:])
#
# return startState([n.strip() for n in text.splitlines()])
#
def check_user_valid(**kwargs):
password = kwargs.pop('password', None)
public_key = kwargs.pop('public_key', None)

View File

@ -118,13 +118,14 @@ class UserDetailView(AdminUserRequiredMixin, DetailView):
@method_decorator(csrf_exempt, name='dispatch')
class UserExportView(View):
def get(self, request, *args, **kwargs):
def get(self, request):
spm = request.GET.get('spm', '')
users_id = cache.get(spm)
if not users_id and not isinstance(users_id, list):
return HttpResponse('May be expired', status=404)
users = User.objects.filter(id__in=users_id)
print(users)
wb = Workbook()
ws = wb.active
ws.title = 'User'
@ -133,9 +134,12 @@ class UserExportView(View):
ws.append(header)
for user in users:
ws.append([user.name, user.username, user.email,
','.join([group.name for group in user.groups.all()]),
user.role, user.phone, user.wechat, user.comment])
print(user.name)
ws.append([
user.name, user.username, user.email,
','.join([group.name for group in user.groups.all()]),
user.role, user.phone, user.wechat, user.comment,
])
filename = 'users-{}.xlsx'.format(
timezone.localtime(timezone.now()).strftime('%Y-%m-%d_%H-%M-%S'))
@ -144,7 +148,7 @@ class UserExportView(View):
response['Content-Disposition'] = 'attachment; filename="%s"' % filename
return response
def post(self, request, *args, **kwargs):
def post(self, request):
try:
users_id = json.loads(request.body).get('users_id', [])
except ValueError:
@ -180,11 +184,13 @@ class UserBulkImportView(AdminUserRequiredMixin, JSONResponseMixin, FormView):
return self.render_json_response(data)
rows = ws.rows
header_need = ["name", 'username', 'email', 'groups', "role", "phone", "wechat", "comment"]
header_need = ["name", 'username', 'email', 'groups',
"role", "phone", "wechat", "comment"]
header = [col.value for col in next(rows)]
print(header)
if header != header_need:
data = {'valid': False, 'msg': 'Must be same format as template or export file'}
data = {'valid': False, 'msg': 'Must be same format as '
'template or export file'}
return self.render_json_response(data)
created = []

View File

@ -18,8 +18,8 @@ class Config:
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = os.environ.get('SECRET_KEY') or '2vym+ky!997d5kkcc64mnz06y1mmui3lut#(^wd=%s_qj$1%x'
# How many line display every page, default 20
DISPLAY_PER_PAGE = 20
# How many line display every page, default 25
DISPLAY_PER_PAGE = 25
# It's used to identify your site, When we send a create mail to user, we only know login url is /login/
# But we should know the absolute url like: http://jms.jumpserver.org/login/, so SITE_URL is