From 13013d099571ac96a653372dee40fe68a2b8083a Mon Sep 17 00:00:00 2001 From: Jamil Bou Kheir Date: Fri, 10 Dec 2021 17:13:11 -0800 Subject: [PATCH 1/7] Download config working --- apps/fz_common/lib/fz_string.ex | 5 +++ apps/fz_common/test/fz_string_test.exs | 6 +++ apps/fz_http/lib/fz_http/devices.ex | 37 +++++++++++++++++++ .../controllers/device_controller.ex | 16 ++++++++ .../live/device_live/show.html.heex | 29 +++++++++------ .../fz_http_web/live/device_live/show_live.ex | 29 ++------------- apps/fz_http/lib/fz_http_web/router.ex | 1 + .../test/fz_http/connectivity_checks_test.exs | 4 +- .../fixtures/connectivity_checks_fixtures.ex | 2 +- 9 files changed, 90 insertions(+), 39 deletions(-) diff --git a/apps/fz_common/lib/fz_string.ex b/apps/fz_common/lib/fz_string.ex index 73b91b712..14169e31c 100644 --- a/apps/fz_common/lib/fz_string.ex +++ b/apps/fz_common/lib/fz_string.ex @@ -3,6 +3,11 @@ defmodule FzCommon.FzString do Utility functions for working with Strings. """ + def sanitize_filename(str) when is_binary(str) do + str + |> String.replace(~r/[^a-zA-Z0-9]+/, "_") + end + def to_boolean(str) when is_binary(str) do as_bool(String.downcase(str)) end diff --git a/apps/fz_common/test/fz_string_test.exs b/apps/fz_common/test/fz_string_test.exs index 1ffed172b..31cadfccc 100644 --- a/apps/fz_common/test/fz_string_test.exs +++ b/apps/fz_common/test/fz_string_test.exs @@ -3,6 +3,12 @@ defmodule FzCommon.FzStringTest do alias FzCommon.FzString + describe "sanitize_filename/1" do + test "santizes sequential spaces" do + assert "Factory_Device" == FzString.sanitize_filename("Factory Device") + end + end + describe "to_boolean/1" do test "converts to true" do assert true == FzString.to_boolean("True") diff --git a/apps/fz_http/lib/fz_http/devices.ex b/apps/fz_http/lib/fz_http/devices.ex index 2a0a130d0..10afd03f1 100644 --- a/apps/fz_http/lib/fz_http/devices.ex +++ b/apps/fz_http/lib/fz_http/devices.ex @@ -92,4 +92,41 @@ defmodule FzHttp.Devices do |> Enum.map(fn field -> {field, Device.field(changeset, field)} end) |> Map.new() end + + def as_config(device) do + wireguard_port = Application.fetch_env!(:fz_vpn, :wireguard_port) + + """ + [Interface] + PrivateKey = #{device.private_key} + Address = #{ipv4_address(device)}/32, #{ipv6_address(device)}/128 + #{dns_servers_config(device)} + + [Peer] + PublicKey = #{device.server_public_key} + AllowedIPs = #{allowed_ips(device)} + Endpoint = #{endpoint(device)}:#{wireguard_port} + """ + end + + defp dns_servers_config(device) when is_struct(device) do + dns_servers = dns_servers(device) + + if dns_servers_empty?(dns_servers) do + "" + else + "DNS = #{dns_servers}" + end + end + + defp dns_servers_empty?(nil), do: true + + defp dns_servers_empty?(dns_servers) when is_binary(dns_servers) do + len = + dns_servers + |> String.trim() + |> String.length() + + len == 0 + end end diff --git a/apps/fz_http/lib/fz_http_web/controllers/device_controller.ex b/apps/fz_http/lib/fz_http_web/controllers/device_controller.ex index 3f9fe19e4..3e673d42d 100644 --- a/apps/fz_http/lib/fz_http_web/controllers/device_controller.ex +++ b/apps/fz_http/lib/fz_http_web/controllers/device_controller.ex @@ -4,10 +4,26 @@ defmodule FzHttpWeb.DeviceController do """ use FzHttpWeb, :controller + import FzCommon.FzString, only: [sanitize_filename: 1] + alias FzHttp.Devices + plug :redirect_unauthenticated def index(conn, _params) do conn |> redirect(to: Routes.device_index_path(conn, :index)) end + + def download_config(conn, %{"id" => device_id}) do + device = Devices.get_device!(device_id) + filename = "#{sanitize_filename(device.name)}.conf" + content_type = "text/plain" + + conn + |> send_download( + {:binary, Devices.as_config(device)}, + filename: filename, + content_type: content_type + ) + end end diff --git a/apps/fz_http/lib/fz_http_web/live/device_live/show.html.heex b/apps/fz_http/lib/fz_http_web/live/device_live/show.html.heex index 93a44505d..47695fe8d 100644 --- a/apps/fz_http/lib/fz_http_web/live/device_live/show.html.heex +++ b/apps/fz_http/lib/fz_http_web/live/device_live/show.html.heex @@ -81,22 +81,15 @@
-

Config

+

+ Config +

Add the following to your WireGuard configuration file:
-

-[Interface]
-PrivateKey = <%= @device.private_key %>
-Address = <%= FzHttp.Devices.ipv4_address(@device) %>/32, <%= FzHttp.Devices.ipv6_address(@device) %>/128
-<%= @dns_servers %>
-
-[Peer]
-PublicKey = <%= @device.server_public_key %>
-AllowedIPs = <%= @allowed_ips %>
-Endpoint = <%= @endpoint %>:<%= @wireguard_port %>
+
<%= @config %>

Or scan the QR code with your mobile phone: @@ -105,6 +98,20 @@ Endpoint = <%= @endpoint %>:<%= @wireguard_port %>
+
+ +
diff --git a/apps/fz_http/lib/fz_http_web/live/device_live/show_live.ex b/apps/fz_http/lib/fz_http_web/live/device_live/show_live.ex index 2f7ac4ef4..291722ecf 100644 --- a/apps/fz_http/lib/fz_http_web/live/device_live/show_live.ex +++ b/apps/fz_http/lib/fz_http_web/live/device_live/show_live.ex @@ -46,39 +46,18 @@ defmodule FzHttpWeb.DeviceLive.Show do device = Devices.get_device!(id) if device.user_id == socket.assigns.current_user.id do - assign( - socket, + socket + |> assign( device: device, user: Users.get_user!(device.user_id), page_title: device.name, allowed_ips: Devices.allowed_ips(device), - dns_servers: dns_servers(device), + dns_servers: Devices.dns_servers(device), endpoint: Devices.endpoint(device), - wireguard_port: Application.fetch_env!(:fz_vpn, :wireguard_port) + config: Devices.as_config(device) ) else not_authorized(socket) end end - - defp dns_servers(device) when is_struct(device) do - dns_servers = Devices.dns_servers(device) - - if dns_servers_empty?(dns_servers) do - "" - else - "DNS = #{dns_servers}" - end - end - - defp dns_servers_empty?(nil), do: true - - defp dns_servers_empty?(dns_servers) when is_binary(dns_servers) do - len = - dns_servers - |> String.trim() - |> String.length() - - len == 0 - end end diff --git a/apps/fz_http/lib/fz_http_web/router.ex b/apps/fz_http/lib/fz_http_web/router.ex index 33b9c7fa5..ace120ff1 100644 --- a/apps/fz_http/lib/fz_http_web/router.ex +++ b/apps/fz_http/lib/fz_http_web/router.ex @@ -22,6 +22,7 @@ defmodule FzHttpWeb.Router do pipe_through :browser get "/", DeviceController, :index + get "/devices/:id/dl", DeviceController, :download_config resources "/session", SessionController, only: [:new, :create, :delete], singleton: true live "/users", UserLive.Index, :index diff --git a/apps/fz_http/test/fz_http/connectivity_checks_test.exs b/apps/fz_http/test/fz_http/connectivity_checks_test.exs index fe06187a0..72c3ab6df 100644 --- a/apps/fz_http/test/fz_http/connectivity_checks_test.exs +++ b/apps/fz_http/test/fz_http/connectivity_checks_test.exs @@ -62,7 +62,7 @@ defmodule FzHttp.ConnectivityChecksTest do response_body: "some updated response_body", response_code: 500, response_headers: %{"updated" => "response headers"}, - url: "https://ping.firez.one/6.6.6" + url: "https://ping-dev.firez.one/6.6.6" } assert {:ok, %ConnectivityCheck{} = connectivity_check} = @@ -71,7 +71,7 @@ defmodule FzHttp.ConnectivityChecksTest do assert connectivity_check.response_body == "some updated response_body" assert connectivity_check.response_code == 500 assert connectivity_check.response_headers == %{"updated" => "response headers"} - assert connectivity_check.url == "https://ping.firez.one/6.6.6" + assert connectivity_check.url == "https://ping-dev.firez.one/6.6.6" end test "update_connectivity_check/2 with invalid data returns error changeset" do diff --git a/apps/fz_http/test/support/fixtures/connectivity_checks_fixtures.ex b/apps/fz_http/test/support/fixtures/connectivity_checks_fixtures.ex index 7f17c6c6c..7b138e6f0 100644 --- a/apps/fz_http/test/support/fixtures/connectivity_checks_fixtures.ex +++ b/apps/fz_http/test/support/fixtures/connectivity_checks_fixtures.ex @@ -16,7 +16,7 @@ defmodule FzHttp.ConnectivityChecksFixtures do response_body: "some response_body", response_code: 142, response_headers: %{"Content-Type" => "text/plain"}, - url: "https://ping.firez.one/0.0.0+git.0.deadbeef0" + url: "https://ping-dev.firez.one/0.0.0+git.0.deadbeef0" }) |> ConnectivityChecks.create_connectivity_check() From e9d1d7e12e3c23b8f759b9d01b75a6a73b0200a5 Mon Sep 17 00:00:00 2001 From: Jamil Bou Kheir Date: Sat, 11 Dec 2021 11:00:03 -0800 Subject: [PATCH 2/7] Dropdown buttons --- apps/fz_http/assets/js/hooks.js | 32 ++++++ apps/fz_http/lib/fz_http_web.ex | 1 + .../live/account_live/show.html.heex | 12 +-- .../live/device_live/show.html.heex | 99 ++++++++++++------- .../fz_http_web/live/device_live/show_live.ex | 6 +- .../lib/fz_http_web/live/modal_component.ex | 12 ++- .../live/rule_live/index.html.heex | 2 - .../fz_http_web/live/rule_live/index_live.ex | 2 +- .../live/setting_live/default.html.heex | 3 - .../templates/layout/root.html.heex | 2 +- apps/fz_http/mix.exs | 4 +- .../live/device_live/show_test.exs | 2 +- mix.lock | 8 +- 13 files changed, 120 insertions(+), 65 deletions(-) diff --git a/apps/fz_http/assets/js/hooks.js b/apps/fz_http/assets/js/hooks.js index 1b84f78b1..0c6137ddc 100644 --- a/apps/fz_http/assets/js/hooks.js +++ b/apps/fz_http/assets/js/hooks.js @@ -24,6 +24,34 @@ const formatTimestamp = function () { this.el.innerHTML = moment(timestamp).format("dddd, MMMM Do YYYY, h:mm:ss a z") } +/* XXX: Sad we have to write custom JS for this. Keep an eye on + * https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.JS.html + * in case a toggleClass function is implemented. The toggle() + * function listed there automatically adds display: none which is not + * what we want + */ +const toggleDropdown = function () { + const button = this.el + const dropdown = document.getElementById(button.dataset.target) + + document.addEventListener("click", e => { + let ancestor = e.target + + do { + if (ancestor == button) { + return + } + + ancestor = ancestor.parentNode + } while(ancestor) + + dropdown.classList.remove("is-active") + }) + button.addEventListener("click", e => { + dropdown.classList.toggle("is-active") + }) +} + let Hooks = {} Hooks.QrCode = { mounted: renderQrCode, @@ -33,5 +61,9 @@ Hooks.FormatTimestamp = { mounted: formatTimestamp, updated: formatTimestamp } +Hooks.ToggleDropdown = { + mounted: toggleDropdown, + updated: toggleDropdown +} export default Hooks diff --git a/apps/fz_http/lib/fz_http_web.ex b/apps/fz_http/lib/fz_http_web.ex index 84f640559..14585f768 100644 --- a/apps/fz_http/lib/fz_http_web.ex +++ b/apps/fz_http/lib/fz_http_web.ex @@ -56,6 +56,7 @@ defmodule FzHttpWeb do quote do use Phoenix.LiveView, layout: {FzHttpWeb.LayoutView, "live.html"} import FzHttpWeb.LiveHelpers + alias Phoenix.LiveView.JS @events_module Application.compile_env!(:fz_http, :events_module) diff --git a/apps/fz_http/lib/fz_http_web/live/account_live/show.html.heex b/apps/fz_http/lib/fz_http_web/live/account_live/show.html.heex index ca145f4bb..a17470da4 100644 --- a/apps/fz_http/lib/fz_http_web/live/account_live/show.html.heex +++ b/apps/fz_http/lib/fz_http_web/live/account_live/show.html.heex @@ -1,11 +1,11 @@ <%= if @live_action == :edit do %> <%= live_modal( - FzHttpWeb.AccountLive.FormComponent, - return_to: Routes.account_show_path(@socket, :show), - title: "Edit Account", - id: "user-#{@current_user.id}", - user: @current_user, - action: @live_action) %> + FzHttpWeb.AccountLive.FormComponent, + return_to: Routes.account_show_path(@socket, :show), + title: "Edit Account", + id: "user-#{@current_user.id}", + user: @current_user, + action: @live_action) %> <% end %> <%= render FzHttpWeb.SharedView, "heading.html", page_title: @page_title %> diff --git a/apps/fz_http/lib/fz_http_web/live/device_live/show.html.heex b/apps/fz_http/lib/fz_http_web/live/device_live/show.html.heex index 47695fe8d..b60f0124c 100644 --- a/apps/fz_http/lib/fz_http_web/live/device_live/show.html.heex +++ b/apps/fz_http/lib/fz_http_web/live/device_live/show.html.heex @@ -17,6 +17,41 @@

Details

+
@@ -53,29 +88,6 @@
-
- -
@@ -84,6 +96,33 @@

Config

+
@@ -98,20 +137,6 @@
-
- -
diff --git a/apps/fz_http/lib/fz_http_web/live/device_live/show_live.ex b/apps/fz_http/lib/fz_http_web/live/device_live/show_live.ex index 291722ecf..9ef7f0a66 100644 --- a/apps/fz_http/lib/fz_http_web/live/device_live/show_live.ex +++ b/apps/fz_http/lib/fz_http_web/live/device_live/show_live.ex @@ -6,19 +6,19 @@ defmodule FzHttpWeb.DeviceLive.Show do alias FzHttp.{Devices, Users} - @impl true + @impl Phoenix.LiveView def mount(params, session, socket) do {:ok, socket |> assign_defaults(params, session, &load_data/2)} end - @impl true + @impl Phoenix.LiveView def handle_params(_params, _url, socket) do {:noreply, socket} end - @impl true + @impl Phoenix.LiveView def handle_event("delete_device", %{"device_id" => device_id}, socket) do device = Devices.get_device!(device_id) diff --git a/apps/fz_http/lib/fz_http_web/live/modal_component.ex b/apps/fz_http/lib/fz_http_web/live/modal_component.ex index fa0489450..eb2e34229 100644 --- a/apps/fz_http/lib/fz_http_web/live/modal_component.ex +++ b/apps/fz_http/lib/fz_http_web/live/modal_component.ex @@ -6,22 +6,24 @@ defmodule FzHttpWeb.ModalComponent do @impl true def render(assigns) do - ~L""" -