From 6751c5a63a7f55a76917a86e6c878a37dd1c4238 Mon Sep 17 00:00:00 2001 From: "xiaokong1937@gmail.com" <763691951@qq.com> Date: Tue, 13 Sep 2016 21:45:10 +0800 Subject: [PATCH] reset user password and ssh pk implement --- .../css/plugins/sweetalert/sweetalert.css | 715 ++++++++++++++++++ apps/static/js/jumpserver.js | 38 +- .../js/plugins/sweetalert/sweetalert.min.js | 1 + apps/templates/_modal.html | 20 + apps/users/api.py | 23 + apps/users/serializers.py | 16 + .../templates/users/_user_reset_pk_modal.html | 8 + apps/users/templates/users/user_detail.html | 87 ++- apps/users/urls.py | 2 + apps/users/utils.py | 3 + 10 files changed, 876 insertions(+), 37 deletions(-) create mode 100644 apps/static/css/plugins/sweetalert/sweetalert.css create mode 100644 apps/static/js/plugins/sweetalert/sweetalert.min.js create mode 100644 apps/templates/_modal.html create mode 100644 apps/users/templates/users/_user_reset_pk_modal.html diff --git a/apps/static/css/plugins/sweetalert/sweetalert.css b/apps/static/css/plugins/sweetalert/sweetalert.css new file mode 100644 index 000000000..4469aea6b --- /dev/null +++ b/apps/static/css/plugins/sweetalert/sweetalert.css @@ -0,0 +1,715 @@ +body.stop-scrolling { + height: 100%; + overflow: hidden; } + +.sweet-overlay { + background-color: black; + /* IE8 */ + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=40)"; + /* IE8 */ + background-color: rgba(0, 0, 0, 0.4); + position: fixed; + left: 0; + right: 0; + top: 0; + bottom: 0; + display: none; + z-index: 10000; } + +.sweet-alert { + background-color: white; + font-family: 'Open Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif; + width: 478px; + padding: 17px; + border-radius: 5px; + text-align: center; + position: fixed; + left: 50%; + top: 50%; + margin-left: -256px; + margin-top: -200px; + overflow: hidden; + display: none; + z-index: 99999; } + @media all and (max-width: 540px) { + .sweet-alert { + width: auto; + margin-left: 0; + margin-right: 0; + left: 15px; + right: 15px; } } + .sweet-alert h2 { + color: #575757; + font-size: 30px; + text-align: center; + font-weight: 600; + text-transform: none; + position: relative; + margin: 25px 0; + padding: 0; + line-height: 40px; + display: block; } + .sweet-alert p { + color: #797979; + font-size: 16px; + text-align: center; + font-weight: 300; + position: relative; + text-align: inherit; + float: none; + margin: 0; + padding: 0; + line-height: normal; } + .sweet-alert fieldset { + border: none; + position: relative; } + .sweet-alert .sa-error-container { + background-color: #f1f1f1; + margin-left: -17px; + margin-right: -17px; + overflow: hidden; + padding: 0 10px; + max-height: 0; + webkit-transition: padding 0.15s, max-height 0.15s; + transition: padding 0.15s, max-height 0.15s; } + .sweet-alert .sa-error-container.show { + padding: 10px 0; + max-height: 100px; + webkit-transition: padding 0.2s, max-height 0.2s; + transition: padding 0.25s, max-height 0.25s; } + .sweet-alert .sa-error-container .icon { + display: inline-block; + width: 24px; + height: 24px; + border-radius: 50%; + background-color: #ea7d7d; + color: white; + line-height: 24px; + text-align: center; + margin-right: 3px; } + .sweet-alert .sa-error-container p { + display: inline-block; } + .sweet-alert .sa-input-error { + position: absolute; + top: 29px; + right: 26px; + width: 20px; + height: 20px; + opacity: 0; + -webkit-transform: scale(0.5); + transform: scale(0.5); + -webkit-transform-origin: 50% 50%; + transform-origin: 50% 50%; + -webkit-transition: all 0.1s; + transition: all 0.1s; } + .sweet-alert .sa-input-error::before, .sweet-alert .sa-input-error::after { + content: ""; + width: 20px; + height: 6px; + background-color: #f06e57; + border-radius: 3px; + position: absolute; + top: 50%; + margin-top: -4px; + left: 50%; + margin-left: -9px; } + .sweet-alert .sa-input-error::before { + -webkit-transform: rotate(-45deg); + transform: rotate(-45deg); } + .sweet-alert .sa-input-error::after { + -webkit-transform: rotate(45deg); + transform: rotate(45deg); } + .sweet-alert .sa-input-error.show { + opacity: 1; + -webkit-transform: scale(1); + transform: scale(1); } + .sweet-alert input { + width: 100%; + box-sizing: border-box; + border-radius: 3px; + border: 1px solid #d7d7d7; + height: 43px; + margin-top: 10px; + margin-bottom: 17px; + font-size: 18px; + box-shadow: inset 0px 1px 1px rgba(0, 0, 0, 0.06); + padding: 0 12px; + display: none; + -webkit-transition: all 0.3s; + transition: all 0.3s; } + .sweet-alert input:focus { + outline: none; + box-shadow: 0px 0px 3px #c4e6f5; + border: 1px solid #b4dbed; } + .sweet-alert input:focus::-moz-placeholder { + transition: opacity 0.3s 0.03s ease; + opacity: 0.5; } + .sweet-alert input:focus:-ms-input-placeholder { + transition: opacity 0.3s 0.03s ease; + opacity: 0.5; } + .sweet-alert input:focus::-webkit-input-placeholder { + transition: opacity 0.3s 0.03s ease; + opacity: 0.5; } + .sweet-alert input::-moz-placeholder { + color: #bdbdbd; } + .sweet-alert input:-ms-input-placeholder { + color: #bdbdbd; } + .sweet-alert input::-webkit-input-placeholder { + color: #bdbdbd; } + .sweet-alert.show-input input { + display: block; } + .sweet-alert button { + background-color: #AEDEF4; + color: white; + border: none; + box-shadow: none; + font-size: 17px; + font-weight: 500; + -webkit-border-radius: 4px; + border-radius: 5px; + padding: 10px 32px; + margin: 26px 5px 0 5px; + cursor: pointer; } + .sweet-alert button:focus { + outline: none; + box-shadow: 0 0 2px rgba(128, 179, 235, 0.5), inset 0 0 0 1px rgba(0, 0, 0, 0.05); } + .sweet-alert button:hover { + background-color: #a1d9f2; } + .sweet-alert button:active { + background-color: #81ccee; } + .sweet-alert button.cancel { + background-color: #D0D0D0; } + .sweet-alert button.cancel:hover { + background-color: #c8c8c8; } + .sweet-alert button.cancel:active { + background-color: #b6b6b6; } + .sweet-alert button.cancel:focus { + box-shadow: rgba(197, 205, 211, 0.8) 0px 0px 2px, rgba(0, 0, 0, 0.0470588) 0px 0px 0px 1px inset !important; } + .sweet-alert button::-moz-focus-inner { + border: 0; } + .sweet-alert[data-has-cancel-button=false] button { + box-shadow: none !important; } + .sweet-alert[data-has-confirm-button=false][data-has-cancel-button=false] { + padding-bottom: 40px; } + .sweet-alert .sa-icon { + width: 80px; + height: 80px; + border: 4px solid gray; + -webkit-border-radius: 40px; + border-radius: 40px; + border-radius: 50%; + margin: 20px auto; + padding: 0; + position: relative; + box-sizing: content-box; } + .sweet-alert .sa-icon.sa-error { + border-color: #F27474; } + .sweet-alert .sa-icon.sa-error .sa-x-mark { + position: relative; + display: block; } + .sweet-alert .sa-icon.sa-error .sa-line { + position: absolute; + height: 5px; + width: 47px; + background-color: #F27474; + display: block; + top: 37px; + border-radius: 2px; } + .sweet-alert .sa-icon.sa-error .sa-line.sa-left { + -webkit-transform: rotate(45deg); + transform: rotate(45deg); + left: 17px; } + .sweet-alert .sa-icon.sa-error .sa-line.sa-right { + -webkit-transform: rotate(-45deg); + transform: rotate(-45deg); + right: 16px; } + .sweet-alert .sa-icon.sa-warning { + border-color: #F8BB86; } + .sweet-alert .sa-icon.sa-warning .sa-body { + position: absolute; + width: 5px; + height: 47px; + left: 50%; + top: 10px; + -webkit-border-radius: 2px; + border-radius: 2px; + margin-left: -2px; + background-color: #F8BB86; } + .sweet-alert .sa-icon.sa-warning .sa-dot { + position: absolute; + width: 7px; + height: 7px; + -webkit-border-radius: 50%; + border-radius: 50%; + margin-left: -3px; + left: 50%; + bottom: 10px; + background-color: #F8BB86; } + .sweet-alert .sa-icon.sa-info { + border-color: #C9DAE1; } + .sweet-alert .sa-icon.sa-info::before { + content: ""; + position: absolute; + width: 5px; + height: 29px; + left: 50%; + bottom: 17px; + border-radius: 2px; + margin-left: -2px; + background-color: #C9DAE1; } + .sweet-alert .sa-icon.sa-info::after { + content: ""; + position: absolute; + width: 7px; + height: 7px; + border-radius: 50%; + margin-left: -3px; + top: 19px; + background-color: #C9DAE1; } + .sweet-alert .sa-icon.sa-success { + border-color: #A5DC86; } + .sweet-alert .sa-icon.sa-success::before, .sweet-alert .sa-icon.sa-success::after { + content: ''; + -webkit-border-radius: 40px; + border-radius: 40px; + border-radius: 50%; + position: absolute; + width: 60px; + height: 120px; + background: white; + -webkit-transform: rotate(45deg); + transform: rotate(45deg); } + .sweet-alert .sa-icon.sa-success::before { + -webkit-border-radius: 120px 0 0 120px; + border-radius: 120px 0 0 120px; + top: -7px; + left: -33px; + -webkit-transform: rotate(-45deg); + transform: rotate(-45deg); + -webkit-transform-origin: 60px 60px; + transform-origin: 60px 60px; } + .sweet-alert .sa-icon.sa-success::after { + -webkit-border-radius: 0 120px 120px 0; + border-radius: 0 120px 120px 0; + top: -11px; + left: 30px; + -webkit-transform: rotate(-45deg); + transform: rotate(-45deg); + -webkit-transform-origin: 0px 60px; + transform-origin: 0px 60px; } + .sweet-alert .sa-icon.sa-success .sa-placeholder { + width: 80px; + height: 80px; + border: 4px solid rgba(165, 220, 134, 0.2); + -webkit-border-radius: 40px; + border-radius: 40px; + border-radius: 50%; + box-sizing: content-box; + position: absolute; + left: -4px; + top: -4px; + z-index: 2; } + .sweet-alert .sa-icon.sa-success .sa-fix { + width: 5px; + height: 90px; + background-color: white; + position: absolute; + left: 28px; + top: 8px; + z-index: 1; + -webkit-transform: rotate(-45deg); + transform: rotate(-45deg); } + .sweet-alert .sa-icon.sa-success .sa-line { + height: 5px; + background-color: #A5DC86; + display: block; + border-radius: 2px; + position: absolute; + z-index: 2; } + .sweet-alert .sa-icon.sa-success .sa-line.sa-tip { + width: 25px; + left: 14px; + top: 46px; + -webkit-transform: rotate(45deg); + transform: rotate(45deg); } + .sweet-alert .sa-icon.sa-success .sa-line.sa-long { + width: 47px; + right: 8px; + top: 38px; + -webkit-transform: rotate(-45deg); + transform: rotate(-45deg); } + .sweet-alert .sa-icon.sa-custom { + background-size: contain; + border-radius: 0; + border: none; + background-position: center center; + background-repeat: no-repeat; } + +/* + * Animations + */ +@-webkit-keyframes showSweetAlert { + 0% { + transform: scale(0.7); + -webkit-transform: scale(0.7); } + 45% { + transform: scale(1.05); + -webkit-transform: scale(1.05); } + 80% { + transform: scale(0.95); + -webkit-transform: scale(0.95); } + 100% { + transform: scale(1); + -webkit-transform: scale(1); } } + +@keyframes showSweetAlert { + 0% { + transform: scale(0.7); + -webkit-transform: scale(0.7); } + 45% { + transform: scale(1.05); + -webkit-transform: scale(1.05); } + 80% { + transform: scale(0.95); + -webkit-transform: scale(0.95); } + 100% { + transform: scale(1); + -webkit-transform: scale(1); } } + +@-webkit-keyframes hideSweetAlert { + 0% { + transform: scale(1); + -webkit-transform: scale(1); } + 100% { + transform: scale(0.5); + -webkit-transform: scale(0.5); } } + +@keyframes hideSweetAlert { + 0% { + transform: scale(1); + -webkit-transform: scale(1); } + 100% { + transform: scale(0.5); + -webkit-transform: scale(0.5); } } + +@-webkit-keyframes slideFromTop { + 0% { + top: 0%; } + 100% { + top: 50%; } } + +@keyframes slideFromTop { + 0% { + top: 0%; } + 100% { + top: 50%; } } + +@-webkit-keyframes slideToTop { + 0% { + top: 50%; } + 100% { + top: 0%; } } + +@keyframes slideToTop { + 0% { + top: 50%; } + 100% { + top: 0%; } } + +@-webkit-keyframes slideFromBottom { + 0% { + top: 70%; } + 100% { + top: 50%; } } + +@keyframes slideFromBottom { + 0% { + top: 70%; } + 100% { + top: 50%; } } + +@-webkit-keyframes slideToBottom { + 0% { + top: 50%; } + 100% { + top: 70%; } } + +@keyframes slideToBottom { + 0% { + top: 50%; } + 100% { + top: 70%; } } + +.showSweetAlert[data-animation=pop] { + -webkit-animation: showSweetAlert 0.3s; + animation: showSweetAlert 0.3s; } + +.showSweetAlert[data-animation=none] { + -webkit-animation: none; + animation: none; } + +.showSweetAlert[data-animation=slide-from-top] { + -webkit-animation: slideFromTop 0.3s; + animation: slideFromTop 0.3s; } + +.showSweetAlert[data-animation=slide-from-bottom] { + -webkit-animation: slideFromBottom 0.3s; + animation: slideFromBottom 0.3s; } + +.hideSweetAlert[data-animation=pop] { + -webkit-animation: hideSweetAlert 0.2s; + animation: hideSweetAlert 0.2s; } + +.hideSweetAlert[data-animation=none] { + -webkit-animation: none; + animation: none; } + +.hideSweetAlert[data-animation=slide-from-top] { + -webkit-animation: slideToTop 0.4s; + animation: slideToTop 0.4s; } + +.hideSweetAlert[data-animation=slide-from-bottom] { + -webkit-animation: slideToBottom 0.3s; + animation: slideToBottom 0.3s; } + +@-webkit-keyframes animateSuccessTip { + 0% { + width: 0; + left: 1px; + top: 19px; } + 54% { + width: 0; + left: 1px; + top: 19px; } + 70% { + width: 50px; + left: -8px; + top: 37px; } + 84% { + width: 17px; + left: 21px; + top: 48px; } + 100% { + width: 25px; + left: 14px; + top: 45px; } } + +@keyframes animateSuccessTip { + 0% { + width: 0; + left: 1px; + top: 19px; } + 54% { + width: 0; + left: 1px; + top: 19px; } + 70% { + width: 50px; + left: -8px; + top: 37px; } + 84% { + width: 17px; + left: 21px; + top: 48px; } + 100% { + width: 25px; + left: 14px; + top: 45px; } } + +@-webkit-keyframes animateSuccessLong { + 0% { + width: 0; + right: 46px; + top: 54px; } + 65% { + width: 0; + right: 46px; + top: 54px; } + 84% { + width: 55px; + right: 0px; + top: 35px; } + 100% { + width: 47px; + right: 8px; + top: 38px; } } + +@keyframes animateSuccessLong { + 0% { + width: 0; + right: 46px; + top: 54px; } + 65% { + width: 0; + right: 46px; + top: 54px; } + 84% { + width: 55px; + right: 0px; + top: 35px; } + 100% { + width: 47px; + right: 8px; + top: 38px; } } + +@-webkit-keyframes rotatePlaceholder { + 0% { + transform: rotate(-45deg); + -webkit-transform: rotate(-45deg); } + 5% { + transform: rotate(-45deg); + -webkit-transform: rotate(-45deg); } + 12% { + transform: rotate(-405deg); + -webkit-transform: rotate(-405deg); } + 100% { + transform: rotate(-405deg); + -webkit-transform: rotate(-405deg); } } + +@keyframes rotatePlaceholder { + 0% { + transform: rotate(-45deg); + -webkit-transform: rotate(-45deg); } + 5% { + transform: rotate(-45deg); + -webkit-transform: rotate(-45deg); } + 12% { + transform: rotate(-405deg); + -webkit-transform: rotate(-405deg); } + 100% { + transform: rotate(-405deg); + -webkit-transform: rotate(-405deg); } } + +.animateSuccessTip { + -webkit-animation: animateSuccessTip 0.75s; + animation: animateSuccessTip 0.75s; } + +.animateSuccessLong { + -webkit-animation: animateSuccessLong 0.75s; + animation: animateSuccessLong 0.75s; } + +.sa-icon.sa-success.animate::after { + -webkit-animation: rotatePlaceholder 4.25s ease-in; + animation: rotatePlaceholder 4.25s ease-in; } + +@-webkit-keyframes animateErrorIcon { + 0% { + transform: rotateX(100deg); + -webkit-transform: rotateX(100deg); + opacity: 0; } + 100% { + transform: rotateX(0deg); + -webkit-transform: rotateX(0deg); + opacity: 1; } } + +@keyframes animateErrorIcon { + 0% { + transform: rotateX(100deg); + -webkit-transform: rotateX(100deg); + opacity: 0; } + 100% { + transform: rotateX(0deg); + -webkit-transform: rotateX(0deg); + opacity: 1; } } + +.animateErrorIcon { + -webkit-animation: animateErrorIcon 0.5s; + animation: animateErrorIcon 0.5s; } + +@-webkit-keyframes animateXMark { + 0% { + transform: scale(0.4); + -webkit-transform: scale(0.4); + margin-top: 26px; + opacity: 0; } + 50% { + transform: scale(0.4); + -webkit-transform: scale(0.4); + margin-top: 26px; + opacity: 0; } + 80% { + transform: scale(1.15); + -webkit-transform: scale(1.15); + margin-top: -6px; } + 100% { + transform: scale(1); + -webkit-transform: scale(1); + margin-top: 0; + opacity: 1; } } + +@keyframes animateXMark { + 0% { + transform: scale(0.4); + -webkit-transform: scale(0.4); + margin-top: 26px; + opacity: 0; } + 50% { + transform: scale(0.4); + -webkit-transform: scale(0.4); + margin-top: 26px; + opacity: 0; } + 80% { + transform: scale(1.15); + -webkit-transform: scale(1.15); + margin-top: -6px; } + 100% { + transform: scale(1); + -webkit-transform: scale(1); + margin-top: 0; + opacity: 1; } } + +.animateXMark { + -webkit-animation: animateXMark 0.5s; + animation: animateXMark 0.5s; } + +@-webkit-keyframes pulseWarning { + 0% { + border-color: #F8D486; } + 100% { + border-color: #F8BB86; } } + +@keyframes pulseWarning { + 0% { + border-color: #F8D486; } + 100% { + border-color: #F8BB86; } } + +.pulseWarning { + -webkit-animation: pulseWarning 0.75s infinite alternate; + animation: pulseWarning 0.75s infinite alternate; } + +@-webkit-keyframes pulseWarningIns { + 0% { + background-color: #F8D486; } + 100% { + background-color: #F8BB86; } } + +@keyframes pulseWarningIns { + 0% { + background-color: #F8D486; } + 100% { + background-color: #F8BB86; } } + +.pulseWarningIns { + -webkit-animation: pulseWarningIns 0.75s infinite alternate; + animation: pulseWarningIns 0.75s infinite alternate; } + +/* Internet Explorer 9 has some special quirks that are fixed here */ +/* The icons are not animated. */ +/* This file is automatically merged into sweet-alert.min.js through Gulp */ +/* Error icon */ +.sweet-alert .sa-icon.sa-error .sa-line.sa-left { + -ms-transform: rotate(45deg) \9; } + +.sweet-alert .sa-icon.sa-error .sa-line.sa-right { + -ms-transform: rotate(-45deg) \9; } + +/* Success icon */ +.sweet-alert .sa-icon.sa-success { + border-color: transparent\9; } + +.sweet-alert .sa-icon.sa-success .sa-line.sa-tip { + -ms-transform: rotate(45deg) \9; } + +.sweet-alert .sa-icon.sa-success .sa-line.sa-long { + -ms-transform: rotate(-45deg) \9; } 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/static/js/plugins/sweetalert/sweetalert.min.js b/apps/static/js/plugins/sweetalert/sweetalert.min.js new file mode 100644 index 000000000..3e4e7ff4c --- /dev/null +++ b/apps/static/js/plugins/sweetalert/sweetalert.min.js @@ -0,0 +1 @@ +!function(e,t,n){"use strict";!function o(e,t,n){function a(s,l){if(!t[s]){if(!e[s]){var i="function"==typeof require&&require;if(!l&&i)return i(s,!0);if(r)return r(s,!0);var u=new Error("Cannot find module '"+s+"'");throw u.code="MODULE_NOT_FOUND",u}var c=t[s]={exports:{}};e[s][0].call(c.exports,function(t){var n=e[s][1][t];return a(n?n:t)},c,c.exports,o,e,t,n)}return t[s].exports}for(var r="function"==typeof require&&require,s=0;s=0;)n=n.replace(" "+t+" "," ");e.className=n.replace(/^\s+|\s+$/g,"")}},i=function(e){var n=t.createElement("div");return n.appendChild(t.createTextNode(e)),n.innerHTML},u=function(e){e.style.opacity="",e.style.display="block"},c=function(e){if(e&&!e.length)return u(e);for(var t=0;t0?setTimeout(o,t):e.style.display="none"});o()},h=function(n){if("function"==typeof MouseEvent){var o=new MouseEvent("click",{view:e,bubbles:!1,cancelable:!0});n.dispatchEvent(o)}else if(t.createEvent){var a=t.createEvent("MouseEvents");a.initEvent("click",!1,!1),n.dispatchEvent(a)}else t.createEventObject?n.fireEvent("onclick"):"function"==typeof n.onclick&&n.onclick()},g=function(t){"function"==typeof t.stopPropagation?(t.stopPropagation(),t.preventDefault()):e.event&&e.event.hasOwnProperty("cancelBubble")&&(e.event.cancelBubble=!0)};a.hasClass=r,a.addClass=s,a.removeClass=l,a.escapeHtml=i,a._show=u,a.show=c,a._hide=d,a.hide=f,a.isDescendant=p,a.getTopMargin=m,a.fadeIn=v,a.fadeOut=y,a.fireClick=h,a.stopEventPropagation=g},{}],5:[function(t,o,a){Object.defineProperty(a,"__esModule",{value:!0});var r=t("./handle-dom"),s=t("./handle-swal-dom"),l=function(t,o,a){var l=t||e.event,i=l.keyCode||l.which,u=a.querySelector("button.confirm"),c=a.querySelector("button.cancel"),d=a.querySelectorAll("button[tabindex]");if(-1!==[9,13,32,27].indexOf(i)){for(var f=l.target||l.srcElement,p=-1,m=0;m"),i.innerHTML=e.html?e.text:s.escapeHtml(e.text||"").split("\n").join("
"),e.text&&s.show(i),e.customClass)s.addClass(t,e.customClass),t.setAttribute("data-custom-class",e.customClass);else{var d=t.getAttribute("data-custom-class");s.removeClass(t,d),t.setAttribute("data-custom-class","")}if(s.hide(t.querySelectorAll(".sa-icon")),e.type&&!a.isIE8()){var f=function(){for(var o=!1,a=0;ao;o++)n=parseInt(e.substr(2*o,2),16),n=Math.round(Math.min(Math.max(0,n+n*t),255)).toString(16),a+=("00"+n).substr(n.length);return a};o.extend=a,o.hexToRgb=r,o.isIE8=s,o.logStr=l,o.colorLuminance=i},{}]},{},[1]),"function"==typeof define&&define.amd?define(function(){return sweetAlert}):"undefined"!=typeof module&&module.exports&&(module.exports=sweetAlert)}(window,document); \ No newline at end of file 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 41cdde6e5..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 %}
@@ -114,8 +116,8 @@ - - + - - + @@ -191,7 +193,7 @@ {% endfor %} @@ -205,6 +207,7 @@ + {% 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 }} - +