bitslip6/bitfire

View on GitHub
firewall/views/wizard.html

Summary

Maintainability
Test Coverage
    <style>
      {{custom_css}}

      .form-switch {
        pargin-right:6rem;
      }
      #wizard_enable, #wizard_text {
        border: 1px solid #DDD;
        border-left: 0;
        border-right: 0;
        margin: 2rem 1rem 4rem 1rem;
      }
      #wizard_enable {
        padding: 0.5rem 2rem 2rem 2rem;
      }
      #wizard_text:not(.collapse) {
        display: flex;
      }
      #wizard_text_input { flex-grow: 1; max-height: 30px; }
      #wizard_text_label { flex-grow: 0; }
    </style>
       
    <script type="text/javascript">
        window.CONFIG_LIST = {};

      
        function rand_string(config_name, length = 32) {
            let result           = '';
            let characters       = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
            for ( var i = 0; i < length; i++ ) {
              result += characters.charAt(Math.floor(Math.random() * characters.length));
          }
          GBI(config_name+"_text").value = result;
          update_str(config_name);
        }

        function show_list(config_name) {
          let html = "";
          for (let i=0; i < domain_list.length; i++) {
            let id = config_name + "-" + i;
            html += '<div style="margin-bottom:5px;" id="item_'+id+'">';
            html += '<input type="text" autocomplete="off" disabled id="list_'+id+'" class="form-control txtin" style="width:80%;float:left;margin-right:10px" value="'+domain_list[i]+'">';
            html += '<div class="btn btn-danger" style="cursor:pointer;padding-top:10px;padding-left:10px;" title="remove list element" onclick="remove_list(\''+config_name+'\', \''+domain_list[i]+'\', '+i+')"><span class="fe fe-trash"></span></div>'; 
          }
          return html;
        }

        function remove_list(config_name, value, idx) {
          console.log("config name delete", config_name, value, idx);
          BitFire_api("remove_list_elm", {"config_name":config_name, "config_value":value, "index":idx})
            .then(r => r.json())
            .then(function(res) {
            if (res.success) {
              window.location.reload();
            } else {
              alert(res.note);
              window.location.reload();
            }
          });
        }

        function add_list(config_name) {
          let elm = GBI("new_"+config_name);
          BitFire_api("add_list_elm", {"config_name":config_name, "config_value":elm.value})
            .then(r => r.json())
            .then(function(res) {
            if (res.success) {
                window.location.reload();
            } else {
              alert(res.note);
              window.location.reload();
            }
          });
        }
                
        const VERSION = {{version}};
        const VERSION_STR = "{{sym_version}}";
        window.CTR = 0;
        
        function set_config(id, value) {
          console.log("set config", id, value);
        }

        function soption(config, value) {
          GBI("drop_"+config).innerText = value;
          return update_value(config, value);
        }

        function update_value(config, value) {
          GBI("spin").classList.remove("hidden");
          BitFire_api("toggle_config_value", {"param":config,"value":value})
            .then(r => r.json())
            .then(data => {
              window.CTR++;
              window.EN_IDX++;
              //console.log("api resp", window.CTR, window.EN_IDX);
              if (window.CTR == window.EN.length) {
                //console.log("window counter >= 5");
                window.setTimeout(function() {
                  update_value("wizard", "true");
                  GBI("spin").classList.add("hidden");
                  GBI("save_changes").innerHTML = "<a href='{{self}}?page=bitfire&tooltip=1' class='white' style='color:#FFF'>Tour Dashboard <span class='fe fe-chevron-right'></span></a> ";
                }, 200);
              } else if (window.EN_IDX < window.EN.length) {
                //console.log("update value", window.CTR, window.EN_IDX);
                window.setTimeout(function() {
                  update_value(window.EN[window.EN_IDX][0], window.EN[window.EN_IDX][1]);
                }, 100);
              }
            });
        }


        function update_str(config) {
          console.log("up", config);
          let e = GBI(config+"_text");
          if (e) {
            let value = e.value;
            console.log("update str", config, value);
            return update_value(config, value);
          } else {
            alert("error, unable to update " + config + " please create support ticket");
          }
        }

        function toggle_report(config, report) {
          if (config == "enforce_ssl_1year" && window.location.protocol != "https:") {
            alert("Please switch to SSL to enable this feature");
            return false;
          }
          GBI(config+"_spin").classList.remove("hidden");
          if (report && window.CONFIG_LIST[config] == "on") {
            let g = GBI(config+"_block");
            if (g) { g.checked = false; }
          }
          if (!report && window.CONFIG_LIST[config] == "report") {
            let g = GBI(config+"_report");
            if (g) { g.checked = false; }
          }
          let value = "false";
          let r = GBI(config+"_report");
          if (r && r.checked) { value = "report"; }
          if (GBI(config+"_block").checked) { value = "on"; }
          window.CONFIG_LIST[config] = value;

          if (config == "auto_start") {
            let action=(value == "on") ? "install" : "uninstall";
            BitFire_api(action, {})
              .then(r => r.json())
              .then(response => {
                console.log(response); 
                alert(response.note);
                window.setTimeout(function(){ GBI(config+"_spin").classList.add("hidden"); }, 100);
            });

          }
          else {
            BitFire_api("toggle_config_value", {"param":config,"value":value})
              .then(r => r.json())
              .then(data => {
              console.log("response...", data);
              if (!data.success) { alert("error updating " + config + " : " + data.note + " : " + data.errors.join()); }
              window.setTimeout(function(){ GBI(config+"_spin").classList.add("hidden"); }, 100);
            });
          }
        }

        // config name, block|on|true / report|alert / off|false
        function toggle(elm, type) {
          var e = GBI(elm);
          console.log(e);
          e.innerText = cap(type);
          BitFire_api("toggle_config_value", {"param":elm,"value":type})
            .then(r => r.json())
            .then(data => { console.log("toggle", data); });
          if (type == "block" || type == "on" || type == "true") {
            e.classList.remove("btn-warning");
            e.classList.remove("btn-secondary");
            e.classList.add("btn-success");
          } else if (type == "report" || type == "alert") {
            e.classList.remove("btn-success");
            e.classList.remove("btn-secondary");
            e.classList.add("btn-warning");
          } else {
            e.classList.remove("btn-success");
            e.classList.remove("btn-warning");
            e.classList.add("btn-secondary");
          }
        }
              
    </script>

    <script type="text/template" id="alert_template">
      <div class="alert alert-warning alert-dismissible fade show">
        {{text}}
        <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
      </div>
    </script>

    <!-- NAVIGATION
    ================================================== -->
    

    <div class="main-content">
      
      {{header}}    

      <!-- CARDS -->
      <div class="container-fluid">
        <div class="row justify-content-center">

          <div class="col-12 col-lg-10 col-xl-8" id="alert_block">
          </div>

          <div class="col-12 col-lg-10 col-xl-8">

            <div class="card">
              <div class="card-header">
                <h3 class="card-header-title text-primary">
                  BitFire Setup Wizard
                </h3>
                <span class="text-muted">select major features to enable</span>
              </div>
              <div class="card-body mb-4" style="padding:1rem !important">

                <h2 id="wizard_title" class="mb-2 mt-4">
                </h2>

                <p id="wizard_body" class="text-muted" style="min-height:60px">
                </p>

                <div class="col-auto tog" id="wizard_enable" data-enabled="false" data-title="Enable Feature" data-toggle="false">
                  <label class="form-label left" style="margin-right:70px; margin-left:-10px" id="wizard_label"> Enabled </label>
                  <div class="form-check form-switch right">
                      <input class="form-check-input success" autocomplete="off" id="wizard_enable_tog" type="checkbox" onclick="toggle_wiz(this)" data-bs-toggle="tooltip" data-bs-placement="top" title="Enable setting">
                      <label class="form-check-label" for="wizard_enable_tog"></label>
                  </div>
                </div>

                <div class="collapse" id="wizard_text">
                  <label id="wizard_text_label" for="wizard_text_input" class="form-label left" style="margin-right:70px; margin-left:-10px; padding: .5rem 2rem 0 2rem;"> Alert notification email </label>
                  <input type="email" class="form-contro" id="wizard_text_input" style="margin:.25rem" onchange="text_set(this)" title="setting value" value="">
                </div>


                <button type="button" class="btn btn-primary left" id="wizard_back" disabled="disabled"><span class="fe fe-chevron-left"></span> Back</button>

                <button type="button" class="btn btn-primary hidden right" id="wizard_finish" data-bs-toggle="modal" data-bs-target="#finish_modal">Finish <span class="fe fe-chevrons-right"></span></button>
                <button type="button" class="btn btn-primary right" id="wizard_next" data-idx="0">Next <span class="fe fe-chevron-right"></span></button>
              </div>
              <div class="card-footer card-footer-boxed">
                <div class="row align-items-center">
                  <div class="col">
                    <div class="row align-items-center g-0">
                      <div class="col-auto">
            
                        <!-- Value -->
                        <small id="progress_per" class="me-2">0%</small>
            
                      </div>
                      <div class="col">
            
                        <!-- Progress -->
                        <div class="progress progress-sm">
                          <div id="progress_bar" class="progress-bar" role="progressbar" style="width: 0%" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"></div>
                        </div>
            
                      </div>
                    </div> <!-- / .row -->
                  </div>
                </div>
              </div>
            </div>
          </div>
        </div>

      </div>
    </div>

    <div class="modal" id="finish_modal" tabindex="-1" style="margin-top: 200px">
      <div class="modal-dialog">
        <div class="modal-content">
          <div class="modal-header">
            <h5 class="modal-title">Firewall Setup Complete</h5>
            <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
          </div>
          <div class="modal-body">
            <p>Firewall Setup complete.  You can come back here to adjust settings at any time.</p>

            <div id="spin" class="text-success hidden spinner-border left mt-1 mr-2 " style="margin-left: 0" role="status"></div>
          </div>
          <div class="modal-footer">
            <button type="button" class="btn btn-primary" id="save_changes">Save changes</button>
          </div>
        </div>
      </div>
    </div>

  <script type="text/javascript">

  window.BITFIRE_NONCE = '{{api_code}}';
  window.EN_IDX = 0;
  window.EN = [
    ["auto_start", true],
    ["allow_ip_block", false],
    ["enforce_ssl_1year", (document.location.protocol == 'https:')],
    ["require_full_browser", true],
    ["whitelist_enable", true],
    ["hacked", false],
    ["notification_email", "{{your_email}}"]
  ];
  window.WIZ = [];
  window.WIZ[0] = {'title': 'Always On Protection', 'descr': 'Always on protection runs the firewall as the very first script before any plugins load.  This ' +
'will protect direct access to any script on your server.\nWorks for 99.99% of all installs.  Recommended setting.', 'label': 'Enabled'};
  window.WIZ[1] = {'title': 'Allow IP Blocking', 'descr': 'IP Blocking will add a fast block for IP address that are sending multiple ' +
'attack probes.  This will block all traffic from the offending IP address from 1 - 24 hours. <br><strong class="text-secondary">Enable if you are under active attack.</strong>', 'label': 'Enabled' };
  window.WIZ[2] = {'title': 'Force SSL', 'descr' : 'This will prevent non SSL traffic from accessing your web site.  All non-SSL ' +
'traffic will be disabled.  Recommended setting if your site supports HTTPS.', 'label': 'Enabled' };
  window.WIZ[3] = {'title': 'Require Full Browser', 'descr': 'Send javascript challenge to verify browsers are real. This will stop ' +
'99.99% of all automated attacks.  Recommended setting, <small>(may reduce server traffic by eliminating bots)</small>.', 'label': 'Enabled'};
  window.WIZ[4] = {'title': 'Block Impersonating Robots', 'descr': 'Hackers often impersonate search engines like GoogleBot, Bing and others to bypass filtering. ' +
'BitFire can prevent these attacks by verifying over 100 commonly used search engines and 3\'rd party services.', 'label': 'Enabled'};
  window.WIZ[5] = {'title': 'Is your website compromised?', 'descr': 'if you have no reason to suspect your security has been compromised, BitFire ' +
  'will mark all your files as clean and make malware scanning much easier.', 'label': 'Compromised?'};
  window.WIZ[6] = {'title': 'Email Notifications', 'descr': 'BitFire can notify you if it detects malware or security issues with your plugins and themes. ' +
'Where would you like critical notifications to go?'};


  function toggle_wiz(elm) {
    let idx = GBI("wizard_next").getAttribute("data-idx");
    let en = elm.checked;
    window.EN[idx][1] = en;
  }

  function text_set(elm) {
    let idx = GBI("wizard_next").getAttribute("data-idx");
    window.EN[idx][1] = elm.value;
  }

  function set_percent(per) {
    //console.log("set wiz pos", per, percent);
    GBI("progress_per").innerText = per + "%";
    let b = GBI("progress_bar");
    b.style = "width: " + per + "%";
    b.setAttribute("aria-valuenow", per);
  }


  function set_wiz_pos(pos) {
    //console.log("set wiz pos", pos)
    percent = Math.round((pos / window.WIZ.length) * 100);
    set_percent(percent);

    let t = window.WIZ[pos]["title"].replace("\n", "<br>");
    let d = window.WIZ[pos]["descr"].replace("\n", "<br>");
    let l = window.WIZ[pos]["label"];

    GBI("wizard_title").innerHTML = t;
    GBI("wizard_body").innerHTML = d;
    GBI("wizard_label").innerHTML = l;
    GBI("wizard_next").setAttribute("data-idx", pos);
    GBI("progress_bar").setAttribute("aria-valuenow", percent);

    let def = window.EN[pos][1];
    if (typeof def == "string") {
      let t_elm = GBI("wizard_text");
      if (t_elm.classList.contains("collapse")) {
        GBI("wizard_enable").classList.toggle("collapse");
        t_elm.classList.toggle("collapse");
        let i_elm = GBI("wizard_text_input");
        console.log("t_elm value", i_elm.value, i_elm);
        if (!i_elm.value || i_elm.value == "") {
          console.log("set def value", def, window.EN[pos]);
          i_elm.value = def;
        }
      }
    } else {
      let t_elm = GBI("wizard_enable");
      if (t_elm.classList.contains("collapse")) {
        GBI("wizard_enable").classList.toggle("collapse");
        GBI("wizard_text").classList.toggle("collapse");
      }
    }

    GBI("wizard_enable_tog").checked = window.EN[pos][1];
  }

  function next_click() {
    let i = parseInt(GBI("wizard_next").getAttribute("data-idx"));
    do_click(i+1);
  }
  function back_click() {
    let i = parseInt(GBI("wizard_next").getAttribute("data-idx"));
    do_click(i-1);
  }

  function do_click(pos, x) {
    console.log("next click", pos, x);

    let n = GBI("wizard_next");
    let f = GBI("wizard_finish");
    if (pos == 0) {
      GBI("wizard_back").setAttribute("disabled", "disabled");
    } else {
      GBI("wizard_back").removeAttribute("disabled");
    }
    if (pos >= window.WIZ.length-1) {
      f.classList.remove("hidden");
      n.classList.add("hidden");
    } else {
      f.classList.add("hidden");
      n.classList.remove("hidden");
    }
    set_wiz_pos(pos);
  }

  // MAIN
  document.addEventListener("DOMContentLoaded", function () {
    GBI("wizard_next").addEventListener("click", next_click);
    //GBI("wizard_finish").addEventListener("click", next_click);
    GBI("wizard_back").addEventListener("click", back_click);
    GBI("save_changes").addEventListener("click", save_changes);
    set_wiz_pos(0);
  });

  function save_changes() {
    console.log("save changes", window.EN);
    GBI("save_changes").removeEventListener("click", save_changes);
    update_value(window.EN[window.EN_IDX][0], window.EN[window.EN_IDX][1]);
  }

      
  function toggle_features() {
    var elms = document.getElementsByClassName("feature");
    console.log("toggle features", elms);

    Array.prototype.forEach.call(elms, function (element) {
      console.log("element", element.innerText);
      if (element.innerText.trim() == "report" || element.innerText.trim() == "alert") {
        console.log("warn");
        element.classList.remove("btn-success");
        element.classList.remove("btn-secondary");
        element.classList.add("btn-warning");
      } else if (element.innerText.trim() == "block" || element.innerText.trim() == "true" || element.innerText.trim() == "on") {
        console.log("success");
        element.classList.remove("btn-warning");
        element.classList.remove("btn-secondary");
        element.classList.add("btn-success");
      } else {
        element.classList.remove("btn-success");
        element.classList.remove("btn-warning");
        element.classList.add("btn-secondary");
        console.log("second");
      }
    });
  }

  /*
  let toggles = document.getElementsByClassName("tog");
  for (let i = 0; i < toggles.length; i++) {
    let id = toggles[i].getAttribute("id");
    let name = id.replace("_con", "");
    let title = toggles[i].getAttribute("data-title");
    let tog = toggles[i].hasAttribute("data-toggle");

    let alert = toggles[i].getAttribute("data-enabled");

    let check1 = (alert == "report") ? "checked" : "";
    let check2 = (alert == "on") ? "checked" : "";

    let tail1 = (tog) ? "" : " in alert mode only";
    let tail2 = (tog) ? "" : " in full blocking";

    let tool1 = 'data-bs-toggle="tooltip" data-bs-placement="top" title="'+title+tail1+'"';
    let tool2 = 'data-bs-toggle="tooltip" data-bs-placement="top" title="'+title+tail2+'"';


    let html = '<div id="'+name+'_spin" class="spinner-border text-success spinner-border-sm left mt-1 mr-2 hidden" role="status"></div>';
    if (!tog) {
      html += '<div class="form-check form-switch left" style="margin-left:-60px"> <input class="form-check-input warning" id="'+name+'_report" autocomplete="off" type="checkbox" onclick="return toggle_report(\''+name+'\', true)" '+check1+' '+ tool1 +'> </div>';
    }
    html += '\
<div class="form-switch right">\
    <input class="form-check-input success" autocomplete="off" id="'+name+'_block" type="checkbox" onclick="return toggle_report(\''+name+'\', false)" '+check2+' '+tool2+'>\
</div>';
    window.CONFIG_LIST[name] = alert;

    toggles[i].innerHTML = html;
  }
  */

  </script>

    
  <script type="text/javascript">

    function alert_or_block(config) {
      // console.log("alert_or_block", config);
      if (config === 'report' || config === 'alert') { return 'report'; }
      if (!config) { return 'off'; }
      return 'on';
    }


    if (GBI("password_btn")) {
      GBI("password_btn").addEventListener("click", function() {
        GBI('password_close').classList.remove("hidden");
      });
    }

  </script>
  {{gtag}}