Merge pull request #99 from george-e-shaw-iv/master

Changing passwords and deletion of server users
This commit is contained in:
George Shaw 2017-12-01 16:59:26 -06:00 committed by GitHub
commit 1037177cec
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 345 additions and 6 deletions

60
pkg/api/user/delete.go Normal file
View file

@ -0,0 +1,60 @@
package user
import (
"encoding/json"
"log"
"net/http"
"strconv"
"github.com/Ennovar/gPanel/pkg/database"
)
func Delete(res http.ResponseWriter, req *http.Request, logger *log.Logger, dir string) bool {
if req.Method != "UPDATE" {
logger.Println(req.URL.Path + "::" + req.Method + "::" + strconv.Itoa(http.StatusMethodNotAllowed) + "::" + http.StatusText(http.StatusMethodNotAllowed))
http.Error(res, req.Method+" HTTP method is unsupported for this API.", http.StatusMethodNotAllowed)
return false
}
var deleteUserRequestData struct {
User string `json:"user"`
}
err := json.NewDecoder(req.Body).Decode(&deleteUserRequestData)
if err != nil {
logger.Println(req.URL.Path + "::" + err.Error())
http.Error(res, err.Error(), http.StatusBadRequest)
return false
}
ds, err := database.Open(dir + database.DB_MAIN)
if err != nil || ds == nil {
logger.Println(req.URL.Path + "::" + err.Error())
http.Error(res, err.Error(), http.StatusInternalServerError)
return false
}
defer ds.Close()
count, err := ds.Count(database.BUCKET_USERS)
if err != nil {
logger.Println(req.URL.Path + "::" + err.Error())
http.Error(res, err.Error(), http.StatusInternalServerError)
return false
}
if count <= 1 {
logger.Println(req.URL.Path + ":: if only one user exists it cannot be deleted")
http.Error(res, "If only one user exists it cannot be deleted", http.StatusBadRequest)
return false
}
err = ds.Delete(database.BUCKET_USERS, []byte(deleteUserRequestData.User))
if err != nil {
logger.Println(req.URL.Path + "::" + err.Error())
http.Error(res, err.Error(), http.StatusInternalServerError)
return false
}
res.WriteHeader(http.StatusNoContent)
return true
}

View file

@ -0,0 +1,63 @@
package user
import (
"encoding/json"
"log"
"net/http"
"strconv"
"github.com/Ennovar/gPanel/pkg/database"
"github.com/Ennovar/gPanel/pkg/encryption"
)
func UpdatePassword(res http.ResponseWriter, req *http.Request, logger *log.Logger, dir string) bool {
if req.Method != "UPDATE" {
logger.Println(req.URL.Path + "::" + req.Method + "::" + strconv.Itoa(http.StatusMethodNotAllowed) + "::" + http.StatusText(http.StatusMethodNotAllowed))
http.Error(res, req.Method+" HTTP method is unsupported for this API.", http.StatusMethodNotAllowed)
return false
}
var updatePasswordRequestData struct {
User string `json:"user"`
Pass string `json:"pass"`
}
err := json.NewDecoder(req.Body).Decode(&updatePasswordRequestData)
if err != nil {
logger.Println(req.URL.Path + "::" + err.Error())
http.Error(res, err.Error(), http.StatusBadRequest)
return false
}
ds, err := database.Open(dir + database.DB_MAIN)
if err != nil || ds == nil {
logger.Println(req.URL.Path + "::" + err.Error())
http.Error(res, err.Error(), http.StatusInternalServerError)
return false
}
defer ds.Close()
var userDatabaseData struct {
Pass string `json:"pass"`
Secret string `json:"secret"`
}
userDatabaseData.Pass, err = encryption.HashPassword(updatePasswordRequestData.Pass)
if err != nil {
logger.Println(req.URL.Path + "::" + err.Error())
http.Error(res, err.Error(), http.StatusInternalServerError)
return false
}
userDatabaseData.Secret = ""
err = ds.Put(database.BUCKET_USERS, []byte(updatePasswordRequestData.User), userDatabaseData)
if err != nil {
logger.Println(req.URL.Path + "::" + err.Error())
http.Error(res, err.Error(), http.StatusInternalServerError)
return false
}
res.WriteHeader(http.StatusNoContent)
return true
}

View file

@ -122,3 +122,20 @@ func (ds *Datastore) Delete(bucket string, key []byte) error {
return tx.Bucket([]byte(bucket)).Delete(key)
})
}
func (ds *Datastore) Count(bucket string) (int, error) {
count := 0
ds.handle.View(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte(bucket))
c := b.Cursor()
for k, _ := c.First(); k != nil; k, _ = c.Next() {
count++
}
return nil
})
return count, nil
}

View file

@ -74,6 +74,10 @@ func (con *Controller) apiHandler(res http.ResponseWriter, req *http.Request) (b
return true, user.Logout(res, req, con.APILogger, con.Directory)
case "/user/list":
return true, user.List(res, req, con.APILogger, con.Directory)
case "/user/delete":
return true, user.Delete(res, req, con.APILogger, con.Directory)
case "/user/update_password":
return true, user.UpdatePassword(res, req, con.APILogger, con.Directory)
case "/bundle/create":
return true, bundle.Create(res, req, con.APILogger, con.Bundles)
case "/bundle/list":

View file

@ -0,0 +1,33 @@
jQuery(document).on('click', '._js_user-management-delete', function(e){
e.preventDefault();
if(!jQuery(this).attr('data') || jQuery(this).attr('data') == "") {
alert("An error has occurred, please refresh and try again. If problem persists please contact your administrator.");
return;
}
var ensure = confirm('Are you sure you want to delete the user "' + jQuery(this).attr('data') + '"?');
if(ensure) {
var requestData = {};
requestData["user"] = jQuery(this).attr('data');
var xhr = new XMLHttpRequest();
xhr.open('UPDATE', 'api/user/delete', true);
xhr.setRequestHeader('Content-Type', 'application/json; charset=UTF-8');
xhr.send(JSON.stringify(requestData));
xhr.onloadend = function() {
if(xhr.status == 204) {
listCurrentUsers();
}
else {
if(xhr.response != undefined && xhr.response.length != 0) {
alert('Error: ' + xhr.response);
}
else {
alert("An error has occurred, please refresh and try again. If problem persists please contact your administrator.");
}
}
}
}
});

View file

@ -43,17 +43,23 @@ jQuery('._js_add-user-form').on('submit', function(e){
jQuery('._js_add-user-generate-password').on('click', function(e){
e.preventDefault();
var genpass = generatePassword();
toggleShowPassword(true);
passwordInput.prop('value', genpass);
passwordInputRetype.prop('value', genpass);
});
function generatePassword() {
var gen = "";
var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()_+-[]:;<>?";
for (var i = 0; i < 32; i++) {
gen += chars.charAt(Math.floor(Math.random() * chars.length));
}
toggleShowPassword(true);
passwordInput.prop('value', gen);
passwordInputRetype.prop('value', gen);
});
return gen
}
jQuery('._js_user-management-show-password').on('change', function(e){
e.preventDefault();

View file

@ -0,0 +1,107 @@
var userModal = jQuery('.user-management-modal');
var newPassModal = jQuery('.new-pass-modal');
var newPassword = jQuery('#updatePassword');
var newPasswordRetype = jQuery('#updatePasswordRetype');
var newPasswordUsername = jQuery('#updatePasswordUsername');
jQuery(document).on('click', '._js_user-management-new-password', function(e){
e.preventDefault();
if(!jQuery(this).attr('data') || jQuery(this).attr('data') == "") {
alert("An error has occurred, please refresh and try again. If problem persists please contact your administrator.");
return;
}
var username = jQuery(this).attr('data');
newPasswordUsername.attr('value', username);
newPassModal.find('.modal-title').html('Changing password for "'+username+'"');
toggleShowPasswordNewPassword(false);
userModal.modal('hide');
newPassModal.modal('show');
});
jQuery('._js_back-to-user-management').on('click', function(e){
e.preventDefault();
newPassModal.modal('hide');
userModal.modal('show');
});
jQuery('._js_update-password-form').on('submit', function(e){
e.preventDefault();
if((newPassword && newPassword.val()) && (newPasswordRetype && newPasswordRetype.val()) && (newPasswordUsername && newPasswordUsername.val())) {
if(newPassword.val() == newPasswordRetype.val()) {
var ensure = confirm("Are you sure you want to change the password of user \"" + newPasswordUsername.val() + "\"?");
if(ensure) {
var requestData = {};
requestData["user"] = newPasswordUsername.val();
requestData["pass"] = newPassword.val();
var xhr = new XMLHttpRequest();
xhr.open(jQuery(this).attr('method'), jQuery(this).attr('action'), true);
xhr.setRequestHeader('Content-Type', 'application/json; charset=UTF-8');
xhr.send(JSON.stringify(requestData));
xhr.onloadend = function() {
if(xhr.status == 204) {
alert("Password successfully updated.");
newPassModal.modal('hide');
userModal.modal('show');
}
else {
if(xhr.response != undefined && xhr.response.length != 0) {
alert('Error: ' + xhr.response);
}
else {
alert('An error has occurred, refresh and try again. If problem persists please contact your administrator.');
}
}
}
}
}
else {
alert("Passwords must match.");
}
}
else {
alert("All fields need to be filled out");
}
});
jQuery('._js_update-password-show-password').on('change', function(e){
e.preventDefault();
if(this.checked) {
toggleShowPasswordNewPassword(true);
}
else {
toggleShowPasswordNewPassword(false);
}
});
jQuery('._js_update-password-generate-password').on('click', function(e){
e.preventDefault();
var genpass = generatePassword();
toggleShowPasswordNewPassword(true);
newPassword.prop('value', genpass);
newPasswordRetype.prop('value', genpass);
});
function toggleShowPasswordNewPassword(show) {
if(show) {
jQuery('._js_update-password-show-password').prop('checked', true);
newPassword.attr('type', 'text');
newPasswordRetype.attr('type', 'text');
}
else {
jQuery('._js_update-password-show-password').prop('checked', false);
newPassword.attr('type', 'password');
newPasswordRetype.attr('type', 'password');
}
}

View file

@ -23,7 +23,7 @@ function listCurrentUsers() {
if(xhr.response != undefined && xhr.response.length != 0) {
jsonResponse = JSON.parse(xhr.response)
jQuery.each(jsonResponse, function(k, v) {
display.append('<div class="row mt-2"><div class="col-6 d-flex align-items-center"><p class="mb-0">'+v+'</p></div><div class="col-6 d-flex justify-content-end"><div class="btn-group" role="group"><button class="btn btn-outline-primary _js_user-management-new-password" data="'+v+'">New Password</button><button class="btn btn-outline-primary _js_user-management-delete" data="'+v+'">Delete</button></div></div></div>');
display.append('<div class="row mt-2"><div class="col-6 d-flex align-items-center"><p class="mb-0">'+v+'</p></div><div class="col-6 d-flex justify-content-end"><div class="btn-group" role="group"><button class="btn btn-outline-primary _js_user-management-new-password" data="'+v+'">New Password</button><button class="btn btn-outline-danger _js_user-management-delete" data="'+v+'">Delete</button></div></div></div>');
});
}
else {

View file

@ -231,6 +231,53 @@
</div>
</div>
<!-- New Pass Modal -->
<div class="modal fade new-pass-modal" tabindex="-1" role="dialog" aria-labelledby="new-pass-modal" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title"></h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<form class="_js_update-password-form" action="api/user/update_password" method="UPDATE">
<div class="form-group">
<label class="sr-only" for="updatePassword">New Password</label>
<div class="input-group">
<div class="input-group-addon"><i class="fa fa-key" aria-hidden="true"></i></div>
<input name="pass" type="password" class="form-control" id="updatePassword" placeholder="Password">
</div>
</div>
<div class="form-group">
<label class="sr-only" for="updatePasswordRetype">Re-type New Password</label>
<div class="input-group">
<div class="input-group-addon"><i class="fa fa-key" aria-hidden="true"></i></div>
<input name="pass" type="password" class="form-control" id="updatePasswordRetype" placeholder="Re-type Password">
</div>
</div>
<div class="form-check">
<label class="form-check-label">
<input type="checkbox" class="form-check-input _js_update-password-show-password">
Show Password
</label>
</div>
<div class="btn-group" role="group">
<button type="submit" class="btn btn-primary">Update Password</button>
<button type="button" class="btn btn-success _js_update-password-generate-password">Generate Strong Password</button>
</div>
<input type="hidden" value="" id="updatePasswordUsername">
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
<button type="button" class="btn btn-warning _js_back-to-user-management">Back to User Mangement</button>
</div>
</div>
</div>
</div>
<div class="container">
<div class="row">
<div class="col-12">
@ -317,6 +364,8 @@
<script type="text/javascript" src="assets/js/panelHandlers/users/open.js"></script>
<script type="text/javascript" src="assets/js/panelHandlers/users/new.js"></script>
<script type="text/javascript" src="assets/js/panelHandlers/users/delete.js"></script>
<script type="text/javascript" src="assets/js/panelHandlers/users/new_password.js"></script>
<!-- KEEP AT BOTTOM OF BODY TAGS -->
</body>
</html>