From 0869931e67fdf6e7ca36d6a1462b6ddeb2587916 Mon Sep 17 00:00:00 2001 From: ibuler Date: Fri, 20 Jan 2017 20:13:22 +0800 Subject: [PATCH] =?UTF-8?q?[Bugfix]=20=E4=BF=AE=E6=94=B9=E4=BA=86=E4=B8=80?= =?UTF-8?q?=E4=BA=9Bissue=E4=B8=8A=E7=9A=84bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/assets/forms.py | 44 ++++------ .../assets/admin_user_create_update.html | 5 ++ .../templates/assets/admin_user_detail.html | 49 ++++++++++- .../templates/assets/asset_group_detail.html | 1 - .../templates/assets/idc_create_update.html | 2 +- apps/assets/templates/assets/idc_detail.html | 1 - apps/assets/views.py | 37 +++----- .../templates/audits/login_log_list.html | 1 + .../templates/audits/proxy_log_list.html | 13 +-- apps/audits/views.py | 74 ++++++++++------ apps/perms/forms.py | 25 +++--- .../perms/asset_permission_detail.html | 31 +++---- .../perms/asset_permission_list.html | 19 +++- apps/static/js/jumpserver.js | 8 +- apps/templates/_pagination.html | 2 +- apps/users/models/group.py | 5 +- apps/users/templates/users/_user.html | 87 ++++++++++--------- apps/users/templates/users/user_detail.html | 14 ++- .../users/user_group_create_update.html | 5 ++ .../templates/users/user_group_detail.html | 12 +++ apps/users/utils.py | 57 +----------- apps/users/views/user.py | 20 +++-- config_example.py | 4 +- 23 files changed, 283 insertions(+), 233 deletions(-) diff --git a/apps/assets/forms.py b/apps/assets/forms.py index 602a68c7c..eea3c23dd 100644 --- a/apps/assets/forms.py +++ b/apps/assets/forms.py @@ -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 diff --git a/apps/assets/templates/assets/admin_user_create_update.html b/apps/assets/templates/assets/admin_user_create_update.html index 575ab667a..2849a51c5 100644 --- a/apps/assets/templates/assets/admin_user_create_update.html +++ b/apps/assets/templates/assets/admin_user_create_update.html @@ -27,6 +27,11 @@
+ {% if form.non_field_errors %} +
+ {{ form.non_field_errors }} +
+ {% endif %}
{% csrf_token %} {{ form.name|bootstrap_horizontal }} diff --git a/apps/assets/templates/assets/admin_user_detail.html b/apps/assets/templates/assets/admin_user_detail.html index 53948921b..cfc7bc852 100644 --- a/apps/assets/templates/assets/admin_user_detail.html +++ b/apps/assets/templates/assets/admin_user_detail.html @@ -21,6 +21,11 @@
  • Update
  • +
  • + + Delete + +
  • @@ -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); +}) {% endblock %} \ No newline at end of file diff --git a/apps/assets/templates/assets/asset_group_detail.html b/apps/assets/templates/assets/asset_group_detail.html index 94e41ad10..6b1b2da6d 100644 --- a/apps/assets/templates/assets/asset_group_detail.html +++ b/apps/assets/templates/assets/asset_group_detail.html @@ -16,7 +16,6 @@
    -
    {% endblock %} {% block custom_foot_js %} diff --git a/apps/assets/views.py b/apps/assets/views.py index 5ecc9efe8..66a8c8531 100644 --- a/apps/assets/views.py +++ b/apps/assets/views.py @@ -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 %s successfully.' % - ( - reverse_lazy('assets:admin-user-detail', kwargs={'pk': self.object.pk}), - self.object.name, - )) + success_message = _( + 'Create admin user %s 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}) diff --git a/apps/audits/templates/audits/login_log_list.html b/apps/audits/templates/audits/login_log_list.html index 53b4ef0a2..eb7a4a813 100644 --- a/apps/audits/templates/audits/login_log_list.html +++ b/apps/audits/templates/audits/login_log_list.html @@ -84,6 +84,7 @@ $(document).ready(function() { $('table').DataTable({ "searching": false, + "bInfo" : false, "paging": false, "order": [] }); diff --git a/apps/audits/templates/audits/proxy_log_list.html b/apps/audits/templates/audits/proxy_log_list.html index 9c26e82ac..d28548e47 100644 --- a/apps/audits/templates/audits/proxy_log_list.html +++ b/apps/audits/templates/audits/proxy_log_list.html @@ -24,17 +24,17 @@
    @@ -42,12 +42,12 @@
    - +
    @@ -108,6 +108,7 @@ $('table').DataTable({ "searching": false, "paging": false, + "bInfo" : false, "order": [] }); $('.select2').select2(); diff --git a/apps/audits/views.py b/apps/audits/views.py index 0c0c7cf55..8ff2fc6cf 100644 --- a/apps/audits/views.py +++ b/apps/audits/views.py @@ -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): diff --git a/apps/perms/forms.py b/apps/perms/forms.py index 360fed810..27c8136c0 100644 --- a/apps/perms/forms.py +++ b/apps/perms/forms.py @@ -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', diff --git a/apps/perms/templates/perms/asset_permission_detail.html b/apps/perms/templates/perms/asset_permission_detail.html index 10de521f6..4f6b7725e 100644 --- a/apps/perms/templates/perms/asset_permission_detail.html +++ b/apps/perms/templates/perms/asset_permission_detail.html @@ -31,6 +31,11 @@
  • Update
  • +
  • + + Delete + +
  • @@ -191,25 +196,15 @@ {% endblock %} {% block custom_foot_js %} {% endblock %} \ No newline at end of file diff --git a/apps/perms/templates/perms/asset_permission_list.html b/apps/perms/templates/perms/asset_permission_list.html index d7b06f1c2..83d425af3 100644 --- a/apps/perms/templates/perms/asset_permission_list.html +++ b/apps/perms/templates/perms/asset_permission_list.html @@ -2,7 +2,9 @@ {% load i18n %} {% load common_tags %} {% block content_left_head %} - {% trans "Create permission" %} + + {% trans "Create permission" %} + {% endblock %} {% block table_head %} @@ -39,8 +41,12 @@ {% endif %} - {% trans 'Update' %} - {% trans 'Delete' %} + {% trans 'Update' %} + + {% trans 'Delete' %} + {% 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); }) {% endblock %} diff --git a/apps/static/js/jumpserver.js b/apps/static/js/jumpserver.js index c7a3144a6..f11416e90 100644 --- a/apps/static/js/jumpserver.js +++ b/apps/static/js/jumpserver.js @@ -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"); diff --git a/apps/templates/_pagination.html b/apps/templates/_pagination.html index e337b9efb..c5f4ccb0c 100644 --- a/apps/templates/_pagination.html +++ b/apps/templates/_pagination.html @@ -2,7 +2,7 @@ {% if is_paginated %}
    - {{ page_obj.start_index }} - {{ page_obj.end_index }} of {{ paginator.count }} + 显示第 {{ page_obj.start_index }} 至 {{ page_obj.end_index }} 项结果,共 {{ paginator.count }} 项
    diff --git a/apps/users/models/group.py b/apps/users/models/group.py index e73942d25..8da42aebc 100644 --- a/apps/users/models/group.py +++ b/apps/users/models/group.py @@ -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 diff --git a/apps/users/templates/users/_user.html b/apps/users/templates/users/_user.html index f119821b3..118ab8d2e 100644 --- a/apps/users/templates/users/_user.html +++ b/apps/users/templates/users/_user.html @@ -3,49 +3,54 @@ {% load static %} {% load bootstrap %} {% block form %} - - {% csrf_token %} -

    {% trans 'Account' %}

    - {{ form.username|bootstrap_horizontal }} - {{ form.name|bootstrap_horizontal }} - {{ form.email|bootstrap_horizontal }} - {{ form.groups|bootstrap_horizontal }} - -
    - {% block password %} {% endblock %} - -
    -

    {% trans 'Security and Role' %}

    - {{ form.role|bootstrap_horizontal }} -
    - -
    -
    - - -
    - {{ form.date_expired.errors }} -
    -
    -
    - -
    - {{ form.enable_otp }} -
    + {% if form.non_field_errors %} +
    + {{ form.non_field_errors }}
    -
    -

    {% trans 'Profile' %}

    - {{ form.phone|bootstrap_horizontal }} - {{ form.wechat|bootstrap_horizontal }} - {{ form.comment|bootstrap_horizontal }} -
    -
    -
    - - -
    + {% endif %} + + {% csrf_token %} +

    {% trans 'Account' %}

    + {{ form.username|bootstrap_horizontal }} + {{ form.name|bootstrap_horizontal }} + {{ form.email|bootstrap_horizontal }} + {{ form.groups|bootstrap_horizontal }} + +
    + {% block password %} {% endblock %} + +
    +

    {% trans 'Security and Role' %}

    + {{ form.role|bootstrap_horizontal }} +
    + +
    +
    + + +
    + {{ form.date_expired.errors }} +
    - +
    + +
    + {{ form.enable_otp }} +
    +
    +
    +

    {% trans 'Profile' %}

    + {{ form.phone|bootstrap_horizontal }} + {{ form.wechat|bootstrap_horizontal }} + {{ form.comment|bootstrap_horizontal }} +
    +
    +
    + + +
    +
    + {% endblock %} {% block custom_foot_js %} diff --git a/apps/users/templates/users/user_detail.html b/apps/users/templates/users/user_detail.html index ba6128124..6d78a42fd 100644 --- a/apps/users/templates/users/user_detail.html +++ b/apps/users/templates/users/user_detail.html @@ -29,6 +29,11 @@
  • Update
  • +
  • + + Delete + +
  • @@ -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); +}) {% endblock %} diff --git a/apps/users/templates/users/user_group_create_update.html b/apps/users/templates/users/user_group_create_update.html index abb18a239..3b6814860 100644 --- a/apps/users/templates/users/user_group_create_update.html +++ b/apps/users/templates/users/user_group_create_update.html @@ -16,6 +16,11 @@
    {{ action }}
    + {% if form.non_field_errors %} +
    + {{ form.non_field_errors }} +
    + {% endif %}
    {% csrf_token %} {{ form.name|bootstrap_horizontal }} diff --git a/apps/users/templates/users/user_group_detail.html b/apps/users/templates/users/user_group_detail.html index 145d9bfe8..a27c4f91f 100644 --- a/apps/users/templates/users/user_group_detail.html +++ b/apps/users/templates/users/user_group_detail.html @@ -30,6 +30,11 @@
  • Update
  • +
  • + + Delete + +
  • @@ -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); }) {% endblock %} diff --git a/apps/users/utils.py b/apps/users/utils.py index 2f1da6924..fdff529d5 100644 --- a/apps/users/utils.py +++ b/apps/users/utils.py @@ -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, ) 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) diff --git a/apps/users/views/user.py b/apps/users/views/user.py index 8a2df396e..61859adcd 100644 --- a/apps/users/views/user.py +++ b/apps/users/views/user.py @@ -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 = [] diff --git a/config_example.py b/config_example.py index 13fb382b9..2a528fbc3 100644 --- a/config_example.py +++ b/config_example.py @@ -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