mirror of https://github.com/jumpserver/jumpserver
Merge branch 'master' of code.simcu.com:jumpserver/jumpserver
commit
0f9bdab108
|
@ -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 = {};
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
{% load i18n %}
|
||||
<div aria-hidden="true" role="dialog" tabindex="-1" id="{% block modal_id %}{% endblock %}" class="modal inmodal" style="display: none;">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content animated fadeIn">
|
||||
<div class="modal-header">
|
||||
<button data-dismiss="modal" class="close" type="button"><span aria-hidden="true">×</span><span class="sr-only">Close</span></button>
|
||||
<h4 class="modal-title">{% block modal_title %}{% endblock %}</h4>
|
||||
<small>{% block modal_comment %}{% endblock %}</small>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
{% block modal_body %}
|
||||
{% endblock %}
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button data-dismiss="modal" class="btn btn-white" type="button">{% trans "Close" %}</button>
|
||||
<button class="btn btn-primary" type="button" id="{% block modal_confirm_id %}{% endblock %}">{% trans 'Confirm' %}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 %}
|
||||
<textarea id="txt_pk" class="form-control" cols="30" rows="10" placeholder="-----BEGIN RSA PRIVATE KEY-----"></textarea>
|
||||
{% endblock %}
|
||||
{% block modal_confirm_id %}btn_user_reset_pk{% endblock %}
|
|
@ -6,7 +6,9 @@
|
|||
|
||||
{% block custom_head_css_js %}
|
||||
<link href="{% static "css/plugins/select2/select2.min.css" %}" rel="stylesheet">
|
||||
<link href="{% static "css/plugins/sweetalert/sweetalert.css" %}" rel="stylesheet">
|
||||
<script src="{% static "js/plugins/select2/select2.full.min.js" %}"></script>
|
||||
<script src="{% static "js/plugins/sweetalert/sweetalert.min.js" %}"></script>
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<div class="wrapper wrapper-content animated fadeInRight">
|
||||
|
@ -22,7 +24,7 @@
|
|||
</ul>
|
||||
</div>
|
||||
<div class="tab-content">
|
||||
<div class="col-sm-7" style="padding-left: 0px;">
|
||||
<div class="col-sm-7" style="padding-left: 0">
|
||||
<div class="ibox float-e-margins">
|
||||
<div class="ibox-title">
|
||||
<span class="label"><b>{{ user_object.name }}</b></span>
|
||||
|
@ -108,14 +110,14 @@
|
|||
<div class="col-sm-5" style="padding-left: 0;padding-right: 0">
|
||||
<div class="panel panel-primary">
|
||||
<div class="panel-heading">
|
||||
<i class="fa fa-info-circle"></i> {% trans 'Quick update' %}
|
||||
<i class="fa fa-info-circle"></i> {% trans 'Quick modify' %}
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<table class="table">
|
||||
<tbody>
|
||||
<tr class="no-borders-tr">
|
||||
<td width="50%">Active:</td>
|
||||
<td><span style="float: right">
|
||||
<td width="50%">{% trans 'Active' %}:</td>
|
||||
<td><span class="pull-right">
|
||||
<div class="switch">
|
||||
<div class="onoffswitch">
|
||||
<input type="checkbox" {% if user_object.is_active %} checked {% endif %} class="onoffswitch-checkbox" id="is_active">
|
||||
|
@ -128,8 +130,8 @@
|
|||
</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>二次验证:</td>
|
||||
<td><span style="float: right">
|
||||
<td>{% trans 'Enable OTP' %}:</td>
|
||||
<td><span class="pull-right">
|
||||
<div class="switch">
|
||||
<div class="onoffswitch">
|
||||
<input type="checkbox" class="onoffswitch-checkbox" {% if user_object.enable_otp %} checked {% endif %}
|
||||
|
@ -145,16 +147,16 @@
|
|||
<tr>
|
||||
<td>{% trans 'Reset password' %}:</td>
|
||||
<td>
|
||||
<span style="float: right">
|
||||
<button type="button" class="btn btn-primary btn-xs" style="width: 54px">{% trans 'Reset' %}</button>
|
||||
<span class="pull-right">
|
||||
<button type="button" class="btn btn-primary btn-xs" id="btn_reset_password" style="width: 54px">{% trans 'Reset' %}</button>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{% trans 'Reset ssh key' %}:</td>
|
||||
<td>
|
||||
<span style="float: right">
|
||||
<button type="button" class="btn btn-primary btn-xs" style="width: 54px;">{% trans 'Reset' %}</button>
|
||||
<span class="pull-right">
|
||||
<button type="button" class="btn btn-primary btn-xs" id="btn_reset_pk" style="width: 54px;" data-toggle="modal" data-target="#user_reset_pk_modal">{% trans 'Reset' %}</button>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -191,7 +193,7 @@
|
|||
<tr>
|
||||
<td ><b class="bdg_user_group" data-gid={{ group.id }}>{{ group.name }}</b></td>
|
||||
<td>
|
||||
<button class="btn btn-danger btn-xs btn_delete_user_group" type="button" style="float: right;"><i class="fa fa-minus"></i></button>
|
||||
<button class="btn btn-danger pull-right btn-sm btn_delete_user_group" type="button"><i class="fa fa-minus"></i></button>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
@ -205,12 +207,11 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% include 'users/_user_reset_pk_modal.html' %}
|
||||
{% endblock %}
|
||||
{% block custom_foot_js %}
|
||||
<script>
|
||||
jumpserver.selected_groups = new Object();
|
||||
jumpserver.selected_groups = {};
|
||||
function updateUserGroups(user_groups) {
|
||||
var the_url = "{% url 'users:user-group-edit-api' pk=user_object.id%}";
|
||||
var body = {
|
||||
|
@ -227,43 +228,38 @@ function updateUserGroups(user_groups) {
|
|||
$('.group_edit tbody').append(
|
||||
'<tr>' +
|
||||
'<td><b class="bdg_user_group" data-gid="' + index + '">' + group_name + '</b></td>' +
|
||||
'<td><button class="btn btn-danger btn-sm btn_delete_user_group" type="button" style="float: right;"><i class="fa fa-minus"></i></button></td>' +
|
||||
'<td><button class="btn btn-danger btn-sm pull-right btn_delete_user_group" type="button"><i class="fa fa-minus"></i></button></td>' +
|
||||
'</tr>'
|
||||
)
|
||||
});
|
||||
// clear jumpserver.selected_groups
|
||||
jumpserver.selected_groups = {};
|
||||
toastr.success('{% trans "Update success!" %}')
|
||||
}
|
||||
toastr.success('{% trans "UserGroup Update Success!" %}')
|
||||
};
|
||||
APIUpdateAttr({ url: the_url, body: JSON.stringify(body), success: success, method: 'PUT'});
|
||||
|
||||
}
|
||||
$(document).ready(function () {
|
||||
$('.select2').select2()
|
||||
.on('select2:select', function(evt, params) {
|
||||
.on('select2:select', function(evt) {
|
||||
var data = evt.params.data;
|
||||
jumpserver.selected_groups[data.id] = data.text;
|
||||
}).on('select2:unselect', function(evt) {
|
||||
var data = evt.params.data;
|
||||
delete jumpserver.selected_groups[data.id]
|
||||
})
|
||||
});
|
||||
$(document).on('click', '#is_active', function(){
|
||||
}).on('click', '#is_active', function(){
|
||||
var the_url = "{% url 'users:user-patch-api' pk=user_object.id %}";
|
||||
var checked = !$(this).prop('checked');
|
||||
var body = {'is_active': checked };
|
||||
var success = function(data) {
|
||||
toastr.success('{% trans "Update success!" %}')
|
||||
}
|
||||
APIUpdateAttr({ url: the_url, body: body, success: success});
|
||||
var success = '{% trans "Update Successfully!" %}';
|
||||
APIUpdateAttr({ url: the_url, body: JSON.stringify(body), success_message: success});
|
||||
}).on('click', '#enable_otp', function(){
|
||||
var the_url = "{% url 'users:user-patch-api' pk=user_object.id %}";
|
||||
var checked = !$(this).prop('checked');
|
||||
var body = {'enable_otp': checked };
|
||||
var success = function(data) {
|
||||
toastr.success('{% trans "Update success!" %}')
|
||||
}
|
||||
APIUpdateAttr({ url: the_url, body: body, success: success});
|
||||
var success = '{% trans "Update Successfully!" %}';
|
||||
APIUpdateAttr({ url: the_url, body: JSON.stringify(body), success_message: success});
|
||||
}).on('click', '#btn_add_user_group', function(){
|
||||
if (Object.keys(jumpserver.selected_groups).length === 0) {
|
||||
return false;
|
||||
|
@ -290,6 +286,56 @@ $(document).on('click', '#is_active', function(){
|
|||
return $(this).data('gid');
|
||||
}).get();
|
||||
updateUserGroups(user_groups)
|
||||
})
|
||||
}).on('click', '#btn_reset_password', function(){
|
||||
function doReset() {
|
||||
var the_url = '{% url "users:user-reset-password-api" pk=user_object.id %}';
|
||||
var body = {};
|
||||
var success = function() {
|
||||
var msg = "{% trans 'E-mail sent successfully. An e-mail has been sent to the user\'s mailbox.' %}";
|
||||
swal("{% trans 'Password-Reset' %}", msg, "success");
|
||||
}
|
||||
APIUpdateAttr({ url: the_url, body: JSON.stringify(body), success: success});
|
||||
}
|
||||
swal({
|
||||
title: "{% trans 'Are you sure?' %}",
|
||||
text: "{% trans 'This will reset the user\'s password.' %}",
|
||||
type: "warning",
|
||||
showCancelButton: true,
|
||||
confirmButtonColor: "#DD6B55",
|
||||
confirmButtonText: "{% trans 'Confirm' %}",
|
||||
closeOnConfirm: false
|
||||
}, function () {
|
||||
doReset();
|
||||
}
|
||||
);
|
||||
}).on('click', '#btn_user_reset_pk', function(){
|
||||
var $this = $(this);
|
||||
var pk = $('#txt_pk').val();
|
||||
var the_url = '{% url "users:user-reset-pk-api" pk=user_object.id %}';
|
||||
var body = {'_private_key': pk};
|
||||
var success = function() {
|
||||
$('#txt_pk').val('');
|
||||
$this.closest('.modal').modal('hide');
|
||||
var msg = "{% trans 'Successfully updated the SSH private key.' %}";
|
||||
swal("{% trans 'User SSH Private Key Reset' %}", msg, "success");
|
||||
};
|
||||
var fail = function() {
|
||||
var msg = "{% trans 'Failed to update the user\'s SSH private key.' %}";
|
||||
swal({
|
||||
title: "{% trans 'User SSH Private Key Reset' %}",
|
||||
text: msg,
|
||||
type: "error",
|
||||
showCancelButton: false,
|
||||
confirmButtonColor: "#DD6B55",
|
||||
confirmButtonText: "{% trans 'Confirm' %}",
|
||||
closeOnConfirm: true
|
||||
}, function () {
|
||||
$('#txt_pk').focus();
|
||||
}
|
||||
);
|
||||
}
|
||||
APIUpdateAttr({ url: the_url, body: JSON.stringify(body), success: success, error: fail});
|
||||
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
|
|
@ -35,6 +35,8 @@ urlpatterns += [
|
|||
api.UserDetailDeleteUpdateApi.as_view(), name='user-detail-api'),
|
||||
url(r'^v1/users/(?P<pk>[0-9]+)/patch$',
|
||||
api.UserAttributeApi.as_view(), name='user-patch-api'),
|
||||
url(r'^v1/users/(?P<pk>\d+)/reset-password/$', api.UserResetPasswordApi.as_view(), name='user-reset-password-api'),
|
||||
url(r'^v1/users/(?P<pk>\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<pk>[0-9]+)$',
|
||||
api.UserGroupDetailDeleteUpdateApi.as_view(), name='user-group-detail-api'),
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
Loading…
Reference in New Issue