customHeaders.html 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <!-- Notes: This should be open in its original path-->
  5. <link rel="stylesheet" href="../script/semantic/semantic.min.css">
  6. <script src="../script/jquery-3.6.0.min.js"></script>
  7. <script src="../script/semantic/semantic.min.js"></script>
  8. <style>
  9. .ui.tabular.menu .item.narrowpadding{
  10. padding: 0.6em !important;
  11. margin: 0.15em !important;
  12. }
  13. </style>
  14. </head>
  15. <body>
  16. <br>
  17. <div class="ui container">
  18. <div class="ui header">
  19. <div class="content">
  20. Custom Headers
  21. <div class="sub header" id="epname"></div>
  22. </div>
  23. </div>
  24. <div class="ui divider"></div>
  25. <div class="ui small pointing secondary menu">
  26. <a class="item active narrowpadding" data-tab="customheaders">Custom Headers</a>
  27. <a class="item narrowpadding" data-tab="security">Security Headers</a>
  28. </div>
  29. <div class="ui tab basic segment active" data-tab="customheaders">
  30. <table class="ui very basic compacted unstackable celled table">
  31. <thead>
  32. <tr>
  33. <th>Key</th>
  34. <th>Value</th>
  35. <th>Remove</th>
  36. </tr></thead>
  37. <tbody id="headerTable">
  38. <tr>
  39. <td colspan="3"><i class="ui green circle check icon"></i> No Additonal Header</td>
  40. </tr>
  41. </tbody>
  42. </table>
  43. <p>
  44. <i class="angle double right blue icon"></i> Sent additional custom headers to origin server <br>
  45. <i class="angle double left orange icon"></i> Inject custom headers into origin server responses
  46. </p>
  47. <div class="ui divider"></div>
  48. <h4>Edit Custom Header</h4>
  49. <p>Add or remove custom header(s) over this proxy target</p>
  50. <div class="scrolling content ui form">
  51. <div class="five small fields credentialEntry">
  52. <div class="field" align="center">
  53. <button id="toOriginButton" style="margin-top: 0.6em;" title="Downstream to Upstream" class="ui circular basic active button">Zoraxy <i class="angle double right blue icon" style="margin-right: 0.4em;"></i> Origin</button>
  54. <button id="toClientButton" style="margin-top: 0.6em;" title="Upstream to Downstream" class="ui circular basic button">Client <i class="angle double left orange icon" style="margin-left: 0.4em;"></i> Zoraxy</button>
  55. </div>
  56. <div class="field" align="center">
  57. <button id="headerModeAdd" style="margin-top: 0.6em;" class="ui circular basic active button"><i class="ui green circle add icon"></i> Add Header</button>
  58. <button id="headerModeRemove" style="margin-top: 0.6em;" class="ui circular basic button"><i class="ui red circle times icon"></i> Remove Header</button>
  59. </div>
  60. <div class="field">
  61. <label>Header Key</label>
  62. <input id="headerName" type="text" placeholder="X-Custom-Header" autocomplete="off">
  63. <small>The header key is <b>NOT</b> case sensitive</small>
  64. </div>
  65. <div class="field">
  66. <label>Header Value</label>
  67. <input id="headerValue" type="text" placeholder="value1,value2,value3" autocomplete="off">
  68. </div>
  69. <div class="field" >
  70. <button class="ui basic button" onclick="addCustomHeader();"><i class="green add icon"></i> Add Header Rewrite Rule</button>
  71. </div>
  72. <div class="ui divider"></div>
  73. </div>
  74. </div>
  75. </div>
  76. <div class="ui tab basic segment" data-tab="security">
  77. <h4>HTTP Strict Transport Security</h4>
  78. <p>Force future attempts to access this site to only use HTTPS</p>
  79. <div class="ui toggle checkbox">
  80. <input type="checkbox" id="enableHSTS" name="enableHSTS">
  81. <label>Enable HSTS<br>
  82. <small>HSTS header will be automatically ignored if the site is accessed using HTTP</small></label>
  83. </div>
  84. <div class="ui divider"></div>
  85. <h4>Permission Policy</h4>
  86. <p>Explicitly declare what functionality can and cannot be used on this website. </p>
  87. <div class="ui toggle checkbox" style="margin-top: 0.6em;">
  88. <input type="checkbox" name="enableHSTS">
  89. <label>Enable Permission Policy<br>
  90. <small>Enable Permission-Policy header with all allowed state.</small></label>
  91. </div>
  92. <div id="permissionPolicyEditTable">
  93. </div>
  94. <br><br>
  95. <button class="ui basic button"><i class="green save icon"></i> Save</button>
  96. </div>
  97. <div class="field" >
  98. <button class="ui basic button" style="float: right;" onclick="closeThisWrapper();">Close</button>
  99. </div>
  100. </div>
  101. <br><br><br><br>
  102. <script>
  103. $('.menu .item').tab();
  104. let editingEndpoint = {};
  105. if (window.location.hash.length > 1){
  106. let payloadHash = window.location.hash.substr(1);
  107. try{
  108. payloadHash = JSON.parse(decodeURIComponent(payloadHash));
  109. $("#epname").text(payloadHash.ep);
  110. editingEndpoint = payloadHash;
  111. }catch(ex){
  112. console.log("Unable to load endpoint data from hash")
  113. }
  114. }
  115. function closeThisWrapper(){
  116. parent.hideSideWrapper(true);
  117. }
  118. //Bind events to header mod mode
  119. $("#headerModeAdd").on("click", function(){
  120. $("#headerModeAdd").addClass("active");
  121. $("#headerModeRemove").removeClass("active");
  122. $("#headerValue").parent().show();
  123. });
  124. $("#headerModeRemove").on("click", function(){
  125. $("#headerModeAdd").removeClass("active");
  126. $("#headerModeRemove").addClass("active");
  127. $("#headerValue").parent().hide();
  128. $("#headerValue").val("");
  129. });
  130. //Bind events to header directions option
  131. $("#toOriginButton").on("click", function(){
  132. $("#toOriginButton").addClass("active");
  133. $("#toClientButton").removeClass("active");
  134. });
  135. $("#toClientButton").on("click", function(){
  136. $("#toOriginButton").removeClass("active");
  137. $("#toClientButton").addClass("active");
  138. });
  139. //Return "add" or "remove" depending on mode user selected
  140. function getHeaderEditMode(){
  141. if ($("#headerModeAdd").hasClass("active")){
  142. return "add";
  143. }
  144. return "remove";
  145. }
  146. //Return "toOrigin" or "toClient"
  147. function getHeaderDirection(){
  148. if ($("#toOriginButton").hasClass("active")){
  149. return "toOrigin";
  150. }
  151. return "toClient";
  152. }
  153. //$("#debug").text(JSON.stringify(editingEndpoint));
  154. function addCustomHeader(){
  155. let name = $("#headerName").val().trim();
  156. let value = $("#headerValue").val().trim();
  157. if (name == ""){
  158. $("#headerName").parent().addClass("error");
  159. return
  160. }else{
  161. $("#headerName").parent().removeClass("error");
  162. }
  163. if (getHeaderEditMode() == "add"){
  164. if (value == ""){
  165. $("#headerValue").parent().addClass("error");
  166. return
  167. }else{
  168. $("#headerValue").parent().removeClass("error");
  169. }
  170. }
  171. $.ajax({
  172. url: "/api/proxy/header/add",
  173. data: {
  174. "type": getHeaderEditMode(),
  175. "domain": editingEndpoint.ep,
  176. "direction":getHeaderDirection(),
  177. "name": name,
  178. "value": value
  179. },
  180. success: function(data){
  181. if (data.error != undefined){
  182. if (parent != undefined && parent.msgbox != undefined){
  183. parent.msgbox(data.error,false);
  184. }else{
  185. alert(data.error);
  186. }
  187. }else{
  188. listCustomHeaders();
  189. if (parent != undefined && parent.msgbox != undefined){
  190. parent.msgbox("Custom header added",true);
  191. }
  192. //Clear the form
  193. $("#headerName").val("");
  194. $("#headerValue").val("");
  195. }
  196. }
  197. });
  198. }
  199. function deleteCustomHeader(name){
  200. $.ajax({
  201. url: "/api/proxy/header/remove",
  202. data: {
  203. //"type": editingEndpoint.ept,
  204. "domain": editingEndpoint.ep,
  205. "name": name,
  206. },
  207. success: function(data){
  208. listCustomHeaders();
  209. if (parent != undefined && parent.msgbox != undefined){
  210. parent.msgbox("Custom header removed",true);
  211. }
  212. }
  213. });
  214. }
  215. function listCustomHeaders(){
  216. $("#headerTable").html(`<tr><td colspan="3"><i class="ui loading spinner icon"></i> Loading</td></tr>`);
  217. $.ajax({
  218. url: "/api/proxy/header/list",
  219. data: {
  220. "type": editingEndpoint.ept,
  221. "domain": editingEndpoint.ep,
  222. },
  223. success: function(data){
  224. if (data.error != undefined){
  225. alert(data.error);
  226. }else{
  227. $("#headerTable").html("");
  228. data.forEach(header => {
  229. let editModeIcon = header.IsRemove?`<i class="ui red times circle icon"></i>`:`<i class="ui green add circle icon"></i>`;
  230. let direction = (header.Direction==0)?`<i class="angle double right blue icon"></i>`:`<i class="angle double left orange icon"></i>`;
  231. let valueField = header.Value;
  232. if (header.IsRemove){
  233. valueField = "<small style='color: grey;'>(Field Removed)</small>";
  234. }
  235. $("#headerTable").append(`
  236. <tr>
  237. <td>${direction} ${header.Key}</td>
  238. <td>${editModeIcon} ${valueField}</td>
  239. <td><button class="ui basic circular mini red icon button" onclick="deleteCustomHeader('${header.Key}');"><i class="ui trash icon"></i></button></td>
  240. </tr>
  241. `);
  242. });
  243. if (data.length == 0){
  244. $("#headerTable").html(`<tr>
  245. <td colspan="3"><i class="ui green circle check icon"></i> No Additonal Header</td>
  246. </tr>`);
  247. }
  248. }
  249. },
  250. });
  251. }
  252. listCustomHeaders();
  253. /* Bind events to toggles */
  254. $.get("/api/proxy/header/handleHSTS?domain=" + editingEndpoint.ep, function(data){
  255. if (data == 0){
  256. //HSTS disabled
  257. $("#enableHSTS").parent().checkbox("set unchecked");
  258. }else{
  259. //HSTS enabled
  260. $("#enableHSTS").parent().checkbox("set checked");
  261. }
  262. $("#enableHSTS").on("change", function(){
  263. let HSTSEnabled = $("#enableHSTS")[0].checked;
  264. $.ajax({
  265. url: "/api/proxy/header/handleHSTS",
  266. method: "POST",
  267. data: {
  268. "domain": editingEndpoint.ep,
  269. "maxage": 31536000
  270. },
  271. success: function(data){
  272. if (data.error != undefined){
  273. parent.msgbox(data.error, false);
  274. }else{
  275. parent.msgbox(`HSTS ${HSTSEnabled?"Enabled":"Disabled"}`);
  276. }
  277. }
  278. })
  279. });
  280. });
  281. /* List permission policy header from server */
  282. </script>
  283. </body>
  284. </html>