Procházet zdrojové kódy

Added working permission policy editor

Toby Chui před 9 měsíci
rodič
revize
82173d1f34
4 změnil soubory, kde provedl 188 přidání a 20 odebrání
  1. 19 6
      mod/dynamicproxy/customHeader.go
  2. 33 0
      reverseproxy.go
  3. 1 1
      web/login.html
  4. 135 13
      web/snippet/customHeaders.html

+ 19 - 6
mod/dynamicproxy/customHeader.go

@@ -1,6 +1,10 @@
 package dynamicproxy
 
-import "strconv"
+import (
+	"strconv"
+
+	"imuslab.com/zoraxy/mod/dynamicproxy/permissionpolicy"
+)
 
 /*
 	CustomHeader.go
@@ -9,9 +13,9 @@ import "strconv"
 	into the dpcore routing logic
 */
 
-//SplitInboundOutboundHeaders split user defined headers into upstream and downstream headers
-//return upstream header and downstream header key-value pairs
-//if the header is expected to be deleted, the value will be set to empty string
+// SplitInboundOutboundHeaders split user defined headers into upstream and downstream headers
+// return upstream header and downstream header key-value pairs
+// if the header is expected to be deleted, the value will be set to empty string
 func (ept *ProxyEndpoint) SplitInboundOutboundHeaders() ([][]string, [][]string) {
 	if len(ept.UserDefinedHeaders) == 0 {
 		//Early return if there are no defined headers
@@ -52,8 +56,17 @@ func (ept *ProxyEndpoint) SplitInboundOutboundHeaders() ([][]string, [][]string)
 	}
 
 	//Check if the endpoint require Permission Policy
-	if ept.EnablePermissionPolicyHeader && ept.PermissionPolicy != nil {
-		downstreamHeaders[downstreamHeaderCounter] = ept.PermissionPolicy.ToKeyValueHeader()
+	if ept.EnablePermissionPolicyHeader {
+		var usingPermissionPolicy *permissionpolicy.PermissionsPolicy
+		if ept.PermissionPolicy != nil {
+			//Custom permission policy
+			usingPermissionPolicy = ept.PermissionPolicy
+		} else {
+			//Permission policy is enabled but not customized. Use default
+			usingPermissionPolicy = permissionpolicy.GetDefaultPermissionPolicy()
+		}
+
+		downstreamHeaders[downstreamHeaderCounter] = usingPermissionPolicy.ToKeyValueHeader()
 		downstreamHeaderCounter++
 	}
 

+ 33 - 0
reverseproxy.go

@@ -1314,7 +1314,40 @@ func HandlePermissionPolicy(w http.ResponseWriter, r *http.Request) {
 		utils.SendJSONResponse(w, string(js))
 		return
 	} else if r.Method == http.MethodPost {
+		//Update the enable state of permission policy
+		enableState, err := utils.PostBool(r, "enable")
+		if err != nil {
+			utils.SendErrorResponse(w, "invalid enable state given")
+			return
+		}
+
+		targetProxyEndpoint.EnablePermissionPolicyHeader = enableState
+		SaveReverseProxyConfig(targetProxyEndpoint)
+		targetProxyEndpoint.UpdateToRuntime()
+		utils.SendOK(w)
+		return
+	} else if r.Method == http.MethodPut {
+		//Store the new permission policy
+		newPermissionPolicyJSONString, err := utils.PostPara(r, "pp")
+		if err != nil {
+			utils.SendErrorResponse(w, "missing pp (permission policy) paramter")
+			return
+		}
+
+		//Parse the permission policy from JSON string
+		newPermissionPolicy := permissionpolicy.GetDefaultPermissionPolicy()
+		err = json.Unmarshal([]byte(newPermissionPolicyJSONString), &newPermissionPolicy)
+		if err != nil {
+			utils.SendErrorResponse(w, "permission policy parse error: "+err.Error())
+			return
+		}
 
+		//Save it to file
+		targetProxyEndpoint.PermissionPolicy = newPermissionPolicy
+		SaveReverseProxyConfig(targetProxyEndpoint)
+		targetProxyEndpoint.UpdateToRuntime()
+		utils.SendOK(w)
+		return
 	}
 
 	http.Error(w, "405 - Method not allowed", http.StatusMethodNotAllowed)

+ 1 - 1
web/login.html

@@ -114,7 +114,7 @@
                 min-height:40px;
             }
         }
-
+        
     </style>
     </head>
     <body>

+ 135 - 13
web/snippet/customHeaders.html

@@ -16,6 +16,10 @@
                 pointer-events: none;
                 user-select: none;
             }
+
+            #permissionPolicyEditor .experimental{
+                background-color: rgb(241, 241, 241);
+            }
         </style>
     </head>
     <body>
@@ -105,16 +109,14 @@
                         </tr></thead>
                         <tbody id="permissionPolicyEditTable">
                           <tr>
-                            <td>James</td>
-                            <td>24</td>
-                            <td>Engineer</td>
-                            <td>Engineer</td>
+                            <td colspan="4"><i class="ui loading spinner icon"></i> Generating</td>
                           </tr>
                         </tbody>
                       </table>
                 </div>
-                <br>
-                <button class="ui basic button"><i class="green save icon"></i> Save</button>
+                <small><i class="ui yellow exclamation triangle icon"></i> Grey out fields are non-standard permission policies</small>
+                <br><br>
+                <button class="ui basic button" onclick="savePermissionPolicy();"><i class="green save icon"></i> Save</button>
             </div>
            
             <div class="field" >
@@ -126,6 +128,7 @@
 
         <script>
             $('.menu .item').tab();
+            let permissionPolicyKeys = [];
 
             let editingEndpoint = {};
             if (window.location.hash.length > 1){
@@ -331,6 +334,33 @@
             }
             initHSTSState();
 
+            //Return true if this is an proposed permission policy feature
+            function isExperimentalFeature(header) {
+                // List of experimental features
+                const experimentalFeatures = [
+                    "clipboard-read",
+                    "clipboard-write",
+                    "gamepad",
+                    "speaker-selection",
+                    "conversion-measurement",
+                    "focus-without-user-activation",
+                    "hid",
+                    "idle-detection",
+                    "interest-cohort",
+                    "serial",
+                    "sync-script",
+                    "trust-token-redemption",
+                    "unload",
+                    "window-placement",
+                    "vertical-scroll"
+                ];
+
+                header = header.replaceAll("_","-");
+
+                // Check if the header is in the list of experimental features
+                return experimentalFeatures.includes(header);
+            }
+
             /* List permission policy header from server */
             function initPermissionPolicy(){
                 $.get("/api/proxy/header/handlePermissionPolicy?domain=" + editingEndpoint.ep, function(data){
@@ -340,7 +370,7 @@
                         return;
                     }
 
-                    //Set checkbox state
+                    //Set checkbox initial state
                     if (data.PPEnabled){
                         $("#enablePP").parent().checkbox("set checked");
                         $("#permissionPolicyEditor").removeClass("disabled");
@@ -349,6 +379,33 @@
                         $("#permissionPolicyEditor").addClass("disabled");
                     }
 
+                    //Bind toggle change events
+                    $("#enablePP").on("change", function(evt){
+                        //Set checkbox state
+                        let ppEnabled = $("#enablePP")[0].checked;
+                        if (ppEnabled){
+                            $("#permissionPolicyEditor").removeClass("disabled");
+                        }else{
+                            $("#permissionPolicyEditor").addClass("disabled");
+                        }
+
+                        $.ajax({
+                            url: "/api/proxy/header/handlePermissionPolicy",
+                            method: "POST",
+                            data: {
+                                enable: ppEnabled,
+                                domain: editingEndpoint.ep
+                            },
+                            success: function(data){
+                                if (data.error != undefined){
+                                    parent.msgbox(data.error, false);
+                                }else{
+                                    parent.msgbox(`Permission Policy ${ppEnabled?"Enabled":"Disabled"}`)
+                                }
+                            }
+                        })
+                    });
+
                     //Render the table to list
                     $("#permissionPolicyEditTable").html("");
                     for (const [key, value] of Object.entries(data.CurrentPolicy)) {
@@ -363,9 +420,12 @@
 
                         if (value.length == 0){
                             enabled = ""
+                            allowall = "checked"; //default state
                         }
-                        $("#permissionPolicyEditTable").append(`<tr>
-                            <td>${key}</td>
+
+                        let isExperimental = isExperimentalFeature(key);
+                        $("#permissionPolicyEditTable").append(`<tr class="${isExperimental?"experimental":""}">
+                            <td>${key.replaceAll("_","-")}</td>
                             <td>
                                 <div class="ui checkbox">
                                     <input class="enabled" type="checkbox" name="${key}" ${enabled}>
@@ -373,22 +433,84 @@
                                 </div>
                             </td>
                             <td>
-                                <div class="ui radio checkbox">
-                                    <input type="radio" value="all" name="${key}-target" ${allowall}>
+                                <div class="ui radio checkbox targetinput ${!enabled?"disabled":""}">
+                                    <input type="radio" value="all" name="${key}-target" ${allowall} ${!enabled?"disabled=\"\"":""}>
                                     <label></label>
                                 </div>
                             </td>
                             <td>
-                                <div class="ui radio checkbox">
-                                    <input type="radio" value="self" name="${key}-target" ${allowself}>
+                                <div class="ui radio checkbox targetinput ${!enabled?"disabled":""}">
+                                    <input type="radio" value="self" name="${key}-target" ${allowself} ${!enabled?"disabled=\"\"":""}>
                                     <label></label>
                                 </div>
                             </td>
                         </tr>`);
+                        
+                        permissionPolicyKeys.push(key);
                     }
+
+                    $("#permissionPolicyEditTable .enabled").on("change", function(){
+                        console.log($(this)[0].checked);
+                        let fieldGroup = $(this).parent().parent().parent();
+                        if ($(this)[0].checked){
+                            fieldGroup.find(".targetinput").removeClass("disabled");
+                            fieldGroup.find("input[type=radio]").prop('disabled', false);
+                        }else{
+                            fieldGroup.find(".targetinput").addClass("disabled");
+                            fieldGroup.find("input[type=radio]").prop('disabled', true);
+                        }
+                    })
                 });
             }
             initPermissionPolicy();
+
+            //Generate the permission policy object for sending to backend
+            function generatePermissionPolicyObject(){
+                function getStructuredFieldValueFromDOM(fieldKey){
+                    var policyTarget = $(`#permissionPolicyEditTable input[name="${fieldKey}-target"]:checked`).val();
+                    var isPolicyEnabled = $(`#permissionPolicyEditTable input[name="${fieldKey}"]`).is(':checked');
+                    
+                    if (!isPolicyEnabled){
+                        return [];
+                    }
+
+                    if (policyTarget == "all"){
+                        //Rewrite all to correct syntax
+                        policyTarget = "*";
+                    }
+                    return [policyTarget];
+                }
+
+                let newPermissionPolicyKeyValuePair = {};
+                permissionPolicyKeys.forEach(policyKey => {
+                    newPermissionPolicyKeyValuePair[policyKey] = getStructuredFieldValueFromDOM(policyKey);
+                });
+
+                console.log(newPermissionPolicyKeyValuePair);
+                return newPermissionPolicyKeyValuePair;
+            }
+
+            //Handle saving of permission policy
+            function savePermissionPolicy(){
+                let permissionPolicy = generatePermissionPolicyObject();
+                let domain = editingEndpoint.ep;
+
+                $.ajax({
+                    url: "/api/proxy/header/handlePermissionPolicy",
+                    method: "PUT",
+                    data: {
+                        "domain": domain,
+                        "pp": JSON.stringify(permissionPolicy),
+                    },
+                    success: function(data){
+                        if (data.error != undefined){
+                            parent.msgbox(data.error, false);
+                        }else{
+                            parent.msgbox("Permission Policy Updated");
+                        }
+                    }
+                })
+            }
         </script>
     </body>
 </html>