diff --git a/apps/static/js/jumpserver.js b/apps/static/js/jumpserver.js index 5b7452b6b..20e62736f 100644 --- a/apps/static/js/jumpserver.js +++ b/apps/static/js/jumpserver.js @@ -4,10 +4,10 @@ var checked=false; function check_all(form) { var checkboxes = document.getElementById(form); - if (checked == false) { - checked = true + if (checked === false) { + checked = true; } else { - checked = false + checked = false; } for (var i = 0; i < checkboxes.elements.length; i++) { if (checkboxes.elements[i].type == "checkbox") { @@ -51,13 +51,13 @@ function GetRowData(row){ //此函数用于在多选提交时至少要选择一行 function GetTableDataBox() { var tabProduct = document.getElementById("editable"); - var tableData = new Array(); - var returnData = new Array(); + var tableData = []; + var returnData = []; var checkboxes = document.getElementById("contents_form"); - var id_list = new Array(); + var id_list = []; len = checkboxes.elements.length; for (var i=0; i < len; i++) { - if (checkboxes.elements[i].type == "checkbox" && checkboxes.elements[i].checked == true && checkboxes.elements[i].value != "checkall") { + if (checkboxes.elements[i].type == "checkbox" && checkboxes.elements[i].checked === true && checkboxes.elements[i].value != "checkall") { id_list.push(i); } } @@ -67,7 +67,7 @@ function GetTableDataBox() { tableData.push(GetRowData(tabProduct.rows[id_list[i]])); } - if (id_list.length == 0){ + if (id_list.length === 0){ alert('请至少选择一行!'); } returnData.push(tableData); @@ -77,7 +77,7 @@ function GetTableDataBox() { function move(from, to, from_o, to_o) { $("#" + from + " option").each(function () { - if ($(this).prop("selected") == true) { + if ($(this).prop("selected") === true) { $("#" + to).append(this); if( typeof from_o !== 'undefined'){ $("#"+to_o).append($("#"+from_o +" option[value='"+this.value+"']")); @@ -88,7 +88,7 @@ function move(from, to, from_o, to_o) { function move_left(from, to, from_o, to_o) { $("#" + from + " option").each(function () { - if ($(this).prop("selected") == true) { + if ($(this).prop("selected") === true) { $("#" + to).append(this); if( typeof from_o !== 'undefined'){ $("#"+to_o).append($("#"+from_o +" option[value='"+this.value+"']")); @@ -126,8 +126,8 @@ function move_left(from, to, from_o, to_o) { function selectAll(){ // 选择该页面所有option $('option').each(function(){ - $(this).attr('selected', true) - }) + $(this).attr('selected', true); + }); } @@ -156,6 +156,8 @@ function getIDall() { function APIUpdateAttr(props) { // props = {url: .., body: , success: , error: , method: ,} props = props || {}; + success_message = props.success_message || 'Update Successfully!'; + fail_message = props.fail_message || 'Error occurred while updating.'; $.ajax({ url: props.url, type: props.method || "PATCH", @@ -164,18 +166,18 @@ function APIUpdateAttr(props) { dataType: props.data_type || "json", }).done(function(data, textStatue, jqXHR) { if (typeof props.success === 'function') { - return props.success(data) + return props.success(data); } else { - toastr.success('Update Success!') + toastr.success(success_message); } }).fail(function(jqXHR, textStatue, errorThrown) { if (typeof props.error === 'function') { - return props.error(errorThrown) + return props.error(errorThrown); } else { - toastr.error('Error occurred while updating.') + toastr.error(fail_message); } - }) + }); return true; } -var jumpserver = new Object(); +var jumpserver = {}; diff --git a/apps/templates/_modal.html b/apps/templates/_modal.html new file mode 100644 index 000000000..9d8b50c70 --- /dev/null +++ b/apps/templates/_modal.html @@ -0,0 +1,20 @@ +{% load i18n %} + diff --git a/apps/users/api.py b/apps/users/api.py index ee7b5f233..bb995d0f8 100644 --- a/apps/users/api.py +++ b/apps/users/api.py @@ -6,6 +6,7 @@ import logging from rest_framework import generics from .serializers import UserSerializer, UserGroupSerializer, UserAttributeSerializer, UserGroupEditSerializer +from .serializers import UserPKUpdateSerializer from .models import User, UserGroup @@ -49,3 +50,25 @@ class UserAttributeApi(generics.RetrieveUpdateDestroyAPIView): class UserGroupEditApi(generics.RetrieveUpdateAPIView): queryset = User.objects.all() serializer_class = UserGroupEditSerializer + + +class UserResetPasswordApi(generics.UpdateAPIView): + queryset = User.objects.all() + serializer_class = UserGroupEditSerializer + + def perform_update(self, serializer): + # Note: we are not updating the user object here. + # We just do the reset-password staff. + user = self.get_object() + from .utils import send_reset_password_mail + send_reset_password_mail(user) + + +class UserResetPKApi(generics.UpdateAPIView): + queryset = User.objects.all() + serializer_class = UserPKUpdateSerializer + + def perform_update(self, serializer): + user = self.get_object() + user.private_key = serializer.validated_data['_private_key'] + user.save() diff --git a/apps/users/serializers.py b/apps/users/serializers.py index 808bb7349..7e70049f8 100644 --- a/apps/users/serializers.py +++ b/apps/users/serializers.py @@ -1,6 +1,8 @@ # -*- coding: utf-8 -*- # +from django.utils.translation import ugettext_lazy as _ + from rest_framework import serializers from .models import User, UserGroup @@ -38,3 +40,17 @@ class UserGroupEditSerializer(serializers.ModelSerializer): class Meta: model = User fields = ['id', 'groups'] + + +class UserPKUpdateSerializer(serializers.ModelSerializer): + + class Meta: + model = User + fields = ['id', '_private_key'] + + def validate__private_key(self, value): + from users.utils import validate_ssh_pk + checked, reason = validate_ssh_pk(value) + if not checked: + raise serializers.ValidationError(_('Not a valid ssh private key.')) + return value diff --git a/apps/users/templates/users/_user_reset_pk_modal.html b/apps/users/templates/users/_user_reset_pk_modal.html new file mode 100644 index 000000000..bd6b62715 --- /dev/null +++ b/apps/users/templates/users/_user_reset_pk_modal.html @@ -0,0 +1,8 @@ +{% extends '_modal.html' %} +{% load i18n %} +{% block modal_id %}user_reset_pk_modal{% endblock %} +{% block modal_title%}{% trans 'Reset User SSH Private Key' %}{% endblock %} +{% block modal_body %} + +{% endblock %} +{% block modal_confirm_id %}btn_user_reset_pk{% endblock %} diff --git a/apps/users/templates/users/user_detail.html b/apps/users/templates/users/user_detail.html index ae61ddf9a..e89e96edf 100644 --- a/apps/users/templates/users/user_detail.html +++ b/apps/users/templates/users/user_detail.html @@ -6,7 +6,9 @@ {% block custom_head_css_js %} + + {% endblock %} {% block content %}
@@ -22,7 +24,7 @@
-
+
{{ user_object.name }} @@ -108,14 +110,14 @@
- {% trans 'Quick update' %} + {% trans 'Quick modify' %}
- - + - - + @@ -191,7 +193,7 @@ {% endfor %} @@ -205,12 +207,11 @@ - - + {% include 'users/_user_reset_pk_modal.html' %} {% endblock %} {% block custom_foot_js %} {% endblock %} diff --git a/apps/users/urls.py b/apps/users/urls.py index 179827216..5a83c64b1 100644 --- a/apps/users/urls.py +++ b/apps/users/urls.py @@ -35,6 +35,8 @@ urlpatterns += [ api.UserDetailDeleteUpdateApi.as_view(), name='user-detail-api'), url(r'^v1/users/(?P[0-9]+)/patch$', api.UserAttributeApi.as_view(), name='user-patch-api'), + url(r'^v1/users/(?P\d+)/reset-password/$', api.UserResetPasswordApi.as_view(), name='user-reset-password-api'), + url(r'^v1/users/(?P\d+)/reset-pk/$', api.UserResetPKApi.as_view(), name='user-reset-pk-api'), url(r'^v1/user-groups$', api.UserGroupListAddApi.as_view(), name='user-group-list-api'), url(r'^v1/user-groups/(?P[0-9]+)$', api.UserGroupDetailDeleteUpdateApi.as_view(), name='user-group-detail-api'), diff --git a/apps/users/utils.py b/apps/users/utils.py index 9e9a34321..13076a106 100644 --- a/apps/users/utils.py +++ b/apps/users/utils.py @@ -5,6 +5,7 @@ import logging import os import re +from django.conf import settings from django.contrib.auth.mixins import UserPassesTestMixin from django.urls import reverse_lazy from django.utils.translation import ugettext as _ @@ -121,6 +122,8 @@ def send_reset_password_mail(user): 'email': user.email, 'login_url': reverse('users:login', external=True), } + if settings.DEBUG: + logger.debug(message) send_mail_async.delay(subject, message, recipient_list, html_message=message)
Active: + {% trans 'Active' %}:
@@ -128,8 +130,8 @@
二次验证: + {% trans 'Enable OTP' %}:
{% trans 'Reset password' %}: - - + +
{% trans 'Reset ssh key' %}: - - + +
{{ group.name }} - +