mirror of https://github.com/jumpserver/jumpserver
[Bugfix] 修改了一些issue上的bug
parent
948214cacb
commit
0869931e67
|
@ -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
|
||||
|
|
|
@ -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 }}
|
||||
|
|
|
@ -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 %}
|
|
@ -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>
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -109,7 +109,6 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
{% block custom_foot_js %}
|
||||
|
|
|
@ -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})
|
||||
|
||||
|
||||
|
|
|
@ -84,6 +84,7 @@
|
|||
$(document).ready(function() {
|
||||
$('table').DataTable({
|
||||
"searching": false,
|
||||
"bInfo" : false,
|
||||
"paging": false,
|
||||
"order": []
|
||||
});
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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 %}
|
|
@ -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 %}
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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 %}
|
||||
|
|
|
@ -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 }}
|
||||
|
|
|
@ -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 %}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 = []
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue