Toby Chui 1 год назад
Родитель
Сommit
40c0fb7ec8
10 измененных файлов с 402 добавлено и 22 удалено
  1. 1 0
      api.go
  2. 1 1
      main.go
  3. 136 0
      reverseproxy.go
  4. 1 1
      web/components/cert.html
  5. 14 16
      web/components/rules.html
  6. 3 2
      web/components/subd.html
  7. 1 1
      web/components/vdir.html
  8. 37 1
      web/index.html
  9. 38 0
      web/main.css
  10. 170 0
      web/snippet/basicAuthEditor.html

+ 1 - 0
api.go

@@ -47,6 +47,7 @@ func initAPIs() {
 	authRouter.HandleFunc("/api/proxy/list", ReverseProxyList)
 	authRouter.HandleFunc("/api/proxy/list", ReverseProxyList)
 	authRouter.HandleFunc("/api/proxy/edit", ReverseProxyHandleEditEndpoint)
 	authRouter.HandleFunc("/api/proxy/edit", ReverseProxyHandleEditEndpoint)
 	authRouter.HandleFunc("/api/proxy/del", DeleteProxyEndpoint)
 	authRouter.HandleFunc("/api/proxy/del", DeleteProxyEndpoint)
+	authRouter.HandleFunc("/api/proxy/updateCredentials", UpdateProxyBasicAuthCredentials)
 	authRouter.HandleFunc("/api/proxy/tlscheck", HandleCheckSiteSupportTLS)
 	authRouter.HandleFunc("/api/proxy/tlscheck", HandleCheckSiteSupportTLS)
 	authRouter.HandleFunc("/api/proxy/setIncoming", HandleIncomingPortSet)
 	authRouter.HandleFunc("/api/proxy/setIncoming", HandleIncomingPortSet)
 	authRouter.HandleFunc("/api/proxy/useHttpsRedirect", HandleUpdateHttpsRedirect)
 	authRouter.HandleFunc("/api/proxy/useHttpsRedirect", HandleUpdateHttpsRedirect)

+ 1 - 1
main.go

@@ -38,7 +38,7 @@ var ztAuthToken = flag.String("ztauth", "", "ZeroTier authtoken for the local no
 var ztAPIPort = flag.Int("ztport", 9993, "ZeroTier controller API port")
 var ztAPIPort = flag.Int("ztport", 9993, "ZeroTier controller API port")
 var (
 var (
 	name        = "Zoraxy"
 	name        = "Zoraxy"
-	version     = "2.7"
+	version     = "2.6.1"
 	nodeUUID    = "generic"
 	nodeUUID    = "generic"
 	development = true //Set this to false to use embedded web fs
 	development = true //Set this to false to use embedded web fs
 
 

+ 136 - 0
reverseproxy.go

@@ -385,16 +385,19 @@ func DeleteProxyEndpoint(w http.ResponseWriter, r *http.Request) {
 	ep, err := utils.GetPara(r, "ep")
 	ep, err := utils.GetPara(r, "ep")
 	if err != nil {
 	if err != nil {
 		utils.SendErrorResponse(w, "Invalid ep given")
 		utils.SendErrorResponse(w, "Invalid ep given")
+		return
 	}
 	}
 
 
 	ptype, err := utils.PostPara(r, "ptype")
 	ptype, err := utils.PostPara(r, "ptype")
 	if err != nil {
 	if err != nil {
 		utils.SendErrorResponse(w, "Invalid ptype given")
 		utils.SendErrorResponse(w, "Invalid ptype given")
+		return
 	}
 	}
 
 
 	err = dynamicProxyRouter.RemoveProxy(ptype, ep)
 	err = dynamicProxyRouter.RemoveProxy(ptype, ep)
 	if err != nil {
 	if err != nil {
 		utils.SendErrorResponse(w, err.Error())
 		utils.SendErrorResponse(w, err.Error())
+		return
 	}
 	}
 
 
 	RemoveReverseProxyConfig(ep)
 	RemoveReverseProxyConfig(ep)
@@ -408,6 +411,139 @@ func DeleteProxyEndpoint(w http.ResponseWriter, r *http.Request) {
 	utils.SendOK(w)
 	utils.SendOK(w)
 }
 }
 
 
+/*
+Handle update request for basic auth credential
+Require paramter: ep (Endpoint) and pytype (proxy Type)
+if request with GET, the handler will return current credentials
+on this endpoint by its username
+
+if request is POST, the handler will write the results to proxy config
+*/
+func UpdateProxyBasicAuthCredentials(w http.ResponseWriter, r *http.Request) {
+	if r.Method == http.MethodGet {
+		ep, err := utils.GetPara(r, "ep")
+		if err != nil {
+			utils.SendErrorResponse(w, "Invalid ep given")
+			return
+		}
+
+		ptype, err := utils.GetPara(r, "ptype")
+		if err != nil {
+			utils.SendErrorResponse(w, "Invalid ptype given")
+			return
+		}
+
+		//Load the target proxy object from router
+		targetProxy, err := dynamicProxyRouter.LoadProxy(ptype, ep)
+		if err != nil {
+			utils.SendErrorResponse(w, err.Error())
+			return
+		}
+
+		usernames := []string{}
+		for _, cred := range targetProxy.BasicAuthCredentials {
+			usernames = append(usernames, cred.Username)
+		}
+
+		js, _ := json.Marshal(usernames)
+		utils.SendJSONResponse(w, string(js))
+
+	} else if r.Method == http.MethodPost {
+		//Write to target
+		ep, err := utils.PostPara(r, "ep")
+		if err != nil {
+			utils.SendErrorResponse(w, "Invalid ep given")
+			return
+		}
+
+		ptype, err := utils.PostPara(r, "ptype")
+		if err != nil {
+			utils.SendErrorResponse(w, "Invalid ptype given")
+			return
+		}
+
+		if ptype != "vdir" && ptype != "subd" {
+			utils.SendErrorResponse(w, "Invalid ptype given")
+			return
+		}
+
+		creds, err := utils.PostPara(r, "creds")
+		if err != nil {
+			utils.SendErrorResponse(w, "Invalid ptype given")
+			return
+		}
+
+		//Load the target proxy object from router
+		targetProxy, err := dynamicProxyRouter.LoadProxy(ptype, ep)
+		if err != nil {
+			utils.SendErrorResponse(w, err.Error())
+			return
+		}
+
+		//Try to marshal the content of creds into the suitable structure
+		newCredentials := []*dynamicproxy.BasicAuthUnhashedCredentials{}
+		err = json.Unmarshal([]byte(creds), newCredentials)
+		if err != nil {
+			utils.SendErrorResponse(w, "Malformed credential data")
+			return
+		}
+
+		//Merge the credentials into the original config
+		//If a new username exists in old config with no pw given, keep the old pw hash
+		//If a new username is found with new password, hash it and push to credential slice
+		mergedCredentials := []*dynamicproxy.BasicAuthCredentials{}
+		for _, credential := range newCredentials {
+			if credential.Password == "" {
+				//Check if exists in the old credential files
+				keepUnchange := false
+				for _, oldCredEntry := range targetProxy.BasicAuthCredentials {
+					if oldCredEntry.Username == credential.Username {
+						//Exists! Reuse the old hash
+						mergedCredentials = append(mergedCredentials, &dynamicproxy.BasicAuthCredentials{
+							Username:     oldCredEntry.Username,
+							PasswordHash: oldCredEntry.PasswordHash,
+						})
+						keepUnchange = true
+					}
+				}
+
+				if !keepUnchange {
+					//This is a new username with no pw given
+					utils.SendErrorResponse(w, "Access password for "+credential.Username+" is empty!")
+					return
+				}
+			} else {
+				//This username have given password
+				mergedCredentials = append(mergedCredentials, &dynamicproxy.BasicAuthCredentials{
+					Username:     credential.Username,
+					PasswordHash: auth.Hash(credential.Password),
+				})
+			}
+		}
+
+		targetProxy.BasicAuthCredentials = mergedCredentials
+
+		//Save it to file
+		thisProxyConfigRecord := Record{
+			ProxyType:            ptype,
+			Rootname:             targetProxy.RootOrMatchingDomain,
+			ProxyTarget:          targetProxy.Domain,
+			UseTLS:               targetProxy.RequireTLS,
+			SkipTlsValidation:    targetProxy.SkipCertValidations,
+			RequireBasicAuth:     targetProxy.RequireBasicAuth,
+			BasicAuthCredentials: targetProxy.BasicAuthCredentials,
+		}
+		SaveReverseProxyConfig(&thisProxyConfigRecord)
+
+		//Replace runtime configuration
+		dynamicProxyRouter.SaveProxy(ptype, ep, targetProxy)
+		utils.SendOK(w)
+	} else {
+		http.Error(w, "invalid usage", http.StatusMethodNotAllowed)
+	}
+
+}
+
 func ReverseProxyStatus(w http.ResponseWriter, r *http.Request) {
 func ReverseProxyStatus(w http.ResponseWriter, r *http.Request) {
 	js, _ := json.Marshal(dynamicProxyRouter)
 	js, _ := json.Marshal(dynamicProxyRouter)
 	utils.SendJSONResponse(w, string(js))
 	utils.SendJSONResponse(w, string(js))

+ 1 - 1
web/components/cert.html

@@ -100,11 +100,11 @@
 
 
     //List the stored certificates
     //List the stored certificates
     function initManagedDomainCertificateList(){
     function initManagedDomainCertificateList(){
-        $("#certifiedDomainList").html("");
         $.get("/api/cert/list?date=true", function(data){
         $.get("/api/cert/list?date=true", function(data){
             if (data.error != undefined){
             if (data.error != undefined){
                 msgbox(data.error, false, 5000);
                 msgbox(data.error, false, 5000);
             }else{
             }else{
+                $("#certifiedDomainList").html("");
                 data.forEach(entry => {
                 data.forEach(entry => {
                     $("#certifiedDomainList").append(`<tr>
                     $("#certifiedDomainList").append(`<tr>
                         <td>${entry.Domain}</td>
                         <td>${entry.Domain}</td>

+ 14 - 16
web/components/rules.html

@@ -311,7 +311,7 @@
         var payload = $(row).attr("payload");
         var payload = $(row).attr("payload");
         payload = JSON.parse(decodeURIComponent(payload));
         payload = JSON.parse(decodeURIComponent(payload));
      
      
-        console.log(payload);
+        //console.log(payload);
         columns.each(function(index) {
         columns.each(function(index) {
             var column = $(this);
             var column = $(this);
             var oldValue = column.text().trim();
             var oldValue = column.text().trim();
@@ -363,35 +363,25 @@
                 column.empty().append(`<div class="ui checkbox" style="margin-top: 0.4em;">
                 column.empty().append(`<div class="ui checkbox" style="margin-top: 0.4em;">
                     <input type="checkbox" class="RequireBasicAuth" ${checkstate}>
                     <input type="checkbox" class="RequireBasicAuth" ${checkstate}>
                     <label>Require Basic Auth</label>
                     <label>Require Basic Auth</label>
-                </div><button class="ui basic tiny right floated button"><i class="ui blue lock icon"></i> Edit Credentials</button>`);
+                </div> <button class="ui basic tiny button" style="margin-left: 0.4em;" onclick="editBasicAuthCredentials('${endpointType}','${uuid}');"><i class="ui blue lock icon"></i> Edit Credentials</button>`);
 
 
             }else if (datatype == 'action'){
             }else if (datatype == 'action'){
                 column.empty().append(`
                 column.empty().append(`
-                <button title="Cancel" onclick="exitProxyInlineEdit();" class="ui basic small circular icon button"><i class="ui remove icon"></i></button>
+                <button title="Cancel" onclick="exitProxyInlineEdit('${endpointType}');" class="ui basic small circular icon button"><i class="ui remove icon"></i></button>
                 <button title="Save" onclick="saveProxyInlineEdit('${uuid}');" class="ui basic small circular icon button"><i class="ui green save icon"></i></button>
                 <button title="Save" onclick="saveProxyInlineEdit('${uuid}');" class="ui basic small circular icon button"><i class="ui green save icon"></i></button>
                 `);
                 `);
             }else{
             }else{
                 //Unknown field. Leave it untouched
                 //Unknown field. Leave it untouched
-
-            }
-            if (column.find('i.green.check.icon').length > 0) {
-                // Create a checkbox input for boolean options
-                var checked = (oldValue === '✓'); // Assuming '✓' indicates a checked state
-                input = $('<input>').attr('type', 'checkbox').prop('checked', checked);
-            } else {
-                // Create a regular text input for other fields
-                input = $('<input>').attr('type', 'text').val(oldValue);
             }
             }
-
-            // Set focus on the input element
-            input.focus();
-
         });
         });
+
+        $("#" + endpointType).find(".editBtn").addClass("disabled");
     }
     }
 
 
     function exitProxyInlineEdit(){
     function exitProxyInlineEdit(){
         listSubd();
         listSubd();
         listVdirs();
         listVdirs();
+        $("#" + endpointType).find(".editBtn").removeClass("disabled");
     }
     }
 
 
     function saveProxyInlineEdit(uuid){
     function saveProxyInlineEdit(uuid){
@@ -439,4 +429,12 @@
             }
             }
         })
         })
     }
     }
+    
+    function editBasicAuthCredentials(endpointType, uuid){
+        let payload = encodeURIComponent(JSON.stringify({
+            ept: endpointType,
+            ep: uuid
+        }));
+        showSideWrapper("snippet/basicAuthEditor.html?t=" + Date.now() + "#" + payload);
+    }
 </script>
 </script>

+ 3 - 2
web/components/subd.html

@@ -22,6 +22,7 @@
     <button class="ui icon right floated basic button" onclick="listSubd();"><i class="green refresh icon"></i> Refresh</button>
     <button class="ui icon right floated basic button" onclick="listSubd();"><i class="green refresh icon"></i> Refresh</button>
     <br><br>
     <br><br>
 </div>
 </div>
+
 <script>
 <script>
     function listSubd(){
     function listSubd(){
         $("#subdList").html(``);
         $("#subdList").html(``);
@@ -47,14 +48,14 @@
                         <td data-label="" editable="true" datatype="skipver">${!subd.SkipCertValidations?`<i class="ui green check icon"></i>`:`<i class="ui yellow exclamation circle icon" title="TLS/SSL Verification will be skipped on this host"></i>`}</td>
                         <td data-label="" editable="true" datatype="skipver">${!subd.SkipCertValidations?`<i class="ui green check icon"></i>`:`<i class="ui yellow exclamation circle icon" title="TLS/SSL Verification will be skipped on this host"></i>`}</td>
                         <td data-label="" editable="true" datatype="basicauth">${subd.RequireBasicAuth?`<i class="ui green check icon"></i>`:`<i class="ui grey remove icon"></i>`}</td>
                         <td data-label="" editable="true" datatype="basicauth">${subd.RequireBasicAuth?`<i class="ui green check icon"></i>`:`<i class="ui grey remove icon"></i>`}</td>
                         <td class="center aligned" editable="true" datatype="action" data-label="">
                         <td class="center aligned" editable="true" datatype="action" data-label="">
-                            <button class="ui circular mini basic icon button" onclick='editEndpoint("subd","${subd.RootOrMatchingDomain}")'><i class="edit icon"></i></button>
+                            <button class="ui circular mini basic icon button editBtn" onclick='editEndpoint("subd","${subd.RootOrMatchingDomain}")'><i class="edit icon"></i></button>
                             <button class="ui circular mini red basic icon button" onclick='deleteEndpoint("subd","${subd.RootOrMatchingDomain}")'><i class="trash icon"></i></button>
                             <button class="ui circular mini red basic icon button" onclick='deleteEndpoint("subd","${subd.RootOrMatchingDomain}")'><i class="trash icon"></i></button>
                         </td>
                         </td>
                     </tr>`);
                     </tr>`);
                 });
                 });
             }
             }
         });
         });
-    }
+    }   
 
 
     //Bind on tab switch events
     //Bind on tab switch events
     tabSwitchEventBind["subd"] = function(){
     tabSwitchEventBind["subd"] = function(){

+ 1 - 1
web/components/vdir.html

@@ -50,7 +50,7 @@
                         <td data-label="" editable="true" datatype="skipver">${!vdir.SkipCertValidations?`<i class="ui green check icon"></i>`:`<i class="ui yellow exclamation circle icon" title="TLS/SSL Verification will be skipped on this host"></i>`}</td>
                         <td data-label="" editable="true" datatype="skipver">${!vdir.SkipCertValidations?`<i class="ui green check icon"></i>`:`<i class="ui yellow exclamation circle icon" title="TLS/SSL Verification will be skipped on this host"></i>`}</td>
                         <td data-label="" editable="true" datatype="basicauth">${vdir.RequireBasicAuth?`<i class="ui green check icon"></i>`:`<i class="ui grey remove icon"></i>`}</td>
                         <td data-label="" editable="true" datatype="basicauth">${vdir.RequireBasicAuth?`<i class="ui green check icon"></i>`:`<i class="ui grey remove icon"></i>`}</td>
                         <td class="center aligned" editable="true" datatype="action" data-label="">
                         <td class="center aligned" editable="true" datatype="action" data-label="">
-                            <button class="ui circular mini basic icon button" onclick='editEndpoint("vdir","${vdir.RootOrMatchingDomain}")'><i class="edit icon"></i></button>
+                            <button class="ui circular mini basic icon button editBtn" onclick='editEndpoint("vdir","${vdir.RootOrMatchingDomain}")'><i class="edit icon"></i></button>
                             <button class="ui circular mini red basic icon button"  onclick='deleteEndpoint("vdir","${vdir.RootOrMatchingDomain}")'><i class="trash icon"></i></button>
                             <button class="ui circular mini red basic icon button"  onclick='deleteEndpoint("vdir","${vdir.RootOrMatchingDomain}")'><i class="trash icon"></i></button>
                         </td>
                         </td>
                     </tr>`);
                     </tr>`);

+ 37 - 1
web/index.html

@@ -131,6 +131,13 @@
             </div>
             </div>
             </div>
             </div>
         </div>
         </div>
+        <div class="sideWrapper" style="display:none;">
+            <div class="fadingBackground" onclick="hideSideWrapper();"></div>
+            <div class="content">
+                <div class="sideWrapperMenu"></div>
+                <iframe src=""></iframe>
+            </div>
+        </div>
         <br><br>
         <br><br>
         <div class="ui divider"></div>
         <div class="ui divider"></div>
         <div class="ui container" style="color: grey; font-size: 90%">
         <div class="ui container" style="color: grey; font-size: 90%">
@@ -251,7 +258,6 @@
                 });
                 });
                 $('html,body').animate({scrollTop: 0}, 'fast');
                 $('html,body').animate({scrollTop: 0}, 'fast');
                 window.location.hash = tabID;
                 window.location.hash = tabID;
-
             }
             }
 
 
             $(window).on("resize", function(){
             $(window).on("resize", function(){
@@ -274,6 +280,36 @@
                 $("#messageBox").stop().finish().fadeIn("fast").delay(delayDuration).fadeOut("fast");
                 $("#messageBox").stop().finish().fadeIn("fast").delay(delayDuration).fadeOut("fast");
             }
             }
 
 
+            /*
+                Toggles for side wrapper
+            */
+
+            function showSideWrapper(scriptpath=""){
+                if (scriptpath != ""){
+                    $(".sideWrapper iframe").attr("src", scriptpath);
+                }
+
+                if ($(".sideWrapper .content").transition("is animating") || $(".sideWrapper .content").transition("is visible")){
+                    return
+                }
+                $(".sideWrapper").show();
+                $(".sideWrapper .fadingBackground").fadeIn("fast");
+                $(".sideWrapper .content").transition('slide left in', 300);
+            }
+
+            function hideSideWrapper(discardFrameContent = false){
+                if (discardFrameContent){
+                    $(".sideWrapper iframe").attr("src", "about:blank");
+                }
+                if ($(".sideWrapper .content").transition("is animating") || !$(".sideWrapper .content").transition("is visible")){
+                    return
+                }
+                $(".sideWrapper .content").transition('slide left out', 300, function(){
+                    $(".sideWrapper .fadingBackground").fadeOut("fast", function(){
+                        $(".sideWrapper").hide(); 
+                    });
+                });
+            }
         </script>
         </script>
     </body>
     </body>
 </html>
 </html>

+ 38 - 0
web/main.css

@@ -119,6 +119,44 @@ body{
     padding-bottom: 0;
     padding-bottom: 0;
 }
 }
 
 
+/*
+    Side Wrapper
+*/
+.sideWrapper{
+    position: fixed;
+    right: 0;
+    top: 52px;
+    height: 100%;
+    width: calc(100% - 5em);
+    max-width: 500px;
+}
+
+.sideWrapper .content{
+    height: 100%;
+    width: 100%;
+    position: relative;
+    background-color: white;
+}
+
+.sideWrapper iframe{
+    height: 100%;
+    width: 100%;
+    border: 0px solid transparent;
+}
+
+.sideWrapper .fadingBackground{
+    position: fixed;
+    top: 0;
+    left: 0;
+    width: 100%;
+    height: 100%;
+    background-color: rgba(38,38,38,0.26);
+}
+
+.sideWrapperMenu{
+    height: 3px;
+    background-color: #414141;
+}
 
 
 /*
 /*
     RWD Rules
     RWD Rules

+ 170 - 0
web/snippet/basicAuthEditor.html

@@ -0,0 +1,170 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <!-- Notes: This should be open in its original path-->
+        <link rel="stylesheet" href="../script/semantic/semantic.min.css">
+        <script src="../script/jquery-3.6.0.min.js"></script>
+        <script src="../script/semantic/semantic.min.js"></script>
+    </head>
+    <body>
+        <br>
+        <div class="ui container">
+            <div class="ui header">
+                <i class="lock icon"></i>
+                <div class="content">
+                    Basic Auth Credential
+                    <div class="sub header"></div>
+                </div>
+            </div>
+            <div class="scrolling content ui form">
+                <div id="inlineEditBasicAuthCredentials" class="field">
+                    <p>Enter the username and password for allowing them to access this proxy endpoint</p>
+                    <table class="ui very basic compacted unstackable celled table">
+                        <thead>
+                        <tr>
+                            <th>Username</th>
+                            <th>Password</th>
+                            <th>Remove</th>
+                        </tr></thead>
+                        <tbody id="inlineEditBasicAuthCredentialTable">
+                        <tr>
+                            <td colspan="3"><i class="ui green circle check icon"></i> No Entered Credential</td>
+                        </tr>
+                        </tbody>
+                    </table>
+                    <div class="three small fields credentialEntry">
+                        <div class="field">
+                            <input id="inlineEditBasicAuthCredUsername" type="text" placeholder="Username" autocomplete="off">
+                        </div>
+                        <div class="field">
+                            <input id="inlineEditBasicAuthCredPassword" type="password" placeholder="Password" autocomplete="off">
+                        </div>
+                        <div class="field" >
+                            <button class="ui basic button" style="float: right;" onclick="addCredentialsToEditingList();"><i class="blue add icon"></i> Add Credential</button>
+                            <button class="ui basic button"  style="float: right;" onclick="cancelCredentialEdit();"><i class="remove icon"></i> Cancel</button>
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </div>
+        <script>
+            let editingCredentials = [];
+
+            if (window.location.hash.length > 1){
+                let payloadHash = window.location.hash.substr(1);
+                try{
+                    payloadHash = JSON.parse(decodeURIComponent(payloadHash));
+                    loadBasicAuthCredentials(payloadHash.ept, payloadHash.ep);
+                }catch(ex){
+                    console.log("Unable to load endpoint data from hash")
+                }
+            }
+
+            function loadBasicAuthCredentials(endpointType, uuid){
+                $.ajax({
+                    url: "/api/proxy/updateCredentials",
+                    method: "GET",
+                    data: {
+                        ep: uuid,
+                        ptype: endpointType
+                    },
+                    success: function(data){
+                        //Push the existing account to list
+                        for(var i = 0; i < data.length; i++){
+                            // Create a new credential object
+                            var credential = {
+                                username: data[i],
+                                password: ""
+                            };
+
+                            // Add the credential to the global credentials array
+                            editingCredentials.push(credential);
+                        }
+                        console.log(data);
+                        updateEditingCredentialList();
+                    }
+                })
+            }
+
+            function addCredentialsToEditingList() {
+                // Retrieve the username and password input values
+                var username = $('#inlineEditBasicAuthCredUsername').val();
+                var password = $('#inlineEditBasicAuthCredPassword').val();
+                
+                if(username == "" || password == ""){
+                    parent.msgbox("Username or password cannot be empty", false, 5000);
+                    return;
+                }
+
+                if (alreadyExists(username)){
+                    parent.msgbox("Credential with same username already exists", false, 5000);
+                    return;
+                }
+                
+                // Create a new credential object
+                var credential = {
+                    username: username,
+                    password: password
+                };
+
+                // Add the credential to the global credentials array
+                editingCredentials.push(credential);
+
+                // Clear the input fields
+                $('#inlineEditBasicAuthCredUsername').val('');
+                $('#inlineEditBasicAuthCredPassword').val('');
+
+                // Update the table body with the credentials
+                updateEditingCredentialList();
+            }
+
+            function updateEditingCredentialList() {
+                var tableBody = $('#inlineEditBasicAuthCredentialTable');
+                tableBody.empty();
+
+                if (editingCredentials.length === 0) {
+                    tableBody.append('<tr><td colspan="3"><i class="ui green circle check icon"></i> No Entered Credential</td></tr>');
+                } else {
+                    for (var i = 0; i < editingCredentials.length; i++) {
+                    var credential = editingCredentials[i];
+                    var username = credential.username;
+                    var password = credential.password.replace(/./g, '*'); // Replace each character with '*'
+                    
+                    if (credential.password == ""){
+                        password = `<span style="color: #c9c9c9;"><i class="eye slash outline icon"></i> Hidden<span>`;
+                    }
+                    var row = '<tr>' +
+                        '<td>' + username + '</td>' +
+                        '<td>' + password + '</td>' +
+                        '<td><button class="ui basic button" onclick="removeCredentialFromEditingList(' + i + ');"><i class="red remove icon"></i> Remove</button></td>' +
+                        '</tr>';
+
+                    tableBody.append(row);
+                    }
+                }
+            }
+
+            function removeCredentialFromEditingList(index) {
+                // Remove the credential from the credentials array
+                editingCredentials.splice(index, 1);
+
+                // Update the table body
+                updateEditingCredentialList();
+            }
+
+            function alreadyExists(username){
+                let isExists = false;
+                editingCredentials.forEach(function(cred){
+                    if (cred.username == username){
+                        isExists = true;
+                    }
+                });
+                return isExists;
+            }
+
+            function cancelCredentialEdit(){
+                parent.hideSideWrapper(true);
+            }
+        </script>
+    </body>
+</html>