From 765976275e3b0c5a4e046ee3fdd8d7af999233a1 Mon Sep 17 00:00:00 2001 From: Jamil Bou Kheir Date: Tue, 30 Nov 2021 10:20:05 -0800 Subject: [PATCH] Add diagnostic pinger; default settings Also don't display DNSservers when empty. Fixes firezone/backlog#135 Fixes firezone/backlog#123 Fixes firezone/backlog#130 Refs #333 --- .github/workflows/ci.yml | 44 + .gitignore | 4 +- .pre-commit-config.yaml | 6 + .tool-versions | 3 +- apps/fz_common/lib/fz_integer.ex | 9 + apps/fz_common/lib/fz_string.ex | 21 + apps/fz_common/test/fz_crypto_test.exs | 25 +- apps/fz_common/test/fz_integer_text.exs | 31 + apps/fz_common/test/fz_map_test.exs | 14 + apps/fz_common/test/fz_string_test.exs | 23 + apps/fz_http/.gitignore | 2 + apps/fz_http/assets/css/app.scss | 8 + apps/fz_http/assets/js/app.js | 2 + apps/fz_http/assets/package-lock.json | 948 ++++++++++++++++-- apps/fz_http/assets/package.json | 5 +- apps/fz_http/lib/fz_http/application.ex | 43 +- .../lib/fz_http/connectivity_check_service.ex | 78 ++ .../lib/fz_http/connectivity_checks.ex | 138 +++ .../connectivity_checks/connectivity_check.ex | 27 + apps/fz_http/lib/fz_http/devices.ex | 36 +- apps/fz_http/lib/fz_http/devices/device.ex | 120 +-- apps/fz_http/lib/fz_http/macros.ex | 17 + apps/fz_http/lib/fz_http/mock_http_client.ex | 32 + apps/fz_http/lib/fz_http/settings.ex | 94 ++ apps/fz_http/lib/fz_http/settings/setting.ex | 69 ++ apps/fz_http/lib/fz_http/shared_validators.ex | 109 ++ apps/fz_http/lib/fz_http/users/user.ex | 17 +- .../live/account_live/show_live.ex | 4 +- .../connectivity_check_live/index.html.heex | 41 + .../connectivity_check_live/index_live.ex | 20 + .../live/device_live/form_component.ex | 19 +- .../live/device_live/form_component.html.heex | 75 +- .../live/device_live/show.html.heex | 13 +- .../fz_http_web/live/device_live/show_live.ex | 25 +- .../live/setting_live/default.html.heex | 49 + .../live/setting_live/default_live.ex | 46 + .../live/setting_live/form_component.ex | 62 ++ .../setting_live/form_component.html.heex | 26 + apps/fz_http/lib/fz_http_web/live_helpers.ex | 31 + apps/fz_http/lib/fz_http_web/router.ex | 10 +- .../templates/layout/root.html.heex | 29 +- .../lib/fz_http_web/views/admin_view.ex | 3 - apps/fz_http/mix.exs | 1 + ...11116172808_create_connectivity_checks.exs | 16 + .../20211116173236_create_settings.exs | 25 + ...03012238_change_device_column_defaults.exs | 18 + apps/fz_http/priv/repo/seeds.exs | 18 +- .../connectivity_check_service_test.exs | 51 + .../test/fz_http/connectivity_checks_test.exs | 103 ++ apps/fz_http/test/fz_http/devices_test.exs | 5 +- apps/fz_http/test/fz_http/settings_test.exs | 73 ++ .../live/account_live/show_test.exs | 20 + .../connectivity_check_live/index_test.exs | 27 + .../live/device_live/show_test.exs | 147 ++- .../live/setting_live/default_test.exs | 169 ++++ apps/fz_http/test/support/conn_case.ex | 4 +- apps/fz_http/test/support/fixtures.ex | 69 -- .../fixtures/connectivity_checks_fixtures.ex | 25 + .../test/support/fixtures/devices_fixtures.ex | 27 + .../test/support/fixtures/rules_fixtures.ex | 25 + .../support/fixtures/sessions_fixtures.ex | 15 + .../support/fixtures/settings_fixtures.ex | 15 + .../test/support/fixtures/users_fixtures.ex | 32 + apps/fz_http/test/support/mock_helpers.ex | 21 - apps/fz_http/test/support/test_helpers.ex | 55 +- config/config.exs | 6 +- config/dev.exs | 2 - config/prod.exs | 3 + config/releases.exs | 19 +- config/test.exs | 8 +- mix.exs | 3 + mix.lock | 27 +- .../cookbooks/firezone/attributes/default.rb | 46 +- .../cookbooks/firezone/libraries/config.rb | 4 +- omnibus/cookbooks/firezone/recipes/config.rb | 5 +- omnibus/cookbooks/firezone/recipes/nginx.rb | 2 +- omnibus/cookbooks/firezone/recipes/phoenix.rb | 2 +- .../cookbooks/firezone/recipes/postgresql.rb | 2 +- .../cookbooks/firezone/recipes/wireguard.rb | 2 +- .../firezone/templates/phoenix.nginx.conf.erb | 6 +- 80 files changed, 3065 insertions(+), 411 deletions(-) create mode 100644 apps/fz_common/lib/fz_integer.ex create mode 100644 apps/fz_common/lib/fz_string.ex create mode 100644 apps/fz_common/test/fz_integer_text.exs create mode 100644 apps/fz_common/test/fz_string_test.exs create mode 100644 apps/fz_http/lib/fz_http/connectivity_check_service.ex create mode 100644 apps/fz_http/lib/fz_http/connectivity_checks.ex create mode 100644 apps/fz_http/lib/fz_http/connectivity_checks/connectivity_check.ex create mode 100644 apps/fz_http/lib/fz_http/macros.ex create mode 100644 apps/fz_http/lib/fz_http/mock_http_client.ex create mode 100644 apps/fz_http/lib/fz_http/settings.ex create mode 100644 apps/fz_http/lib/fz_http/settings/setting.ex create mode 100644 apps/fz_http/lib/fz_http/shared_validators.ex create mode 100644 apps/fz_http/lib/fz_http_web/live/connectivity_check_live/index.html.heex create mode 100644 apps/fz_http/lib/fz_http_web/live/connectivity_check_live/index_live.ex create mode 100644 apps/fz_http/lib/fz_http_web/live/setting_live/default.html.heex create mode 100644 apps/fz_http/lib/fz_http_web/live/setting_live/default_live.ex create mode 100644 apps/fz_http/lib/fz_http_web/live/setting_live/form_component.ex create mode 100644 apps/fz_http/lib/fz_http_web/live/setting_live/form_component.html.heex delete mode 100644 apps/fz_http/lib/fz_http_web/views/admin_view.ex create mode 100644 apps/fz_http/priv/repo/migrations/20211116172808_create_connectivity_checks.exs create mode 100644 apps/fz_http/priv/repo/migrations/20211116173236_create_settings.exs create mode 100644 apps/fz_http/priv/repo/migrations/20211203012238_change_device_column_defaults.exs create mode 100644 apps/fz_http/test/fz_http/connectivity_check_service_test.exs create mode 100644 apps/fz_http/test/fz_http/connectivity_checks_test.exs create mode 100644 apps/fz_http/test/fz_http/settings_test.exs create mode 100644 apps/fz_http/test/fz_http_web/live/connectivity_check_live/index_test.exs create mode 100644 apps/fz_http/test/fz_http_web/live/setting_live/default_test.exs delete mode 100644 apps/fz_http/test/support/fixtures.ex create mode 100644 apps/fz_http/test/support/fixtures/connectivity_checks_fixtures.ex create mode 100644 apps/fz_http/test/support/fixtures/devices_fixtures.ex create mode 100644 apps/fz_http/test/support/fixtures/rules_fixtures.ex create mode 100644 apps/fz_http/test/support/fixtures/sessions_fixtures.ex create mode 100644 apps/fz_http/test/support/fixtures/settings_fixtures.ex create mode 100644 apps/fz_http/test/support/fixtures/users_fixtures.ex delete mode 100644 apps/fz_http/test/support/mock_helpers.ex diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index be872e132..8b0285253 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,6 +11,50 @@ defaults: shell: bash jobs: + static-analysis: + runs-on: ubuntu-18.04 + env: + MIX_ENV: dev + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + steps: + - uses: actions/checkout@v2 + - uses: erlef/setup-beam@v1 + with: + otp-version: '24.1' + elixir-version: '1.12.3' + - uses: actions/cache@v2 + with: + path: | + deps + _build + key: ${{ runner.os }}-mix-${{ hashFiles('**/mix.lock') }} + restore-keys: | + ${{ runner.os }}-mix- + - name: Install Dependencies + run: mix deps.get --only dev + # Don't cache PLTs based on mix.lock hash, as Dialyzer can incrementally update even old ones + # Cache key based on Elixir & Erlang version (also usefull when running in matrix) + - name: Restore PLT cache + uses: actions/cache@v2 + id: plt_cache + with: + key: | + ${{ runner.os }}-${{ steps.beam.outputs.elixir-version }}-${{ steps.beam.outputs.otp-version }}-plt + restore-keys: | + ${{ runner.os }}-${{ steps.beam.outputs.elixir-version }}-${{ steps.beam.outputs.otp-version }}-plt + path: | + priv/plts + # Create PLTs if no cache was found + - name: Create PLTs + if: steps.plt_cache.outputs.cache-hit != 'true' + run: mix dialyzer --plt + - name: Run format check + run: mix format --check-formatted + - name: Run linter + run: mix credo --strict + - name: Run dialyzer + run: mix dialyzer --format dialyxir + unit-test: runs-on: ubuntu-18.04 env: diff --git a/.gitignore b/.gitignore index 8891002e4..51d20e54e 100644 --- a/.gitignore +++ b/.gitignore @@ -33,6 +33,9 @@ npm-debug.log # this depending on your deployment strategy. /priv/static/ +# Dialyxir output +/priv/plts/ + # ElixirLS generates an .elixir_ls folder for user settings .elixir_ls @@ -51,7 +54,6 @@ npm-debug.log /*.deb /*.rpm -pkg/debian/opt # Test screenshots apps/fz_http/screenshots diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9eb9a79ec..32e6ea832 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -13,6 +13,12 @@ repos: language: system pass_filenames: false files: \.exs*$ + - id: mix-analysis + name: 'elixir: mix dialyzer' + entry: mix dialyzer --format dialyxir + language: system + pass_filenames: false + files: \.exs*$ - id: mix-compile name: 'elixir: mix compile' entry: mix compile --force --warnings-as-errors diff --git a/.tool-versions b/.tool-versions index 72e5aebae..217f2f179 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1,4 +1,5 @@ -# These should match the versions used in the build +# These are used for the dev environment. +# This should match the versions used in the built product. nodejs 14.18.1 elixir 1.12.3-otp-24 erlang 24.1.4 diff --git a/apps/fz_common/lib/fz_integer.ex b/apps/fz_common/lib/fz_integer.ex new file mode 100644 index 000000000..cb90015df --- /dev/null +++ b/apps/fz_common/lib/fz_integer.ex @@ -0,0 +1,9 @@ +defmodule FzCommon.FzInteger do + @moduledoc """ + Utility functions for working with Integers. + """ + + def clamp(num, min, _max) when is_integer(num) and num < min, do: min + def clamp(num, _min, max) when is_integer(num) and num > max, do: max + def clamp(num, _min, _max) when is_integer(num), do: num +end diff --git a/apps/fz_common/lib/fz_string.ex b/apps/fz_common/lib/fz_string.ex new file mode 100644 index 000000000..73b91b712 --- /dev/null +++ b/apps/fz_common/lib/fz_string.ex @@ -0,0 +1,21 @@ +defmodule FzCommon.FzString do + @moduledoc """ + Utility functions for working with Strings. + """ + + def to_boolean(str) when is_binary(str) do + as_bool(String.downcase(str)) + end + + defp as_bool("true") do + true + end + + defp as_bool("false") do + false + end + + defp as_bool(unknown) do + raise "Unknown boolean: string #{unknown} not one of ['true', 'false']." + end +end diff --git a/apps/fz_common/test/fz_crypto_test.exs b/apps/fz_common/test/fz_crypto_test.exs index f8ac19a3a..7e7943a18 100644 --- a/apps/fz_common/test/fz_crypto_test.exs +++ b/apps/fz_common/test/fz_crypto_test.exs @@ -3,7 +3,7 @@ defmodule FzCommon.FzCryptoTest do alias FzCommon.FzCrypto - describe "rand_string" do + describe "rand_string/1" do test "it returns a string of default length" do assert 16 == String.length(FzCrypto.rand_string()) end @@ -14,4 +14,27 @@ defmodule FzCommon.FzCryptoTest do end end end + + describe "rand_token/1" do + test "returns a token of default length" do + # 8 bytes is 12 chars in Base64 + assert 12 == String.length(FzCrypto.rand_token()) + end + + test "returns a token of length 4 when bytes is 1" do + assert 4 == String.length(FzCrypto.rand_token(1)) + end + + test "returns a token of length 4 when bytes is 3" do + assert 4 == String.length(FzCrypto.rand_token(3)) + end + + test "returns a token of length 40_000 when bytes is 32_768" do + assert 44 == String.length(FzCrypto.rand_token(32)) + end + + test "returns a token of length 44 when bytes is 32" do + assert 43_692 == String.length(FzCrypto.rand_token(32_768)) + end + end end diff --git a/apps/fz_common/test/fz_integer_text.exs b/apps/fz_common/test/fz_integer_text.exs new file mode 100644 index 000000000..fd09cde77 --- /dev/null +++ b/apps/fz_common/test/fz_integer_text.exs @@ -0,0 +1,31 @@ +defmodule FzCommon.FzIntegerTest do + use ExUnit.Case, async: true + + alias FzCommon.FzInteger + + describe "clamp/3" do + test "clamps to min" do + min = 1 + max = 5 + num = 0 + + assert 1 == FzInteger.clamp(num, min, max) + end + + test "clamps to max" do + min = 1 + max = 5 + num = 7 + + assert 5 == FzInteger.clamp(num, min, max) + end + + test "returns num if in range" do + min = 1 + max = 5 + num = 3 + + assert 3 == FzInteger.clamp(num, min, max) + end + end +end diff --git a/apps/fz_common/test/fz_map_test.exs b/apps/fz_common/test/fz_map_test.exs index ac7a7868e..911809966 100644 --- a/apps/fz_common/test/fz_map_test.exs +++ b/apps/fz_common/test/fz_map_test.exs @@ -18,4 +18,18 @@ defmodule FzCommon.FzMapTest do assert FzMap.compact(@data, "") == %{foo: "bar"} end end + + describe "stringify_keys/1" do + @data %{foo: "bar", bar: "", map: %{foo: "bar"}} + + test "stringifies the keys" do + assert FzMap.stringify_keys(@data) == %{ + "foo" => "bar", + "bar" => "", + "map" => %{ + foo: "bar" + } + } + end + end end diff --git a/apps/fz_common/test/fz_string_test.exs b/apps/fz_common/test/fz_string_test.exs new file mode 100644 index 000000000..1ffed172b --- /dev/null +++ b/apps/fz_common/test/fz_string_test.exs @@ -0,0 +1,23 @@ +defmodule FzCommon.FzStringTest do + use ExUnit.Case, async: true + + alias FzCommon.FzString + + describe "to_boolean/1" do + test "converts to true" do + assert true == FzString.to_boolean("True") + end + + test "converts to false" do + assert false == FzString.to_boolean("False") + end + + test "raises exception on unknowns" do + message = "Unknown boolean: string foobar not one of ['true', 'false']." + + assert_raise RuntimeError, message, fn -> + FzString.to_boolean("foobar") + end + end + end +end diff --git a/apps/fz_http/.gitignore b/apps/fz_http/.gitignore index 9f6a27061..c7032b99f 100644 --- a/apps/fz_http/.gitignore +++ b/apps/fz_http/.gitignore @@ -30,6 +30,8 @@ npm-debug.log # The directory NPM downloads your dependencies sources to. /assets/node_modules/ +/assets/lib/node_modules/ +/assets/bin/ # Since we are building assets from assets/, # we ignore priv/static. You may want to comment diff --git a/apps/fz_http/assets/css/app.scss b/apps/fz_http/assets/css/app.scss index 3336ef710..0727fc16b 100644 --- a/apps/fz_http/assets/css/app.scss +++ b/apps/fz_http/assets/css/app.scss @@ -6,8 +6,16 @@ @import "./email.scss"; @import "./tables.scss"; +/* Font Awesome */ $fa-font-path: "~@fortawesome/fontawesome-free/webfonts"; @import "~@fortawesome/fontawesome-free/scss/fontawesome.scss"; @import "~@fortawesome/fontawesome-free/scss/solid.scss"; @import "~@fortawesome/fontawesome-free/scss/regular.scss"; @import "~@fortawesome/fontawesome-free/scss/brands.scss"; + +/* Material Design Icons */ +$mdi-font-path: "~@mdi/font/fonts"; +@import "~@mdi/font/scss/materialdesignicons.scss"; + +/* Bulma Tooltip */ +@import "~@creativebulma/bulma-tooltip/src/sass/index.sass"; diff --git a/apps/fz_http/assets/js/app.js b/apps/fz_http/assets/js/app.js index 22331c444..38efbeaad 100644 --- a/apps/fz_http/assets/js/app.js +++ b/apps/fz_http/assets/js/app.js @@ -3,6 +3,8 @@ // its own CSS file. import css from "../css/app.scss" +import "@fontsource/fira-sans" + // webpack automatically bundles all modules in your // entry points. Those entry points can be configured // in "webpack.config.js". diff --git a/apps/fz_http/assets/package-lock.json b/apps/fz_http/assets/package-lock.json index bd53d11f6..9c121a66b 100644 --- a/apps/fz_http/assets/package-lock.json +++ b/apps/fz_http/assets/package-lock.json @@ -16,12 +16,15 @@ "phoenix": "file:../../../deps/phoenix", "phoenix_html": "file:../../../deps/phoenix_html", "phoenix_live_view": "file:../../../deps/phoenix_live_view", - "qrcode": "^1.4.4" + "qrcode": "^1.3.3" }, "devDependencies": { "@babel/core": "^7.16.0", "@babel/preset-env": "^7.16.0", + "@creativebulma/bulma-tooltip": "^1.2.0", + "@fontsource/fira-sans": "^4.5.0", "@fortawesome/fontawesome-free": "^5.15.3", + "@mdi/font": "^6.5.95", "admin-one-bulma-dashboard": "file:local_modules/admin-one-bulma-dashboard", "autoprefixer": "^9.8.8", "babel-loader": "^8.2.3", @@ -1625,6 +1628,12 @@ "node": ">=6.9.0" } }, + "node_modules/@creativebulma/bulma-tooltip": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@creativebulma/bulma-tooltip/-/bulma-tooltip-1.2.0.tgz", + "integrity": "sha512-ooImbeXEBxf77cttbzA7X5rC5aAWm9UsXIGViFOnsqB+6M944GkB28S5R4UWRqjFd2iW4zGEkEifAU+q43pt2w==", + "dev": true + }, "node_modules/@discoveryjs/json-ext": { "version": "0.5.5", "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.5.tgz", @@ -1634,6 +1643,12 @@ "node": ">=10.0.0" } }, + "node_modules/@fontsource/fira-sans": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@fontsource/fira-sans/-/fira-sans-4.5.0.tgz", + "integrity": "sha512-qFYIYZgerQ0iC+j6HjhUuKDOYOG3UFV8QIARdkhvZsNDpA6PTHiY8zGr5e3BjwzFFJBJOrnisxSvtbrUb82KXw==", + "dev": true + }, "node_modules/@fortawesome/fontawesome-free": { "version": "5.15.4", "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-5.15.4.tgz", @@ -1644,6 +1659,12 @@ "node": ">=6" } }, + "node_modules/@mdi/font": { + "version": "6.5.95", + "resolved": "https://registry.npmjs.org/@mdi/font/-/font-6.5.95.tgz", + "integrity": "sha512-ES5rj6J39FUkHe/b3C9SJs8bqIungYhuU7rBINTBaHOv/Ce4RCb3Gw08CZVl32W33UEkgRkzyWaIedV4at+QHg==", + "dev": true + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -2007,6 +2028,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true, "engines": { "node": ">=6" } @@ -2015,6 +2037,7 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, "dependencies": { "color-convert": "^1.9.0" }, @@ -2371,25 +2394,6 @@ "ieee754": "^1.2.1" } }, - "node_modules/buffer-alloc": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", - "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", - "dependencies": { - "buffer-alloc-unsafe": "^1.1.0", - "buffer-fill": "^1.0.0" - } - }, - "node_modules/buffer-alloc-unsafe": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", - "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==" - }, - "node_modules/buffer-fill": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", - "integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw=" - }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -2517,6 +2521,14 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/can-promise": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/can-promise/-/can-promise-0.0.1.tgz", + "integrity": "sha512-gzVrHyyrvgt0YpDm7pn04MQt8gjh0ZAhN4ZDyCRtGl6YnuuK6b4aiUTD7G52r9l4YNmxfTtEscb92vxtAlL6XQ==", + "dependencies": { + "window-or-global": "^1.0.1" + } + }, "node_modules/caniuse-api": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", @@ -2602,6 +2614,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "dev": true, "dependencies": { "string-width": "^3.1.0", "strip-ansi": "^5.2.0", @@ -2626,7 +2639,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -2635,6 +2647,7 @@ "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, "dependencies": { "color-name": "1.1.3" } @@ -2642,7 +2655,8 @@ "node_modules/color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true }, "node_modules/colord": { "version": "2.9.1", @@ -3302,7 +3316,8 @@ "node_modules/emoji-regex": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true }, "node_modules/emojis-list": { "version": "3.0.0", @@ -3313,6 +3328,14 @@ "node": ">= 4" } }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dependencies": { + "once": "^1.4.0" + } + }, "node_modules/enhanced-resolve": { "version": "5.8.3", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.8.3.tgz", @@ -3786,6 +3809,7 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, "engines": { "node": "6.* || 8.* || >= 10.*" } @@ -4193,6 +4217,14 @@ "node": ">= 0.10" } }, + "node_modules/invert-kv": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", + "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==", + "engines": { + "node": ">=4" + } + }, "node_modules/is-absolute-url": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-3.0.3.tgz", @@ -4332,8 +4364,7 @@ "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" }, "node_modules/isobject": { "version": "3.0.1", @@ -4509,6 +4540,17 @@ "node": ">= 8" } }, + "node_modules/lcid": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", + "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", + "dependencies": { + "invert-kv": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/lilconfig": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.4.tgz", @@ -4622,6 +4664,17 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/map-age-cleaner": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", + "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", + "dependencies": { + "p-defer": "^1.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/map-obj": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", @@ -4640,6 +4693,19 @@ "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==", "dev": true }, + "node_modules/mem": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz", + "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==", + "dependencies": { + "map-age-cleaner": "^0.1.1", + "mimic-fn": "^2.0.0", + "p-is-promise": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/meow": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/meow/-/meow-9.0.0.tgz", @@ -4728,7 +4794,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true, "engines": { "node": ">=6" } @@ -4901,6 +4966,11 @@ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", "dev": true }, + "node_modules/nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" + }, "node_modules/node-gyp": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-7.1.2.tgz", @@ -5152,7 +5222,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -5225,6 +5294,151 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/os-locale": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", + "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", + "dependencies": { + "execa": "^1.0.0", + "lcid": "^2.0.0", + "mem": "^4.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/os-locale/node_modules/cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dependencies": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + }, + "engines": { + "node": ">=4.8" + } + }, + "node_modules/os-locale/node_modules/execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "dependencies": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/os-locale/node_modules/get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/os-locale/node_modules/is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/os-locale/node_modules/npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "dependencies": { + "path-key": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/os-locale/node_modules/path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "engines": { + "node": ">=4" + } + }, + "node_modules/os-locale/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/os-locale/node_modules/shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dependencies": { + "shebang-regex": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/os-locale/node_modules/shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/os-locale/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/p-defer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", + "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=", + "engines": { + "node": ">=4" + } + }, + "node_modules/p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "engines": { + "node": ">=4" + } + }, + "node_modules/p-is-promise": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.1.0.tgz", + "integrity": "sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==", + "engines": { + "node": ">=6" + } + }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -5987,6 +6201,15 @@ "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==", "dev": true }, + "node_modules/pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, "node_modules/punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", @@ -5997,17 +6220,15 @@ } }, "node_modules/qrcode": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.4.4.tgz", - "integrity": "sha512-oLzEC5+NKFou9P0bMj5+v6Z40evexeE29Z9cummZXZ9QXyMr3lphkURzxjXgPJC5azpxcshoDWV1xE46z+/c3Q==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.3.3.tgz", + "integrity": "sha512-SH7V13AcJusH3GT8bMNOGz4w0L+LjcpNOU/NiOgtBhT/5DoWeZE6D5ntMJnJ84AMkoaM4kjJJoHoh9g++8lWFg==", "dependencies": { - "buffer": "^5.4.3", - "buffer-alloc": "^1.2.0", - "buffer-from": "^1.1.1", + "can-promise": "0.0.1", "dijkstrajs": "^1.0.1", "isarray": "^2.0.1", "pngjs": "^3.3.0", - "yargs": "^13.2.4" + "yargs": "^12.0.5" }, "bin": { "qrcode": "bin/qrcode" @@ -6016,27 +6237,194 @@ "node": ">=4" } }, - "node_modules/qrcode/node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], + "node_modules/qrcode/node_modules/ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "engines": { + "node": ">=4" + } + }, + "node_modules/qrcode/node_modules/cliui": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", + "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" + "string-width": "^2.1.1", + "strip-ansi": "^4.0.0", + "wrap-ansi": "^2.0.0" + } + }, + "node_modules/qrcode/node_modules/find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dependencies": { + "locate-path": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/qrcode/node_modules/get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==" + }, + "node_modules/qrcode/node_modules/locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dependencies": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/qrcode/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/qrcode/node_modules/p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dependencies": { + "p-limit": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/qrcode/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "engines": { + "node": ">=4" + } + }, + "node_modules/qrcode/node_modules/require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=" + }, + "node_modules/qrcode/node_modules/string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dependencies": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/qrcode/node_modules/strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dependencies": { + "ansi-regex": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/qrcode/node_modules/wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "dependencies": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/qrcode/node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/qrcode/node_modules/wrap-ansi/node_modules/is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dependencies": { + "number-is-nan": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/qrcode/node_modules/wrap-ansi/node_modules/string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dependencies": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/qrcode/node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/qrcode/node_modules/yargs": { + "version": "12.0.5", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz", + "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==", + "dependencies": { + "cliui": "^4.0.0", + "decamelize": "^1.2.0", + "find-up": "^3.0.0", + "get-caller-file": "^1.0.1", + "os-locale": "^3.0.0", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^2.0.0", + "which-module": "^2.0.0", + "y18n": "^3.2.1 || ^4.0.0", + "yargs-parser": "^11.1.1" + } + }, + "node_modules/qrcode/node_modules/yargs-parser": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz", + "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==", + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" } }, "node_modules/qs": { @@ -6359,7 +6747,8 @@ "node_modules/require-main-filename": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==" + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true }, "node_modules/resolve": { "version": "1.20.0", @@ -6627,8 +7016,7 @@ "node_modules/signal-exit": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.5.tgz", - "integrity": "sha512-KWcOiKeQj6ZyXx7zq4YxSMgHRlod4czeBQZrPb8OKcohcqAXShm7E20kEMle9WBt26hFcAf0qLOcp5zmY7kOqQ==", - "dev": true + "integrity": "sha512-KWcOiKeQj6ZyXx7zq4YxSMgHRlod4czeBQZrPb8OKcohcqAXShm7E20kEMle9WBt26hFcAf0qLOcp5zmY7kOqQ==" }, "node_modules/slash": { "version": "3.0.0", @@ -6773,6 +7161,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, "dependencies": { "emoji-regex": "^7.0.1", "is-fullwidth-code-point": "^2.0.0", @@ -6786,6 +7175,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, "dependencies": { "ansi-regex": "^4.1.0" }, @@ -6793,6 +7183,14 @@ "node": ">=6" } }, + "node_modules/strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/strip-final-newline": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", @@ -7455,10 +7853,16 @@ "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==", "dev": true }, + "node_modules/window-or-global": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/window-or-global/-/window-or-global-1.0.1.tgz", + "integrity": "sha1-2+RboqKRqrxW1iz2bEW3+jIpRt4=" + }, "node_modules/wrap-ansi": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "dev": true, "dependencies": { "ansi-styles": "^3.2.0", "string-width": "^3.0.0", @@ -7497,6 +7901,7 @@ "version": "13.3.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", + "dev": true, "dependencies": { "cliui": "^5.0.0", "find-up": "^3.0.0", @@ -7514,6 +7919,7 @@ "version": "13.1.2", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", + "dev": true, "dependencies": { "camelcase": "^5.0.0", "decamelize": "^1.2.0" @@ -7523,6 +7929,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, "dependencies": { "locate-path": "^3.0.0" }, @@ -7534,6 +7941,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, "dependencies": { "p-locate": "^3.0.0", "path-exists": "^3.0.0" @@ -7546,6 +7954,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, "dependencies": { "p-try": "^2.0.0" }, @@ -7560,6 +7969,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, "dependencies": { "p-limit": "^2.0.0" }, @@ -7571,6 +7981,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true, "engines": { "node": ">=4" } @@ -8672,18 +9083,36 @@ "to-fast-properties": "^2.0.0" } }, + "@creativebulma/bulma-tooltip": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@creativebulma/bulma-tooltip/-/bulma-tooltip-1.2.0.tgz", + "integrity": "sha512-ooImbeXEBxf77cttbzA7X5rC5aAWm9UsXIGViFOnsqB+6M944GkB28S5R4UWRqjFd2iW4zGEkEifAU+q43pt2w==", + "dev": true + }, "@discoveryjs/json-ext": { "version": "0.5.5", "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.5.tgz", "integrity": "sha512-6nFkfkmSeV/rqSaS4oWHgmpnYw194f6hmWF5is6b0J1naJZoiD0NTc9AiUwPHvWsowkjuHErCZT1wa0jg+BLIA==", "dev": true }, + "@fontsource/fira-sans": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@fontsource/fira-sans/-/fira-sans-4.5.0.tgz", + "integrity": "sha512-qFYIYZgerQ0iC+j6HjhUuKDOYOG3UFV8QIARdkhvZsNDpA6PTHiY8zGr5e3BjwzFFJBJOrnisxSvtbrUb82KXw==", + "dev": true + }, "@fortawesome/fontawesome-free": { "version": "5.15.4", "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-5.15.4.tgz", "integrity": "sha512-eYm8vijH/hpzr/6/1CJ/V/Eb1xQFW2nnUKArb3z+yUWv7HTwj6M7SP957oMjfZjAHU6qpoNc2wQvIxBLWYa/Jg==", "dev": true }, + "@mdi/font": { + "version": "6.5.95", + "resolved": "https://registry.npmjs.org/@mdi/font/-/font-6.5.95.tgz", + "integrity": "sha512-ES5rj6J39FUkHe/b3C9SJs8bqIungYhuU7rBINTBaHOv/Ce4RCb3Gw08CZVl32W33UEkgRkzyWaIedV4at+QHg==", + "dev": true + }, "@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -9012,12 +9441,14 @@ "ansi-regex": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true }, "ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, "requires": { "color-convert": "^1.9.0" } @@ -9277,25 +9708,6 @@ "ieee754": "^1.2.1" } }, - "buffer-alloc": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", - "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", - "requires": { - "buffer-alloc-unsafe": "^1.1.0", - "buffer-fill": "^1.0.0" - } - }, - "buffer-alloc-unsafe": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", - "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==" - }, - "buffer-fill": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", - "integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw=" - }, "buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -9402,6 +9814,14 @@ "quick-lru": "^4.0.1" } }, + "can-promise": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/can-promise/-/can-promise-0.0.1.tgz", + "integrity": "sha512-gzVrHyyrvgt0YpDm7pn04MQt8gjh0ZAhN4ZDyCRtGl6YnuuK6b4aiUTD7G52r9l4YNmxfTtEscb92vxtAlL6XQ==", + "requires": { + "window-or-global": "^1.0.1" + } + }, "caniuse-api": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", @@ -9469,6 +9889,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "dev": true, "requires": { "string-width": "^3.1.0", "strip-ansi": "^5.2.0", @@ -9489,13 +9910,13 @@ "code-point-at": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "dev": true + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" }, "color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, "requires": { "color-name": "1.1.3" } @@ -9503,7 +9924,8 @@ "color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true }, "colord": { "version": "2.9.1", @@ -9981,7 +10403,8 @@ "emoji-regex": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true }, "emojis-list": { "version": "3.0.0", @@ -9989,6 +10412,14 @@ "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", "dev": true }, + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "requires": { + "once": "^1.4.0" + } + }, "enhanced-resolve": { "version": "5.8.3", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.8.3.tgz", @@ -10343,7 +10774,8 @@ "get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true }, "get-intrinsic": { "version": "1.1.1", @@ -10632,6 +11064,11 @@ "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==", "dev": true }, + "invert-kv": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", + "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==" + }, "is-absolute-url": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-3.0.3.tgz", @@ -10735,8 +11172,7 @@ "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" }, "isobject": { "version": "3.0.1", @@ -10875,6 +11311,14 @@ "integrity": "sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ==", "dev": true }, + "lcid": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", + "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", + "requires": { + "invert-kv": "^2.0.0" + } + }, "lilconfig": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.4.tgz", @@ -10966,6 +11410,14 @@ "semver": "^6.0.0" } }, + "map-age-cleaner": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", + "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", + "requires": { + "p-defer": "^1.0.0" + } + }, "map-obj": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", @@ -10978,6 +11430,16 @@ "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==", "dev": true }, + "mem": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz", + "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==", + "requires": { + "map-age-cleaner": "^0.1.1", + "mimic-fn": "^2.0.0", + "p-is-promise": "^2.0.0" + } + }, "meow": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/meow/-/meow-9.0.0.tgz", @@ -11046,8 +11508,7 @@ "mimic-fn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" }, "min-indent": { "version": "1.0.1", @@ -11169,6 +11630,11 @@ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", "dev": true }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" + }, "node-gyp": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-7.1.2.tgz", @@ -11358,8 +11824,7 @@ "number-is-nan": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "dev": true + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" }, "oauth-sign": { "version": "0.9.0", @@ -11408,6 +11873,111 @@ "mimic-fn": "^2.1.0" } }, + "os-locale": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", + "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", + "requires": { + "execa": "^1.0.0", + "lcid": "^2.0.0", + "mem": "^4.0.0" + }, + "dependencies": { + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "requires": { + "pump": "^3.0.0" + } + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" + }, + "npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "requires": { + "path-key": "^2.0.0" + } + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=" + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "p-defer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", + "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=" + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" + }, + "p-is-promise": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.1.0.tgz", + "integrity": "sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==" + }, "p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -11918,6 +12488,15 @@ "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==", "dev": true }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, "punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", @@ -11925,26 +12504,165 @@ "dev": true }, "qrcode": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.4.4.tgz", - "integrity": "sha512-oLzEC5+NKFou9P0bMj5+v6Z40evexeE29Z9cummZXZ9QXyMr3lphkURzxjXgPJC5azpxcshoDWV1xE46z+/c3Q==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.3.3.tgz", + "integrity": "sha512-SH7V13AcJusH3GT8bMNOGz4w0L+LjcpNOU/NiOgtBhT/5DoWeZE6D5ntMJnJ84AMkoaM4kjJJoHoh9g++8lWFg==", "requires": { - "buffer": "^5.4.3", - "buffer-alloc": "^1.2.0", - "buffer-from": "^1.1.1", + "can-promise": "0.0.1", "dijkstrajs": "^1.0.1", "isarray": "^2.0.1", "pngjs": "^3.3.0", - "yargs": "^13.2.4" + "yargs": "^12.0.5" }, "dependencies": { - "buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" + }, + "cliui": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", + "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", "requires": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" + "string-width": "^2.1.1", + "strip-ansi": "^4.0.0", + "wrap-ansi": "^2.0.0" + } + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "requires": { + "locate-path": "^3.0.0" + } + }, + "get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==" + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "requires": { + "p-limit": "^2.0.0" + } + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" + }, + "require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=" + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "requires": { + "ansi-regex": "^3.0.0" + } + }, + "wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "^2.0.0" + } + } + } + }, + "yargs": { + "version": "12.0.5", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz", + "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==", + "requires": { + "cliui": "^4.0.0", + "decamelize": "^1.2.0", + "find-up": "^3.0.0", + "get-caller-file": "^1.0.1", + "os-locale": "^3.0.0", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^2.0.0", + "which-module": "^2.0.0", + "y18n": "^3.2.1 || ^4.0.0", + "yargs-parser": "^11.1.1" + } + }, + "yargs-parser": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz", + "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==", + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" } } } @@ -12205,7 +12923,8 @@ "require-main-filename": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==" + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true }, "resolve": { "version": "1.20.0", @@ -12386,8 +13105,7 @@ "signal-exit": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.5.tgz", - "integrity": "sha512-KWcOiKeQj6ZyXx7zq4YxSMgHRlod4czeBQZrPb8OKcohcqAXShm7E20kEMle9WBt26hFcAf0qLOcp5zmY7kOqQ==", - "dev": true + "integrity": "sha512-KWcOiKeQj6ZyXx7zq4YxSMgHRlod4czeBQZrPb8OKcohcqAXShm7E20kEMle9WBt26hFcAf0qLOcp5zmY7kOqQ==" }, "slash": { "version": "3.0.0", @@ -12514,6 +13232,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, "requires": { "emoji-regex": "^7.0.1", "is-fullwidth-code-point": "^2.0.0", @@ -12524,10 +13243,16 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, "requires": { "ansi-regex": "^4.1.0" } }, + "strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=" + }, "strip-final-newline": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", @@ -13004,10 +13729,16 @@ "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==", "dev": true }, + "window-or-global": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/window-or-global/-/window-or-global-1.0.1.tgz", + "integrity": "sha1-2+RboqKRqrxW1iz2bEW3+jIpRt4=" + }, "wrap-ansi": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "dev": true, "requires": { "ansi-styles": "^3.2.0", "string-width": "^3.0.0", @@ -13040,6 +13771,7 @@ "version": "13.3.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", + "dev": true, "requires": { "cliui": "^5.0.0", "find-up": "^3.0.0", @@ -13057,6 +13789,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, "requires": { "locate-path": "^3.0.0" } @@ -13065,6 +13798,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, "requires": { "p-locate": "^3.0.0", "path-exists": "^3.0.0" @@ -13074,6 +13808,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, "requires": { "p-try": "^2.0.0" } @@ -13082,6 +13817,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, "requires": { "p-limit": "^2.0.0" } @@ -13089,7 +13825,8 @@ "path-exists": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true } } }, @@ -13097,6 +13834,7 @@ "version": "13.1.2", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", + "dev": true, "requires": { "camelcase": "^5.0.0", "decamelize": "^1.2.0" diff --git a/apps/fz_http/assets/package.json b/apps/fz_http/assets/package.json index dac1bfe52..a3ed00b47 100644 --- a/apps/fz_http/assets/package.json +++ b/apps/fz_http/assets/package.json @@ -21,12 +21,15 @@ "phoenix": "file:../../../deps/phoenix", "phoenix_html": "file:../../../deps/phoenix_html", "phoenix_live_view": "file:../../../deps/phoenix_live_view", - "qrcode": "^1.4.4" + "qrcode": "^1.3.3" }, "devDependencies": { "@babel/core": "^7.16.0", "@babel/preset-env": "^7.16.0", + "@creativebulma/bulma-tooltip": "^1.2.0", + "@fontsource/fira-sans": "^4.5.0", "@fortawesome/fontawesome-free": "^5.15.3", + "@mdi/font": "^6.5.95", "admin-one-bulma-dashboard": "file:local_modules/admin-one-bulma-dashboard", "autoprefixer": "^9.8.8", "babel-loader": "^8.2.3", diff --git a/apps/fz_http/lib/fz_http/application.ex b/apps/fz_http/lib/fz_http/application.ex index a2048994b..9bcb1aa0c 100644 --- a/apps/fz_http/lib/fz_http/application.ex +++ b/apps/fz_http/lib/fz_http/application.ex @@ -6,28 +6,10 @@ defmodule FzHttp.Application do use Application def start(_type, _args) do - children = - case Application.get_env(:fz_http, :minimal) do - true -> - [ - FzHttp.Repo, - FzHttp.Vault - ] - - _ -> - [ - FzHttp.Server, - FzHttp.Repo, - FzHttp.Vault, - {Phoenix.PubSub, name: FzHttp.PubSub}, - FzHttpWeb.Endpoint - ] - end - # See https://hexdocs.pm/elixir/Supervisor.html # for other strategies and supported options opts = [strategy: :one_for_one, name: FzHttp.Supervisor] - Supervisor.start_link(children, opts) + Supervisor.start_link(children(), opts) end # Tell Phoenix to update the endpoint configuration @@ -36,4 +18,27 @@ defmodule FzHttp.Application do FzHttpWeb.Endpoint.config_change(changed, removed) :ok end + + defp children, do: children(Application.fetch_env!(:fz_http, :supervision_tree_mode)) + + defp children(:full) do + [ + FzHttp.Server, + FzHttp.Repo, + FzHttp.Vault, + FzHttpWeb.Endpoint, + {Phoenix.PubSub, name: FzHttp.PubSub}, + FzHttp.ConnectivityCheckService + ] + end + + defp children(:test) do + [ + FzHttp.Server, + FzHttp.Repo, + FzHttp.Vault, + FzHttpWeb.Endpoint, + {Phoenix.PubSub, name: FzHttp.PubSub} + ] + end end diff --git a/apps/fz_http/lib/fz_http/connectivity_check_service.ex b/apps/fz_http/lib/fz_http/connectivity_check_service.ex new file mode 100644 index 000000000..bd28d9e09 --- /dev/null +++ b/apps/fz_http/lib/fz_http/connectivity_check_service.ex @@ -0,0 +1,78 @@ +defmodule FzHttp.ConnectivityCheckService do + @moduledoc """ + A simple GenServer to periodically check for WAN connectivity by issuing + POSTs to https://ping[-dev].firez.one/{version}. + """ + use GenServer + + require Logger + + alias FzHttp.ConnectivityChecks + + def start_link(_) do + http_client().start() + GenServer.start_link(__MODULE__, %{}) + end + + @impl GenServer + def init(state) do + if enabled?() do + send(self(), :perform) + :timer.send_interval(interval(), :perform) + end + + {:ok, state} + end + + @impl GenServer + def handle_info(:perform, _state) do + # XXX: Consider passing state here to implement exponential backoff in the + # case of errors. + {:noreply, post_request()} + end + + def post_request, do: post_request(url()) + + def post_request(request_url) do + body = "" + + case http_client().post(request_url, body) do + {:ok, response} -> + ConnectivityChecks.create_connectivity_check(%{ + response_body: response.body, + response_code: response.status_code, + response_headers: Map.new(response.headers), + url: request_url + }) + + response + + {:error, error} -> + Logger.error(""" + An unexpected error occurred while performing a Firezone connectivity check to #{request_url}. Reason: #{error.reason} + """) + + error + end + end + + defp url do + Application.fetch_env!(:fz_http, :connectivity_checks_url) <> version() + end + + defp http_client do + Application.fetch_env!(:fz_http, :http_client) + end + + defp version do + Application.spec(:fz_http, :vsn) |> to_string() + end + + defp interval do + Application.fetch_env!(:fz_http, :connectivity_checks_interval) * 1_000 + end + + defp enabled? do + Application.fetch_env!(:fz_http, :connectivity_checks_enabled) + end +end diff --git a/apps/fz_http/lib/fz_http/connectivity_checks.ex b/apps/fz_http/lib/fz_http/connectivity_checks.ex new file mode 100644 index 000000000..db83fc727 --- /dev/null +++ b/apps/fz_http/lib/fz_http/connectivity_checks.ex @@ -0,0 +1,138 @@ +defmodule FzHttp.ConnectivityChecks do + @moduledoc """ + The ConnectivityChecks context. + """ + + import Ecto.Query, warn: false + alias FzHttp.Repo + + alias FzHttp.ConnectivityChecks.ConnectivityCheck + + @doc """ + Returns the list of connectivity_checks. + + ## Examples + + iex> list_connectivity_checks() + [%ConnectivityCheck{}, ...] + + """ + def list_connectivity_checks do + Repo.all(ConnectivityCheck) + end + + def list_connectivity_checks(limit: limit) when is_integer(limit) do + Repo.all( + from( + c in ConnectivityCheck, + limit: ^limit, + order_by: [desc: :inserted_at] + ) + ) + end + + @doc """ + Gets a single connectivity_check. + + Raises `Ecto.NoResultsError` if the ConnectivityCheck does not exist. + + ## Examples + + iex> get_connectivity_check!(123) + %ConnectivityCheck{} + + iex> get_connectivity_check!(456) + ** (Ecto.NoResultsError) + + """ + def get_connectivity_check!(id), do: Repo.get!(ConnectivityCheck, id) + + @doc """ + Creates a connectivity_check. + + ## Examples + + iex> create_connectivity_check(%{field: value}) + {:ok, %ConnectivityCheck{}} + + iex> create_connectivity_check(%{field: bad_value}) + {:error, %Ecto.Changeset{}} + + """ + def create_connectivity_check(attrs \\ %{}) do + %ConnectivityCheck{} + |> ConnectivityCheck.changeset(attrs) + |> Repo.insert() + end + + @doc """ + Updates a connectivity_check. + + ## Examples + + iex> update_connectivity_check(connectivity_check, %{field: new_value}) + {:ok, %ConnectivityCheck{}} + + iex> update_connectivity_check(connectivity_check, %{field: bad_value}) + {:error, %Ecto.Changeset{}} + + """ + def update_connectivity_check(%ConnectivityCheck{} = connectivity_check, attrs) do + connectivity_check + |> ConnectivityCheck.changeset(attrs) + |> Repo.update() + end + + @doc """ + Deletes a connectivity_check. + + ## Examples + + iex> delete_connectivity_check(connectivity_check) + {:ok, %ConnectivityCheck{}} + + iex> delete_connectivity_check(connectivity_check) + {:error, %Ecto.Changeset{}} + + """ + def delete_connectivity_check(%ConnectivityCheck{} = connectivity_check) do + Repo.delete(connectivity_check) + end + + @doc """ + Returns an `%Ecto.Changeset{}` for tracking connectivity_check changes. + + ## Examples + + iex> change_connectivity_check(connectivity_check) + %Ecto.Changeset{data: %ConnectivityCheck{}} + + """ + def change_connectivity_check(%ConnectivityCheck{} = connectivity_check, attrs \\ %{}) do + ConnectivityCheck.changeset(connectivity_check, attrs) + end + + @doc """ + Returns the latest connectivity_check. + """ + def latest_connectivity_check do + Repo.one( + from( + c in ConnectivityCheck, + limit: 1, + order_by: [desc: :inserted_at] + ) + ) + end + + @doc """ + Returns the latest connectivity_check's response_body which should contain the resolved public + IP. + """ + def endpoint do + case latest_connectivity_check() do + nil -> nil + connectivity_check -> connectivity_check.response_body + end + end +end diff --git a/apps/fz_http/lib/fz_http/connectivity_checks/connectivity_check.ex b/apps/fz_http/lib/fz_http/connectivity_checks/connectivity_check.ex new file mode 100644 index 000000000..7571d8984 --- /dev/null +++ b/apps/fz_http/lib/fz_http/connectivity_checks/connectivity_check.ex @@ -0,0 +1,27 @@ +defmodule FzHttp.ConnectivityChecks.ConnectivityCheck do + @moduledoc """ + Manages the connectivity_checks table + """ + use Ecto.Schema + import Ecto.Changeset + + @url_regex ~r<\Ahttps://ping(?:-dev)?\.firez\.one/\d+\.\d+\.\d+(?:\+git\.\d+\.[0-9a-fA-F]{7,})?\z> + + schema "connectivity_checks" do + field :response_body, :string + field :response_code, :integer + field :response_headers, :map + field :url, :string + + timestamps(type: :utc_datetime_usec) + end + + @doc false + def changeset(connectivity_check, attrs) do + connectivity_check + |> cast(attrs, [:url, :response_body, :response_code, :response_headers]) + |> validate_required([:url, :response_code]) + |> validate_format(:url, @url_regex) + |> validate_number(:response_code, greater_than_or_equal_to: 100, less_than: 600) + end +end diff --git a/apps/fz_http/lib/fz_http/devices.ex b/apps/fz_http/lib/fz_http/devices.ex index 6588c2a77..2a0a130d0 100644 --- a/apps/fz_http/lib/fz_http/devices.ex +++ b/apps/fz_http/lib/fz_http/devices.ex @@ -5,7 +5,7 @@ defmodule FzHttp.Devices do import Ecto.Query, warn: false alias FzCommon.NameGenerator - alias FzHttp.{Devices.Device, Repo, Users.User} + alias FzHttp.{ConnectivityChecks, Devices.Device, Repo, Settings, Users.User} @ipv4_prefix "10.3.2." @ipv6_prefix "fd00:3:2::" @@ -38,8 +38,8 @@ defmodule FzHttp.Devices do Repo.delete(device) end - def change_device(%Device{} = device) do - Device.update_changeset(device, %{}) + def change_device(%Device{} = device, attrs \\ %{}) do + Device.update_changeset(device, attrs) end def rand_name do @@ -62,4 +62,34 @@ defmodule FzHttp.Devices do } end end + + def allowed_ips(device) do + if device.use_default_allowed_ips do + Settings.default_device_allowed_ips() + else + device.allowed_ips + end + end + + def dns_servers(device) do + if device.use_default_dns_servers do + Settings.default_device_dns_servers() + else + device.dns_servers + end + end + + def endpoint(device) do + if device.use_default_endpoint do + Settings.default_device_endpoint() || ConnectivityChecks.endpoint() + else + device.endpoint + end + end + + def defaults(changeset) do + ~w(use_default_allowed_ips use_default_dns_servers use_default_endpoint)a + |> Enum.map(fn field -> {field, Device.field(changeset, field)} end) + |> Map.new() + end end diff --git a/apps/fz_http/lib/fz_http/devices/device.ex b/apps/fz_http/lib/fz_http/devices/device.ex index c0737f74d..a95c85435 100644 --- a/apps/fz_http/lib/fz_http/devices/device.ex +++ b/apps/fz_http/lib/fz_http/devices/device.ex @@ -6,10 +6,13 @@ defmodule FzHttp.Devices.Device do use Ecto.Schema import Ecto.Changeset - import FzCommon.FzNet, + import FzHttp.SharedValidators, only: [ - valid_ip?: 1, - valid_cidr?: 1 + validate_ip: 2, + validate_omitted: 2, + validate_list_of_ips: 2, + validate_no_duplicates: 2, + validate_list_of_ips_or_cidrs: 2 ] alias FzHttp.Users.User @@ -17,8 +20,12 @@ defmodule FzHttp.Devices.Device do schema "devices" do field :name, :string field :public_key, :string - field :allowed_ips, :string, read_after_writes: true - field :dns_servers, :string, read_after_writes: true + field :use_default_allowed_ips, :boolean, read_after_writes: true, default: true + field :use_default_dns_servers, :boolean, read_after_writes: true, default: true + field :use_default_endpoint, :boolean, read_after_writes: true, default: true + field :endpoint, :string + field :allowed_ips, :string + field :dns_servers, :string field :private_key, FzHttp.Encrypted.Binary field :server_public_key, :string field :remote_ip, EctoNetwork.INET @@ -32,25 +39,30 @@ defmodule FzHttp.Devices.Device do def create_changeset(device, attrs) do device - |> cast(attrs, [ - :allowed_ips, - :dns_servers, - :remote_ip, - :address, - :server_public_key, - :private_key, - :user_id, - :name, - :public_key - ]) + |> shared_cast(attrs) |> shared_changeset() end def update_changeset(device, attrs) do + device + |> shared_cast(attrs) + |> shared_changeset() + |> validate_required(:address) + end + + def field(changeset, field) do + get_field(changeset, field) + end + + defp shared_cast(device, attrs) do device |> cast(attrs, [ + :use_default_allowed_ips, + :use_default_dns_servers, + :use_default_endpoint, :allowed_ips, :dns_servers, + :endpoint, :remote_ip, :address, :server_public_key, @@ -59,8 +71,6 @@ defmodule FzHttp.Devices.Device do :name, :public_key ]) - |> shared_changeset() - |> validate_required(:address) end defp shared_changeset(changeset) do @@ -72,9 +82,12 @@ defmodule FzHttp.Devices.Device do :server_public_key, :private_key ]) + |> validate_required_unless_default([:allowed_ips, :dns_servers, :endpoint]) + |> validate_omitted_if_default([:allowed_ips, :dns_servers, :endpoint]) |> validate_list_of_ips_or_cidrs(:allowed_ips) |> validate_list_of_ips(:dns_servers) |> validate_no_duplicates(:dns_servers) + |> validate_ip(:endpoint) |> unique_constraint(:address) |> validate_number(:address, greater_than_or_equal_to: 2, less_than_or_equal_to: 254) |> unique_constraint(:public_key) @@ -82,66 +95,25 @@ defmodule FzHttp.Devices.Device do |> unique_constraint([:user_id, :name]) end - defp validate_no_duplicates(changeset, field) when is_atom(field) do - validate_change(changeset, field, fn _current_field, value -> - try do - trimmed = Enum.map(String.split(value, ","), fn el -> String.trim(el) end) - dupes = Enum.uniq(trimmed -- Enum.uniq(trimmed)) + defp validate_omitted_if_default(changeset, fields) when is_list(fields) do + fields_to_validate = + defaulted_fields(changeset, fields) + |> Enum.map(fn field -> + String.trim(Atom.to_string(field), "use_default_") |> String.to_atom() + end) - if length(dupes) > 0 do - throw(dupes) - end - - [] - catch - dupes -> - [ - {field, - "is invalid: duplicate DNS servers are not allowed: #{Enum.join(dupes, ", ")}"} - ] - end - end) + validate_omitted(changeset, fields_to_validate) end - defp validate_list_of_ips(changeset, field) when is_atom(field) do - validate_change(changeset, field, fn _current_field, value -> - try do - for ip <- String.split(value, ",") do - unless valid_ip?(String.trim(ip)) do - throw(ip) - end - end - - [] - catch - ip -> - [{field, "is invalid: #{String.trim(ip)} is not a valid IPv4 / IPv6 address"}] - end - end) + defp validate_required_unless_default(changeset, fields) when is_list(fields) do + fields_as_atoms = Enum.map(fields, fn field -> String.to_atom("use_default_#{field}") end) + fields_to_validate = fields_as_atoms -- defaulted_fields(changeset, fields) + validate_required(changeset, fields_to_validate) end - defp validate_list_of_ips_or_cidrs(changeset, field) when is_atom(field) do - validate_change(changeset, field, fn _current_field, value -> - try do - for ip_or_cidr <- String.split(value, ",") do - trimmed_ip_or_cidr = String.trim(ip_or_cidr) - - unless valid_ip?(trimmed_ip_or_cidr) or valid_cidr?(trimmed_ip_or_cidr) do - throw(ip_or_cidr) - end - end - - [] - catch - ip_or_cidr -> - [ - {field, - """ - is invalid: #{String.trim(ip_or_cidr)} is not a valid IPv4 / IPv6 address or \ - CIDR range\ - """} - ] - end - end) + defp defaulted_fields(changeset, fields) do + fields + |> Enum.map(fn field -> String.to_atom("use_default_#{field}") end) + |> Enum.filter(fn field -> get_field(changeset, field) end) end end diff --git a/apps/fz_http/lib/fz_http/macros.ex b/apps/fz_http/lib/fz_http/macros.ex new file mode 100644 index 000000000..1e7048e4b --- /dev/null +++ b/apps/fz_http/lib/fz_http/macros.ex @@ -0,0 +1,17 @@ +defmodule FzHttp.Macros do + @moduledoc """ + Metaprogramming macros + """ + + defmacro def_settings(keys) do + quote bind_quoted: [keys: keys] do + Enum.each(keys, fn key -> + fun_name = key |> String.replace(".", "_") |> String.to_atom() |> Macro.var(__MODULE__) + + def unquote(fun_name) do + get_setting!(key: unquote(key)).value + end + end) + end + end +end diff --git a/apps/fz_http/lib/fz_http/mock_http_client.ex b/apps/fz_http/lib/fz_http/mock_http_client.ex new file mode 100644 index 000000000..8fb4097b9 --- /dev/null +++ b/apps/fz_http/lib/fz_http/mock_http_client.ex @@ -0,0 +1,32 @@ +defmodule FzHttp.MockHttpClient do + @moduledoc """ + Mocks http requests in place of HTTPoison + """ + + @success_response { + :ok, + %{ + headers: [ + {"content-length", 9}, + {"date", "Tue, 07 Dec 2021 19:57:02 GMT"} + ], + status_code: 200, + body: "127.0.0.1" + } + } + @error_sentinel "invalid-url" + @error_response {:error, %{reason: :nxdomain}} + + def start, do: nil + + @doc """ + Simulates a POST. Include @error_sentinel in the request URL to simulate an error. + """ + def post(url, _body) do + if String.contains?(url, @error_sentinel) do + @error_response + else + @success_response + end + end +end diff --git a/apps/fz_http/lib/fz_http/settings.ex b/apps/fz_http/lib/fz_http/settings.ex new file mode 100644 index 000000000..f754f2177 --- /dev/null +++ b/apps/fz_http/lib/fz_http/settings.ex @@ -0,0 +1,94 @@ +defmodule FzHttp.Settings do + @moduledoc """ + The Settings context. + """ + + import FzHttp.Macros + import Ecto.Query, warn: false + alias FzHttp.Repo + + alias FzHttp.Settings.Setting + + def_settings(~w( + default.device.allowed_ips + default.device.dns_servers + default.device.endpoint + )) + + @doc """ + Returns the list of settings. + + ## Examples + + iex> list_settings() + [%Setting{}, ...] + + """ + def list_settings do + Repo.all(Setting) + end + + @doc """ + Gets a single setting by its ID. + + Raises `Ecto.NoResultsError` if the Setting does not exist. + + ## Examples + + iex> get_setting!(123) + %Setting{} + + iex> get_setting!(456) + ** (Ecto.NoResultsError) + + """ + def get_setting!(key: key) do + Repo.one!(from s in Setting, where: s.key == ^key) + end + + def get_setting!(id), do: Repo.get!(Setting, id) + + @doc """ + Updates a setting. + + ## Examples + + iex> update_setting(setting, %{field: new_value}) + {:ok, %Setting{}} + + iex> update_setting(setting, %{field: bad_value}) + {:error, %Ecto.Changeset{}} + + """ + def update_setting(%Setting{} = setting, attrs) do + setting + |> Setting.changeset(attrs) + |> Repo.update() + end + + def update_setting(key, value) when is_binary(key) do + get_setting!(key: key) + |> update_setting(%{value: value}) + end + + @doc """ + Returns an `%Ecto.Changeset{}` for tracking setting changes. + + ## Examples + + iex> change_setting(setting) + %Ecto.Changeset{data: %Setting{}} + + """ + def change_setting(%Setting{} = setting, attrs \\ %{}) do + Setting.changeset(setting, attrs) + end + + @doc """ + Returns a list of all the settings beginning with the specified key prefix. + """ + def to_list(prefix \\ "") do + starts_with = prefix <> "%" + Repo.all(from s in Setting, where: ilike(s.key, ^starts_with)) + end +end diff --git a/apps/fz_http/lib/fz_http/settings/setting.ex b/apps/fz_http/lib/fz_http/settings/setting.ex new file mode 100644 index 000000000..ae393d8fc --- /dev/null +++ b/apps/fz_http/lib/fz_http/settings/setting.ex @@ -0,0 +1,69 @@ +defmodule FzHttp.Settings.Setting do + @moduledoc """ + Represents Firezone runtime configuration settings. + + Each record in the table has a unique key corresponding to a configuration setting. + + Settings values can be changed at application runtime on the fly. + Settings cannot be created or destroyed by the running application. + + Settings are created / destroyed in migrations. + """ + + use Ecto.Schema + import Ecto.Changeset + + import FzHttp.SharedValidators, + only: [ + validate_list_of_ips: 2, + validate_list_of_ips_or_cidrs: 2, + validate_no_duplicates: 2 + ] + + schema "settings" do + field :key, :string + field :value, :string + + timestamps(type: :utc_datetime_usec) + end + + @doc false + def changeset(setting, attrs) do + setting + |> cast(attrs, [:key, :value]) + |> validate_required([:key]) + |> validate_setting() + end + + defp validate_setting(%{data: %{key: key}, changes: %{value: _value}} = changeset) do + changeset + |> validate_kv_pair(key) + end + + defp validate_setting(changeset), do: changeset + + defp validate_kv_pair(changeset, "default.device.dns_servers") do + changeset + |> validate_list_of_ips(:value) + |> validate_no_duplicates(:value) + end + + defp validate_kv_pair(changeset, "default.device.allowed_ips") do + changeset + |> validate_required(:value) + |> validate_list_of_ips_or_cidrs(:value) + |> validate_no_duplicates(:value) + end + + defp validate_kv_pair(changeset, "default.device.endpoint") do + changeset + |> validate_list_of_ips_or_cidrs(:value) + |> validate_no_duplicates(:value) + end + + defp validate_kv_pair(changeset, unknown_key) do + validate_change(changeset, :key, fn _current_field, _value -> + [{:key, "is invalid: #{unknown_key} is not a valid setting"}] + end) + end +end diff --git a/apps/fz_http/lib/fz_http/shared_validators.ex b/apps/fz_http/lib/fz_http/shared_validators.ex new file mode 100644 index 000000000..c641acb9d --- /dev/null +++ b/apps/fz_http/lib/fz_http/shared_validators.ex @@ -0,0 +1,109 @@ +defmodule FzHttp.SharedValidators do + @moduledoc """ + Shared validators to use between schemas. + """ + + import Ecto.Changeset + + import FzCommon.FzNet, + only: [ + valid_ip?: 1, + valid_cidr?: 1 + ] + + def validate_no_duplicates(changeset, field) when is_atom(field) do + validate_change(changeset, field, fn _current_field, value -> + try do + trimmed = Enum.map(String.split(value, ","), fn el -> String.trim(el) end) + dupes = Enum.uniq(trimmed -- Enum.uniq(trimmed)) + + if length(dupes) > 0 do + throw(dupes) + end + + [] + catch + dupes -> + [ + {field, + "is invalid: duplicate DNS servers are not allowed: #{Enum.join(dupes, ", ")}"} + ] + end + end) + end + + def validate_ip(changeset, field) when is_atom(field) do + validate_change(changeset, field, fn _current_field, value -> + try do + for ip <- String.split(value, ",") do + unless valid_ip?(String.trim(ip)) do + throw(ip) + end + end + + [] + catch + ip -> + [{field, "is invalid: #{String.trim(ip)} is not a valid IPv4 / IPv6 address"}] + end + end) + end + + def validate_list_of_ips(changeset, field) when is_atom(field) do + validate_change(changeset, field, fn _current_field, value -> + try do + for ip <- String.split(value, ",") do + unless valid_ip?(String.trim(ip)) do + throw(ip) + end + end + + [] + catch + ip -> + [{field, "is invalid: #{String.trim(ip)} is not a valid IPv4 / IPv6 address"}] + end + end) + end + + def validate_list_of_ips_or_cidrs(changeset, field) when is_atom(field) do + validate_change(changeset, field, fn _current_field, value -> + try do + for ip_or_cidr <- String.split(value, ",") do + trimmed_ip_or_cidr = String.trim(ip_or_cidr) + + unless valid_ip?(trimmed_ip_or_cidr) or valid_cidr?(trimmed_ip_or_cidr) do + throw(ip_or_cidr) + end + end + + [] + catch + ip_or_cidr -> + [ + {field, + """ + is invalid: #{String.trim(ip_or_cidr)} is not a valid IPv4 / IPv6 address or \ + CIDR range\ + """} + ] + end + end) + end + + def validate_omitted(changeset, fields) when is_list(fields) do + Enum.reduce(fields, changeset, fn field, accumulated_changeset -> + validate_omitted(accumulated_changeset, field) + end) + end + + def validate_omitted(changeset, field) when is_atom(field) do + validate_change(changeset, field, fn _current_field, value -> + if is_nil(value) do + [] + else + [{field, "must not be present"}] + end + end) + end +end diff --git a/apps/fz_http/lib/fz_http/users/user.ex b/apps/fz_http/lib/fz_http/users/user.ex index 8ada19226..fe0808d84 100644 --- a/apps/fz_http/lib/fz_http/users/user.ex +++ b/apps/fz_http/lib/fz_http/users/user.ex @@ -10,7 +10,6 @@ defmodule FzHttp.Users.User do import Ecto.Changeset import FzHttp.Users.PasswordHelpers - alias FzCommon.FzMap alias FzHttp.Devices.Device schema "users" do @@ -62,7 +61,7 @@ defmodule FzHttp.Users.User do |> validate_required([:sign_in_token, :sign_in_token_created_at]) end - # Password updated with user logged in + # If password isn't being changed, remove it from list of attributes to validate def update_changeset( user, %{ @@ -71,16 +70,24 @@ defmodule FzHttp.Users.User do "current_password" => nil } = attrs ) do - update_changeset(user, FzMap.compact(attrs)) + update_changeset( + user, + Map.drop(attrs, ["password", "password_confirmation", "current_password"]) + ) end + # If password isn't being changed, remove it from list of attributes to validate def update_changeset( user, %{"password" => "", "password_confirmation" => "", "current_password" => ""} = attrs ) do - update_changeset(user, FzMap.compact(attrs, "")) + update_changeset( + user, + Map.drop(attrs, ["password", "password_confirmation", "current_password"]) + ) end + # Password and other fields are being changed def update_changeset( user, %{ @@ -109,7 +116,7 @@ defmodule FzHttp.Users.User do "password_confirmation" => "" } = attrs ) do - update_changeset(user, FzMap.compact(attrs, "")) + update_changeset(user, Map.drop(attrs, ["password", "password_confirmation"])) end # Password updated from token or admin diff --git a/apps/fz_http/lib/fz_http_web/live/account_live/show_live.ex b/apps/fz_http/lib/fz_http_web/live/account_live/show_live.ex index dba6839da..3673f43c7 100644 --- a/apps/fz_http/lib/fz_http_web/live/account_live/show_live.ex +++ b/apps/fz_http/lib/fz_http_web/live/account_live/show_live.ex @@ -6,7 +6,7 @@ defmodule FzHttpWeb.AccountLive.Show do alias FzHttp.Users - @impl true + @impl Phoenix.LiveView def mount(params, session, socket) do {:ok, socket @@ -14,7 +14,7 @@ defmodule FzHttpWeb.AccountLive.Show do |> assign(:page_title, "Account")} end - @impl true + @impl Phoenix.LiveView def handle_params(_params, _url, socket) do {:noreply, socket} end diff --git a/apps/fz_http/lib/fz_http_web/live/connectivity_check_live/index.html.heex b/apps/fz_http/lib/fz_http_web/live/connectivity_check_live/index.html.heex new file mode 100644 index 000000000..14a93d4db --- /dev/null +++ b/apps/fz_http/lib/fz_http_web/live/connectivity_check_live/index.html.heex @@ -0,0 +1,41 @@ +<%= render FzHttpWeb.SharedView, "heading.html", page_title: @page_title %> + +
+ <%= render FzHttpWeb.SharedView, "flash.html", assigns %> + +
+

+ Firezone periodically checks for WAN connectivity to the Internet and logs + the result here. This is used to determine the public IP address of this + server for populating the default endpoint field in device configurations. +

+ + + + + + + + + + <%= for connectivity_check <- @connectivity_checks do %> + + + + + + <% end %> + +
Checked AtResolved IPStatus
+ … + <%= connectivity_check.response_body %> + + + +
+
+
diff --git a/apps/fz_http/lib/fz_http_web/live/connectivity_check_live/index_live.ex b/apps/fz_http/lib/fz_http_web/live/connectivity_check_live/index_live.ex new file mode 100644 index 000000000..e7edfe244 --- /dev/null +++ b/apps/fz_http/lib/fz_http_web/live/connectivity_check_live/index_live.ex @@ -0,0 +1,20 @@ +defmodule FzHttpWeb.ConnectivityCheckLive.Index do + @moduledoc """ + Manages the connectivity_checks view. + """ + use FzHttpWeb, :live_view + + alias FzHttp.ConnectivityChecks + + @impl Phoenix.LiveView + def mount(params, session, socket) do + {:ok, + socket + |> assign_defaults(params, session, &load_data/2) + |> assign(:page_title, "Connectivity Checks")} + end + + defp load_data(_params, socket) do + assign(socket, :connectivity_checks, ConnectivityChecks.list_connectivity_checks(limit: 20)) + end +end diff --git a/apps/fz_http/lib/fz_http_web/live/device_live/form_component.ex b/apps/fz_http/lib/fz_http_web/live/device_live/form_component.ex index e958bab8a..fa5163087 100644 --- a/apps/fz_http/lib/fz_http_web/live/device_live/form_component.ex +++ b/apps/fz_http/lib/fz_http_web/live/device_live/form_component.ex @@ -4,17 +4,32 @@ defmodule FzHttpWeb.DeviceLive.FormComponent do """ use FzHttpWeb, :live_component - alias FzHttp.Devices + alias FzHttp.{ConnectivityChecks, Devices, Settings} def update(assigns, socket) do - changeset = Devices.change_device(assigns.device) + device = assigns.device + changeset = Devices.change_device(device) + default_device_endpoint = Settings.default_device_endpoint() || ConnectivityChecks.endpoint() {:ok, socket |> assign(assigns) + |> assign(Devices.defaults(changeset)) + |> assign(:default_device_allowed_ips, Settings.default_device_allowed_ips()) + |> assign(:default_device_dns_servers, Settings.default_device_dns_servers()) + |> assign(:default_device_endpoint, default_device_endpoint) |> assign(:changeset, changeset)} end + def handle_event("change", %{"device" => device_params}, socket) do + changeset = Devices.change_device(socket.assigns.device, device_params) + + {:noreply, + socket + |> assign(:changeset, changeset) + |> assign(Devices.defaults(changeset))} + end + def handle_event("save", %{"device" => device_params}, socket) do device = socket.assigns.device diff --git a/apps/fz_http/lib/fz_http_web/live/device_live/form_component.html.heex b/apps/fz_http/lib/fz_http_web/live/device_live/form_component.html.heex index a4f5288c0..3c4477a0e 100644 --- a/apps/fz_http/lib/fz_http_web/live/device_live/form_component.html.heex +++ b/apps/fz_http/lib/fz_http_web/live/device_live/form_component.html.heex @@ -1,8 +1,7 @@
- <.form let={f} for={@changeset} id="edit-device" phx-target={@myself} phx-submit="save"> + <.form let={f} for={@changeset} id="edit-device" phx-change="change" phx-target={@myself} phx-submit="save">
<%= label f, :name, class: "label" %> -
<%= text_input f, :name, class: "input" %>
@@ -12,10 +11,26 @@
- <%= label f, :allowed_ips, "Allowed IPs", class: "label" %> - + <%= label f, :use_default_allowed_ips, "Use Default Allowed IPs", class: "label" %>
- <%= text_input f, :allowed_ips, class: "input" %> + + +
+

+ Default: <%= @default_device_allowed_ips %> +

+
+ +
+ <%= label f, :allowed_ips, "Allowed IPs", class: "label" %> +
+ <%= text_input f, :allowed_ips, class: "input", disabled: @use_default_allowed_ips %>

<%= error_tag f, :allowed_ips %> @@ -23,16 +38,60 @@

- <%= label f, :dns_servers, "DNS Servers", class: "label" %> - + <%= label f, :use_default_dns_servers, "Use Default DNS Servers", class: "label" %>
- <%= text_input f, :dns_servers, class: "input" %> + + +
+

+ Default: <%= @default_device_dns_servers %> +

+
+ +
+ <%= label f, :dns_servers, "DNS Servers", class: "label" %> +
+ <%= text_input f, :dns_servers, class: "input", disabled: @use_default_dns_servers %>

<%= error_tag f, :dns_servers %>

+
+ <%= label f, :use_default_endpoint, "Use Default Endpoint", class: "label" %> +
+ + +
+

+ Default: <%= @default_device_endpoint %> +

+
+ +
+ <%= label f, :endpoint, "Server Endpoint", class: "label" %> +

The IP of the server this device should connect to.

+
+ <%= text_input f, :endpoint, class: "input", disabled: @use_default_endpoint %> +
+

+ <%= error_tag f, :endpoint %> +

+
+
<%= label f, :address, "Interface Address (last octet only)", class: "label" %> 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 157d8409b..93a44505d 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 @@ -34,10 +34,13 @@
Allowed IPs
-
<%= @device.allowed_ips %>
+
<%= @allowed_ips %>
DNS Servers
-
<%= @device.dns_servers %>
+
<%= @dns_servers || "None" %>
+ +
Endpoint
+
<%= @endpoint %>
Public key
<%= @device.public_key %>
@@ -88,12 +91,12 @@ [Interface] PrivateKey = <%= @device.private_key %> Address = <%= FzHttp.Devices.ipv4_address(@device) %>/32, <%= FzHttp.Devices.ipv6_address(@device) %>/128 -DNS = <%= @device.dns_servers %> +<%= @dns_servers %> [Peer] PublicKey = <%= @device.server_public_key %> -AllowedIPs = <%= @device.allowed_ips %> -Endpoint = <%= @wireguard_endpoint %>:<%= @wireguard_port %> +AllowedIPs = <%= @allowed_ips %> +Endpoint = <%= @endpoint %>:<%= @wireguard_port %>
Or scan the QR code with your mobile phone: 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 021fb9d96..2f7ac4ef4 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 @@ -51,11 +51,34 @@ defmodule FzHttpWeb.DeviceLive.Show do device: device, user: Users.get_user!(device.user_id), page_title: device.name, - wireguard_endpoint: Application.fetch_env!(:fz_vpn, :wireguard_endpoint), + allowed_ips: Devices.allowed_ips(device), + dns_servers: dns_servers(device), + endpoint: Devices.endpoint(device), wireguard_port: Application.fetch_env!(:fz_vpn, :wireguard_port) ) 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/live/setting_live/default.html.heex b/apps/fz_http/lib/fz_http_web/live/setting_live/default.html.heex new file mode 100644 index 000000000..07bf027ed --- /dev/null +++ b/apps/fz_http/lib/fz_http_web/live/setting_live/default.html.heex @@ -0,0 +1,49 @@ +<%= render FzHttpWeb.SharedView, "heading.html", page_title: @page_title %> + +
+ <%= render FzHttpWeb.SharedView, "flash.html", assigns %> + +
+
+
+
+

Device

+
+
+
+ <%= live_component( + @socket, + FzHttpWeb.SettingLive.FormComponent, + label_text: "Allowed IPs", + placeholder: nil, + changeset: @changesets["default.device.allowed_ips"], + help_text: @help_texts.allowed_ips, + id: :allowed_ips_form_component) %> + +
+ + <%= live_component( + @socket, + FzHttpWeb.SettingLive.FormComponent, + label_text: "DNS Servers", + placeholder: nil, + changeset: @changesets["default.device.dns_servers"], + help_text: @help_texts.dns_servers, + id: :dns_servers_form_component) %> + +
+ + <%= live_component( + @socket, + FzHttpWeb.SettingLive.FormComponent, + label_text: "Endpoint", + placeholder: @endpoint_placeholder, + changeset: @changesets["default.device.endpoint"], + help_text: @help_texts.endpoint, + id: :endpoint_form_component) %> +
+
+
+
+
+
diff --git a/apps/fz_http/lib/fz_http_web/live/setting_live/default_live.ex b/apps/fz_http/lib/fz_http_web/live/setting_live/default_live.ex new file mode 100644 index 000000000..60e63919f --- /dev/null +++ b/apps/fz_http/lib/fz_http_web/live/setting_live/default_live.ex @@ -0,0 +1,46 @@ +defmodule FzHttpWeb.SettingLive.Default do + @moduledoc """ + Manages the defaults view. + """ + use FzHttpWeb, :live_view + + alias FzHttp.{ConnectivityChecks, Settings} + + @help_texts %{ + allowed_ips: """ + Configures the default AllowedIPs setting for devices. + AllowedIPs determines which destination IPs get routed through + Firezone. Specify a comma-separated list of IPs or CIDRs here to achieve split tunneling, or use + 0.0.0.0/0, ::/0 to route all device traffic through this Firezone server. + """, + dns_servers: """ + Comma-separated list of DNS servers to use for devices. + Leaving this blank will omit the DNS section in + generated device configs. + """, + endpoint: """ + IPv4 or IPv6 address that devices will be configured to connect + to. Defaults to this server's public IP if not set. + """ + } + + @impl Phoenix.LiveView + def mount(params, session, socket) do + {:ok, + socket + |> assign_defaults(params, session) + |> assign(:help_texts, @help_texts) + |> assign(:changesets, load_changesets()) + |> assign(:endpoint_placeholder, endpoint_placeholder()) + |> assign(:page_title, "Default Settings")} + end + + defp endpoint_placeholder do + ConnectivityChecks.endpoint() + end + + defp load_changesets do + Settings.to_list("default.") + |> Map.new(fn setting -> {setting.key, Settings.change_setting(setting)} end) + end +end diff --git a/apps/fz_http/lib/fz_http_web/live/setting_live/form_component.ex b/apps/fz_http/lib/fz_http_web/live/setting_live/form_component.ex new file mode 100644 index 000000000..2994ab7af --- /dev/null +++ b/apps/fz_http/lib/fz_http_web/live/setting_live/form_component.ex @@ -0,0 +1,62 @@ +defmodule FzHttpWeb.SettingLive.FormComponent do + @moduledoc """ + Handles updating setting values, one at a time + """ + use FzHttpWeb, :live_component + + alias FzHttp.Settings + + @impl Phoenix.LiveComponent + def update(assigns, socket) do + {:ok, + socket + |> assign(:input_class, "input") + |> assign(:form_changed, false) + |> assign(:input_icon, "") + |> assign(assigns)} + end + + @impl Phoenix.LiveComponent + def handle_event("save", %{"setting" => %{"value" => value}}, socket) do + key = socket.assigns.changeset.data.key + + case Settings.update_setting(key, value) do + {:ok, setting} -> + {:noreply, + socket + |> assign(:input_class, input_class(false)) + |> assign(:input_icon, input_icon(false)) + |> assign(:changeset, Settings.change_setting(setting, %{}))} + + {:error, changeset} -> + {:noreply, + socket + |> assign(:input_class, input_class(true)) + |> assign(:input_icon, input_icon(true)) + |> assign(:changeset, changeset)} + end + end + + @impl Phoenix.LiveComponent + def handle_event("change", _params, socket) do + {:noreply, + socket + |> assign(:form_changed, true)} + end + + defp input_icon(false) do + "mdi mdi-check-circle" + end + + defp input_icon(true) do + "mdi mdi-alert-circle" + end + + defp input_class(false) do + "input is-success" + end + + defp input_class(true) do + "input is-danger" + end +end diff --git a/apps/fz_http/lib/fz_http_web/live/setting_live/form_component.html.heex b/apps/fz_http/lib/fz_http_web/live/setting_live/form_component.html.heex new file mode 100644 index 000000000..bf09b6d72 --- /dev/null +++ b/apps/fz_http/lib/fz_http_web/live/setting_live/form_component.html.heex @@ -0,0 +1,26 @@ +
+ <.form let={f} for={@changeset} id={@id} phx-target={@myself} phx-change="change" phx-submit="save"> +
+ <%= label f, :value, @label_text, class: "label" %> +
+
+ <%= text_input f, :value, placeholder: @placeholder, class: @input_class %> + + + +
+ <%= error_tag f, :value %> +
+
+ <%= raw @help_text %> +
+
+ <%= if @form_changed do %> +
+ <%= submit "Save", class: "button is-primary" %> +
+ <% end %> +
+
+ +
diff --git a/apps/fz_http/lib/fz_http_web/live_helpers.ex b/apps/fz_http/lib/fz_http_web/live_helpers.ex index d3fd10f0f..d6cdad2ca 100644 --- a/apps/fz_http/lib/fz_http_web/live_helpers.ex +++ b/apps/fz_http/lib/fz_http_web/live_helpers.ex @@ -1,6 +1,8 @@ defmodule FzHttpWeb.LiveHelpers do @moduledoc """ Helpers available to all LiveViews. + XXX: Consider splitting these up using one of the techniques at + https://bernheisel.com/blog/phoenix-liveview-and-views """ import Phoenix.LiveView import Phoenix.LiveView.Helpers @@ -43,4 +45,33 @@ defmodule FzHttpWeb.LiveHelpers do modal_opts = [id: :modal, return_to: path, component: component, opts: opts] live_component(FzHttpWeb.ModalComponent, modal_opts) end + + def connectivity_check_span_class(response_code) do + if http_success?(status_digit(response_code)) do + "icon has-text-success" + else + "icon has-text-danger" + end + end + + def connectivity_check_icon_class(response_code) do + if http_success?(status_digit(response_code)) do + "mdi mdi-check-circle" + else + "mdi mdi-alert-circle" + end + end + + defp status_digit(response_code) when is_integer(response_code) do + [status_digit | _tail] = Integer.digits(response_code) + status_digit + end + + defp http_success?(2) do + true + end + + defp http_success?(_) do + false + 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 3023dd02f..33b9c7fa5 100644 --- a/apps/fz_http/lib/fz_http_web/router.ex +++ b/apps/fz_http/lib/fz_http_web/router.ex @@ -24,9 +24,6 @@ defmodule FzHttpWeb.Router do get "/", DeviceController, :index resources "/session", SessionController, only: [:new, :create, :delete], singleton: true - live "/account", AccountLive.Show, :show - live "/account/edit", AccountLive.Show, :edit - live "/users", UserLive.Index, :index live "/users/new", UserLive.Index, :new live "/users/:id", UserLive.Show, :show @@ -38,6 +35,13 @@ defmodule FzHttpWeb.Router do live "/devices/:id", DeviceLive.Show, :show live "/devices/:id/edit", DeviceLive.Show, :edit + live "/settings/default", SettingLive.Default, :default + + live "/account", AccountLive.Show, :show + live "/account/edit", AccountLive.Show, :edit + + live "/connectivity_checks", ConnectivityCheckLive.Index, :index + get "/sign_in/:token", SessionController, :create delete "/user", UserController, :delete end diff --git a/apps/fz_http/lib/fz_http_web/templates/layout/root.html.heex b/apps/fz_http/lib/fz_http_web/templates/layout/root.html.heex index 31fff31f3..d8a7002c6 100644 --- a/apps/fz_http/lib/fz_http_web/templates/layout/root.html.heex +++ b/apps/fz_http/lib/fz_http_web/templates/layout/root.html.heex @@ -17,10 +17,6 @@ - - - -
@@ -44,8 +40,8 @@ @@ -109,7 +120,7 @@
<%= link(to: "mailto:" <> feedback_recipient()) do %> - Leave us feedback! + Click here to leave feedback <% end %>
@@ -129,7 +140,5 @@
- - diff --git a/apps/fz_http/lib/fz_http_web/views/admin_view.ex b/apps/fz_http/lib/fz_http_web/views/admin_view.ex deleted file mode 100644 index 77f6ff492..000000000 --- a/apps/fz_http/lib/fz_http_web/views/admin_view.ex +++ /dev/null @@ -1,3 +0,0 @@ -defmodule FzHttpWeb.AdminView do - use FzHttpWeb, :view -end diff --git a/apps/fz_http/mix.exs b/apps/fz_http/mix.exs index d38aae2ed..1115f9373 100644 --- a/apps/fz_http/mix.exs +++ b/apps/fz_http/mix.exs @@ -58,6 +58,7 @@ defmodule FzHttp.MixProject do {:cloak_ecto, "~> 1.2"}, {:excoveralls, "~> 0.14", only: :test}, {:floki, ">= 0.0.0", only: :test}, + {:httpoison, "~> 1.8"}, {:argon2_elixir, "~> 2.0"}, {:phoenix_pubsub, "~> 2.0"}, {:phoenix_ecto, "~> 4.4"}, diff --git a/apps/fz_http/priv/repo/migrations/20211116172808_create_connectivity_checks.exs b/apps/fz_http/priv/repo/migrations/20211116172808_create_connectivity_checks.exs new file mode 100644 index 000000000..52f5be586 --- /dev/null +++ b/apps/fz_http/priv/repo/migrations/20211116172808_create_connectivity_checks.exs @@ -0,0 +1,16 @@ +defmodule FzHttp.Repo.Migrations.CreateConnectivityChecks do + use Ecto.Migration + + def change do + create table(:connectivity_checks) do + add :url, :string + add :response_body, :string + add :response_code, :integer + add :response_headers, :map + + timestamps(type: :utc_datetime_usec) + end + + create index(:connectivity_checks, :inserted_at) + end +end diff --git a/apps/fz_http/priv/repo/migrations/20211116173236_create_settings.exs b/apps/fz_http/priv/repo/migrations/20211116173236_create_settings.exs new file mode 100644 index 000000000..b3c06bb3b --- /dev/null +++ b/apps/fz_http/priv/repo/migrations/20211116173236_create_settings.exs @@ -0,0 +1,25 @@ +defmodule FzHttp.Repo.Migrations.CreateSettings do + use Ecto.Migration + + def change do + create table(:settings) do + add :key, :string + add :value, :string + + timestamps(type: :utc_datetime_usec) + end + + create unique_index(:settings, :key) + + flush() + + now = DateTime.utc_now() + + execute """ + INSERT INTO settings (key, value, inserted_at, updated_at) VALUES \ + ('default.device.dns_servers', '1.1.1.1, 1.0.0.1', '#{now}', '#{now}'), + ('default.device.allowed_ips', '0.0.0.0/0, ::/0', '#{now}', '#{now}'), + ('default.device.endpoint', null, '#{now}', '#{now}') + """ + end +end diff --git a/apps/fz_http/priv/repo/migrations/20211203012238_change_device_column_defaults.exs b/apps/fz_http/priv/repo/migrations/20211203012238_change_device_column_defaults.exs new file mode 100644 index 000000000..c277e815b --- /dev/null +++ b/apps/fz_http/priv/repo/migrations/20211203012238_change_device_column_defaults.exs @@ -0,0 +1,18 @@ +defmodule FzHttp.Repo.Migrations.ChangeDeviceColumnDefaults do + @moduledoc """ + Removes the device defaults in favor of using values from the + settings table. + """ + use Ecto.Migration + + def change do + alter table("devices") do + add :use_default_endpoint, :boolean, default: true, null: false + add :use_default_allowed_ips, :boolean, default: true, null: false + add :use_default_dns_servers, :boolean, default: true, null: false + add :endpoint, :string, default: nil + modify :allowed_ips, :string, default: nil + modify :dns_servers, :string, default: nil + end + end +end diff --git a/apps/fz_http/priv/repo/seeds.exs b/apps/fz_http/priv/repo/seeds.exs index 193464da9..4878bc45a 100644 --- a/apps/fz_http/priv/repo/seeds.exs +++ b/apps/fz_http/priv/repo/seeds.exs @@ -10,7 +10,7 @@ # We recommend using the bang functions (`insert!`, `update!` # and so on) as they will fail if something goes wrong. -alias FzHttp.{Devices, Rules, Users} +alias FzHttp.{Devices, ConnectivityChecks, Rules, Users} {:ok, user} = Users.create_user(%{ @@ -34,3 +34,19 @@ alias FzHttp.{Devices, Rules, Users} device_id: device.id, destination: %Postgrex.INET{address: {0, 0, 0, 0}, netmask: 0} }) + +{:ok, _connectivity_check} = + ConnectivityChecks.create_connectivity_check(%{ + response_headers: %{"Content-Type" => "text/plain"}, + response_body: "127.0.0.1", + response_code: 200, + url: "https://ping-dev.firez.one/0.1.19" + }) + +{:ok, _connectivity_check} = + ConnectivityChecks.create_connectivity_check(%{ + response_headers: %{"Content-Type" => "text/plain"}, + response_body: "127.0.0.1", + response_code: 400, + url: "https://ping-dev.firez.one/0.20.0" + }) diff --git a/apps/fz_http/test/fz_http/connectivity_check_service_test.exs b/apps/fz_http/test/fz_http/connectivity_check_service_test.exs new file mode 100644 index 000000000..e1815750d --- /dev/null +++ b/apps/fz_http/test/fz_http/connectivity_check_service_test.exs @@ -0,0 +1,51 @@ +defmodule FzHttp.ConnectivityCheckServiceTest do + @moduledoc """ + Tests the ConnectivityCheckService module. + """ + alias Ecto.Adapters.SQL.Sandbox + alias FzHttp.{ConnectivityChecks, ConnectivityCheckService, Repo} + use FzHttp.DataCase, async: true + + describe "post_request/0 valid url" do + @expected_check %{ + response_code: 200, + response_headers: %{"content-length" => 9, "date" => "Tue, 07 Dec 2021 19:57:02 GMT"}, + response_body: "127.0.0.1" + } + + test "inserts connectivity check" do + ConnectivityCheckService.post_request() + assert [@expected_check] = ConnectivityChecks.list_connectivity_checks() + end + end + + describe "post_request/0 error" do + @expected_response %{reason: :nxdomain} + @url "invalid-url" + + test "returns error reason" do + assert @expected_response = ConnectivityCheckService.post_request(@url) + assert ConnectivityChecks.list_connectivity_checks() == [] + end + end + + describe "handle_info/2" do + @expected_response %{ + headers: [ + {"content-length", 9}, + {"date", "Tue, 07 Dec 2021 19:57:02 GMT"} + ], + status_code: 200, + body: "127.0.0.1" + } + + setup _tags do + %{test_pid: start_supervised!(ConnectivityCheckService)} + end + + test ":perform", %{test_pid: test_pid} do + Sandbox.allow(Repo, self(), test_pid) + assert @expected_response == :sys.get_state(test_pid) + end + end +end diff --git a/apps/fz_http/test/fz_http/connectivity_checks_test.exs b/apps/fz_http/test/fz_http/connectivity_checks_test.exs new file mode 100644 index 000000000..fe06187a0 --- /dev/null +++ b/apps/fz_http/test/fz_http/connectivity_checks_test.exs @@ -0,0 +1,103 @@ +defmodule FzHttp.ConnectivityChecksTest do + use FzHttp.DataCase, async: true + + alias FzHttp.ConnectivityChecks + + describe "connectivity_checks" do + alias FzHttp.ConnectivityChecks.ConnectivityCheck + + import FzHttp.ConnectivityChecksFixtures + + @invalid_attrs %{response_body: nil, response_code: nil, response_headers: nil, url: nil} + + test "list_connectivity_checks/0 returns all connectivity_checks" do + connectivity_check = connectivity_check_fixture() + assert ConnectivityChecks.list_connectivity_checks() == [connectivity_check] + end + + test "list_connectivity_checks/1 applies limit" do + connectivity_check_fixture() + connectivity_check = connectivity_check_fixture() + assert ConnectivityChecks.list_connectivity_checks(limit: 1) == [connectivity_check] + end + + test "endpoint/0 returns latest check's response body" do + connectivity_check = connectivity_check_fixture() + assert ConnectivityChecks.endpoint() == connectivity_check.response_body + end + + test "get_connectivity_check!/1 returns the connectivity_check with given id" do + connectivity_check = connectivity_check_fixture() + + assert ConnectivityChecks.get_connectivity_check!(connectivity_check.id) == + connectivity_check + end + + test "create_connectivity_check/1 with valid data creates a connectivity_check" do + valid_attrs = %{ + response_body: "some response_body", + response_code: 500, + response_headers: %{"updated_response" => "headers"}, + url: "https://ping-dev.firez.one/1.1.1" + } + + assert {:ok, %ConnectivityCheck{} = connectivity_check} = + ConnectivityChecks.create_connectivity_check(valid_attrs) + + assert connectivity_check.response_body == "some response_body" + assert connectivity_check.response_code == 500 + assert connectivity_check.response_headers == %{"updated_response" => "headers"} + assert connectivity_check.url == "https://ping-dev.firez.one/1.1.1" + end + + test "create_connectivity_check/1 with invalid data returns error changeset" do + assert {:error, %Ecto.Changeset{}} = + ConnectivityChecks.create_connectivity_check(@invalid_attrs) + end + + test "update_connectivity_check/2 with valid data updates the connectivity_check" do + connectivity_check = connectivity_check_fixture() + + update_attrs = %{ + response_body: "some updated response_body", + response_code: 500, + response_headers: %{"updated" => "response headers"}, + url: "https://ping.firez.one/6.6.6" + } + + assert {:ok, %ConnectivityCheck{} = connectivity_check} = + ConnectivityChecks.update_connectivity_check(connectivity_check, update_attrs) + + 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" + end + + test "update_connectivity_check/2 with invalid data returns error changeset" do + connectivity_check = connectivity_check_fixture() + + assert {:error, %Ecto.Changeset{}} = + ConnectivityChecks.update_connectivity_check(connectivity_check, @invalid_attrs) + + assert connectivity_check == + ConnectivityChecks.get_connectivity_check!(connectivity_check.id) + end + + test "delete_connectivity_check/1 deletes the connectivity_check" do + connectivity_check = connectivity_check_fixture() + + assert {:ok, %ConnectivityCheck{}} = + ConnectivityChecks.delete_connectivity_check(connectivity_check) + + assert_raise Ecto.NoResultsError, fn -> + ConnectivityChecks.get_connectivity_check!(connectivity_check.id) + end + end + + test "change_connectivity_check/1 returns a connectivity_check changeset" do + connectivity_check = connectivity_check_fixture() + assert %Ecto.Changeset{} = ConnectivityChecks.change_connectivity_check(connectivity_check) + end + end +end diff --git a/apps/fz_http/test/fz_http/devices_test.exs b/apps/fz_http/test/fz_http/devices_test.exs index cbdd52698..ebf79c391 100644 --- a/apps/fz_http/test/fz_http/devices_test.exs +++ b/apps/fz_http/test/fz_http/devices_test.exs @@ -52,10 +52,12 @@ defmodule FzHttp.DevicesTest do @attrs %{ name: "Go hard or go home.", - allowed_ips: "0.0.0.0" + allowed_ips: "0.0.0.0", + use_default_allowed_ips: false } @valid_dns_servers_attrs %{ + use_default_dns_servers: false, dns_servers: "1.1.1.1, 1.0.0.1, 2606:4700:4700::1111, 2606:4700:4700::1001" } @@ -68,6 +70,7 @@ defmodule FzHttp.DevicesTest do } @valid_allowed_ips_attrs %{ + use_default_allowed_ips: false, allowed_ips: "0.0.0.0/0, ::/0, ::0/0, 192.168.1.0/24" } diff --git a/apps/fz_http/test/fz_http/settings_test.exs b/apps/fz_http/test/fz_http/settings_test.exs new file mode 100644 index 000000000..3601c34d6 --- /dev/null +++ b/apps/fz_http/test/fz_http/settings_test.exs @@ -0,0 +1,73 @@ +defmodule FzHttp.SettingsTest do + use FzHttp.DataCase + + alias FzHttp.Settings + + @setting_keys ~w( + default.device.dns_servers + default.device.allowed_ips + default.device.endpoint + ) + + describe "settings" do + alias FzHttp.Settings.Setting + + import FzHttp.SettingsFixtures + + @valid_settings %{ + "default.device.dns_servers" => "8.8.8.8", + "default.device.allowed_ips" => "::/0", + "default.device.endpoint" => "172.10.10.10" + } + @invalid_settings %{ + "default.device.dns_servers" => "foobar", + "default.device.allowed_ips" => nil, + "default.device.endpoint" => "foobar" + } + + test "get_setting!/1 returns the setting with given id" do + setting = setting_fixture() + assert Settings.get_setting!(setting.id) == setting + end + + test "get_setting!/1 returns the setting with the given key" do + for key <- @setting_keys do + setting = Settings.get_setting!(key: key) + assert setting.key == key + end + end + + test "update_setting/2 with valid data updates the setting via provided setting" do + for key <- @setting_keys do + value = @valid_settings[key] + setting = Settings.get_setting!(key: key) + assert {:ok, %Setting{} = setting} = Settings.update_setting(setting, %{value: value}) + assert setting.key == key + assert setting.value == value + end + end + + test "update_setting/2 with valid data updates the setting via key, value" do + for key <- @setting_keys do + value = @valid_settings[key] + assert {:ok, %Setting{} = setting} = Settings.update_setting(key, value) + assert setting.key == key + assert setting.value == value + end + end + + test "update_setting/2 with invalid data returns error changeset" do + for key <- @setting_keys do + value = @invalid_settings[key] + assert {:error, %Ecto.Changeset{}} = Settings.update_setting(key, value) + setting = Settings.get_setting!(key: key) + refute setting.value == value + end + end + + test "change_setting/1 returns a setting changeset" do + setting = setting_fixture() + assert %Ecto.Changeset{} = Settings.change_setting(setting) + end + end +end diff --git a/apps/fz_http/test/fz_http_web/live/account_live/show_test.exs b/apps/fz_http/test/fz_http_web/live/account_live/show_test.exs index 2395c9286..3680b08fd 100644 --- a/apps/fz_http/test/fz_http_web/live/account_live/show_test.exs +++ b/apps/fz_http/test/fz_http_web/live/account_live/show_test.exs @@ -45,6 +45,26 @@ defmodule FzHttpWeb.AccountLive.ShowTest do assert flash["info"] == "Account updated successfully." end + test "doesn't allow empty email", %{authed_conn: conn} do + path = Routes.account_show_path(conn, :edit) + {:ok, view, _html} = live(conn, path) + + test_view = + view + |> element("#account-edit") + |> render_submit(%{ + "user" => %{ + "email" => "", + "current_password" => "", + "password" => "", + "password_confirmation" => "" + } + }) + + refute_redirected(view, Routes.account_show_path(conn, :show)) + assert test_view =~ "can't be blank" + end + test "renders validation errors", %{authed_conn: conn} do path = Routes.account_show_path(conn, :edit) {:ok, view, _html} = live(conn, path) diff --git a/apps/fz_http/test/fz_http_web/live/connectivity_check_live/index_test.exs b/apps/fz_http/test/fz_http_web/live/connectivity_check_live/index_test.exs new file mode 100644 index 000000000..f9955b981 --- /dev/null +++ b/apps/fz_http/test/fz_http_web/live/connectivity_check_live/index_test.exs @@ -0,0 +1,27 @@ +defmodule FzHttpWeb.ConnectivityCheckLive.IndexTest do + use FzHttpWeb.ConnCase, async: true + + describe "authenticated/connectivity_checks list" do + setup :create_connectivity_checks + + test "show connectivity checks", %{ + authed_conn: conn, + connectivity_checks: connectivity_checks + } do + path = Routes.connectivity_check_index_path(conn, :index) + {:ok, _view, html} = live(conn, path) + + for connectivity_check <- connectivity_checks do + assert html =~ DateTime.to_iso8601(connectivity_check.inserted_at) + end + end + end + + describe "unauthenticated/connectivity_checks list" do + test "mount redirects to session path", %{unauthed_conn: conn} do + path = Routes.connectivity_check_index_path(conn, :index) + expected_path = Routes.session_path(conn, :new) + assert {:error, {:redirect, %{to: ^expected_path}}} = live(conn, path) + end + end +end diff --git a/apps/fz_http/test/fz_http_web/live/device_live/show_test.exs b/apps/fz_http/test/fz_http_web/live/device_live/show_test.exs index 045bd17be..4edf967ed 100644 --- a/apps/fz_http/test/fz_http_web/live/device_live/show_test.exs +++ b/apps/fz_http/test/fz_http_web/live/device_live/show_test.exs @@ -6,8 +6,36 @@ defmodule FzHttpWeb.DeviceLive.ShowTest do @valid_params %{"device" => %{"name" => "new_name"}} @invalid_params %{"device" => %{"name" => ""}} + @allowed_ips "2.2.2.2" + @allowed_ips_change %{ + "device" => %{"use_default_allowed_ips" => "false", "allowed_ips" => @allowed_ips} + } + @allowed_ips_unchanged %{ + "device" => %{"use_default_allowed_ips" => "true", "allowed_ips" => @allowed_ips} + } @dns_servers "8.8.8.8, 8.8.4.4" - @dns_servers_change %{"device" => %{"dns_servers" => @dns_servers}} + @dns_servers_change %{ + "device" => %{"use_default_dns_servers" => "false", "dns_servers" => @dns_servers} + } + @dns_servers_unchanged %{ + "device" => %{"use_default_dns_servers" => "true", "dns_servers" => @dns_servers} + } + @wireguard_endpoint "6.6.6.6" + @endpoint_change %{ + "device" => %{"use_default_endpoint" => "false", "endpoint" => @wireguard_endpoint} + } + @endpoint_unchanged %{ + "device" => %{"use_default_endpoint" => "true", "dns_servers" => @wireguard_endpoint} + } + @default_allowed_ips_change %{ + "device" => %{"use_default_allowed_ips" => "false"} + } + @default_dns_servers_change %{ + "device" => %{"use_default_dns_servers" => "false"} + } + @default_endpoint_change %{ + "device" => %{"use_default_endpoint" => "false"} + } test "shows device details", %{authed_conn: conn, device: device} do path = Routes.device_show_path(conn, :show, device) @@ -39,6 +67,66 @@ defmodule FzHttpWeb.DeviceLive.ShowTest do assert flash["info"] == "Device updated successfully." end + test "prevents allowed_ips changes when use_default_allowed_ips is true ", %{ + authed_conn: conn, + device: device + } do + path = Routes.device_show_path(conn, :edit, device) + {:ok, view, _html} = live(conn, path) + + test_view = + view + |> form("#edit-device") + |> render_submit(@allowed_ips_unchanged) + + assert test_view =~ "must not be present" + end + + test "prevents dns_servers changes when use_default_dns_servers is true", %{ + authed_conn: conn, + device: device + } do + path = Routes.device_show_path(conn, :edit, device) + {:ok, view, _html} = live(conn, path) + + test_view = + view + |> form("#edit-device") + |> render_submit(@dns_servers_unchanged) + + assert test_view =~ "must not be present" + end + + test "prevents endpoint changes when use_default_endpoint is true", %{ + authed_conn: conn, + device: device + } do + path = Routes.device_show_path(conn, :edit, device) + {:ok, view, _html} = live(conn, path) + + test_view = + view + |> form("#edit-device") + |> render_submit(@endpoint_unchanged) + + assert test_view =~ "must not be present" + end + + test "allows allowed_ips changes", %{authed_conn: conn, device: device} do + path = Routes.device_show_path(conn, :edit, device) + {:ok, view, _html} = live(conn, path) + + view + |> form("#edit-device") + |> render_submit(@allowed_ips_change) + + flash = assert_redirected(view, Routes.device_show_path(conn, :show, device)) + assert flash["info"] == "Device updated successfully." + + {:ok, _view, html} = live(conn, path) + assert html =~ "AllowedIPs = #{@allowed_ips}" + end + test "allows dns_servers changes", %{authed_conn: conn, device: device} do path = Routes.device_show_path(conn, :edit, device) {:ok, view, _html} = live(conn, path) @@ -54,6 +142,21 @@ defmodule FzHttpWeb.DeviceLive.ShowTest do assert html =~ "DNS = #{@dns_servers}" end + test "allows endpoint changes", %{authed_conn: conn, device: device} do + path = Routes.device_show_path(conn, :edit, device) + {:ok, view, _html} = live(conn, path) + + view + |> form("#edit-device") + |> render_submit(@endpoint_change) + + flash = assert_redirected(view, Routes.device_show_path(conn, :show, device)) + assert flash["info"] == "Device updated successfully." + + {:ok, _view, html} = live(conn, path) + assert html =~ "Endpoint = #{@wireguard_endpoint}:51820" + end + test "prevents empty names", %{authed_conn: conn, device: device} do path = Routes.device_show_path(conn, :edit, device) {:ok, view, _html} = live(conn, path) @@ -65,6 +168,48 @@ defmodule FzHttpWeb.DeviceLive.ShowTest do assert test_view =~ "can't be blank" end + + test "on use_default_allowed_ips change", %{authed_conn: conn, device: device} do + path = Routes.device_show_path(conn, :edit, device) + {:ok, view, _html} = live(conn, path) + + test_view = + view + |> form("#edit-device") + |> render_change(@default_allowed_ips_change) + + assert test_view =~ """ + \ + """ + end + + test "on use_default_dns_servers change", %{authed_conn: conn, device: device} do + path = Routes.device_show_path(conn, :edit, device) + {:ok, view, _html} = live(conn, path) + + test_view = + view + |> form("#edit-device") + |> render_change(@default_dns_servers_change) + + assert test_view =~ """ + \ + """ + end + + test "on use_default_endpoint change", %{authed_conn: conn, device: device} do + path = Routes.device_show_path(conn, :edit, device) + {:ok, view, _html} = live(conn, path) + + test_view = + view + |> form("#edit-device") + |> render_change(@default_endpoint_change) + + assert test_view =~ """ + \ + """ + end end describe "delete own device" do diff --git a/apps/fz_http/test/fz_http_web/live/setting_live/default_test.exs b/apps/fz_http/test/fz_http_web/live/setting_live/default_test.exs new file mode 100644 index 000000000..a407f7e39 --- /dev/null +++ b/apps/fz_http/test/fz_http_web/live/setting_live/default_test.exs @@ -0,0 +1,169 @@ +defmodule FzHttpWeb.SettingLive.DefaultTest do + use FzHttpWeb.ConnCase, async: true + + alias FzHttp.Settings + + describe "authenticated/settings default" do + @valid_allowed_ips %{ + "setting" => %{"value" => "1.1.1.1"} + } + @valid_dns_servers %{ + "setting" => %{"value" => "1.1.1.1"} + } + @valid_endpoint %{ + "setting" => %{"value" => "1.1.1.1"} + } + + @invalid_allowed_ips %{ + "setting" => %{"value" => "foobar"} + } + @invalid_dns_servers %{ + "setting" => %{"value" => "foobar"} + } + @invalid_endpoint %{ + "setting" => %{"value" => "foobar"} + } + + setup %{authed_conn: conn} do + path = Routes.setting_default_path(conn, :default) + {:ok, view, html} = live(conn, path) + + %{html: html, view: view} + end + + test "renders current settings", %{html: html} do + assert html =~ Settings.default_device_allowed_ips() + assert html =~ Settings.default_device_dns_servers() + + assert html =~ """ + id="endpoint_form_component"\ + """ + end + + test "hides Save button by default", %{html: html} do + refute html =~ """ + \ + """ + end + + test "shows Save button after allowed_ips form is changed", %{view: view} do + test_view = + view + |> element("#allowed_ips_form_component") + |> render_change(@valid_allowed_ips) + + assert test_view =~ """ + \ + """ + end + + test "shows Save button after dns_servers form is changed", %{view: view} do + test_view = + view + |> element("#dns_servers_form_component") + |> render_change(@valid_dns_servers) + + assert test_view =~ """ + \ + """ + end + + test "shows Save button after endpoint form is changed", %{view: view} do + test_view = + view + |> element("#endpoint_form_component") + |> render_change(@valid_endpoint) + + assert test_view =~ """ + \ + """ + end + + test "updates default allowed_ips", %{view: view} do + test_view = + view + |> element("#allowed_ips_form_component") + |> render_submit(@valid_allowed_ips) + + refute test_view =~ "is invalid" + + assert test_view =~ """ + \ + """ + end + + test "updates default dns_servers", %{view: view} do + test_view = + view + |> element("#dns_servers_form_component") + |> render_submit(@valid_dns_servers) + + refute test_view =~ "is invalid" + + assert test_view =~ """ + \ + """ + end + + test "updates default endpoint", %{view: view} do + test_view = + view + |> element("#endpoint_form_component") + |> render_submit(@valid_endpoint) + + refute test_view =~ "is invalid" + + assert test_view =~ """ + \ + """ + end + + test "prevents invalid allowed_ips", %{view: view} do + test_view = + view + |> element("#allowed_ips_form_component") + |> render_submit(@invalid_allowed_ips) + + assert test_view =~ "is invalid" + + refute test_view =~ """ + element("#dns_servers_form_component") + |> render_submit(@invalid_dns_servers) + + assert test_view =~ "is invalid" + + refute test_view =~ """ + element("#endpoint_form_component") + |> render_submit(@invalid_endpoint) + + assert test_view =~ "is invalid" + + refute test_view =~ """ + - {:ok, user} = - %{email: email, password: "testtest", password_confirmation: "testtest"} - |> Map.merge(attrs) - |> Users.create_user() - - user - - %User{} = user -> - user - end - end - - def device(attrs \\ %{}) do - # don't create a user if user_id is passed - user_id = Map.get_lazy(attrs, :user_id, fn -> user().id end) - - default_attrs = %{ - user_id: user_id, - public_key: "test-pubkey", - name: "factory", - private_key: "test-privkey", - server_public_key: "test-server-pubkey" - } - - {:ok, device} = Devices.create_device(Map.merge(default_attrs, attrs)) - device - end - - def rule4(attrs \\ %{}) do - rule(attrs) - end - - def rule6(attrs \\ %{}) do - rule(Map.merge(attrs, %{destination: "::/0"})) - end - - def rule(attrs \\ %{}) do - default_attrs = %{ - destination: "10.10.10.0/24" - } - - {:ok, rule} = Rules.create_rule(Map.merge(default_attrs, attrs)) - rule - end - - def session(_attrs \\ %{}) do - email = user().email - record = Sessions.get_session!(email: email) - create_params = %{email: email, password: "testtest"} - {:ok, session} = Sessions.create_session(record, create_params) - session - end - - defp counter do - System.unique_integer([:positive]) - end -end diff --git a/apps/fz_http/test/support/fixtures/connectivity_checks_fixtures.ex b/apps/fz_http/test/support/fixtures/connectivity_checks_fixtures.ex new file mode 100644 index 000000000..7f17c6c6c --- /dev/null +++ b/apps/fz_http/test/support/fixtures/connectivity_checks_fixtures.ex @@ -0,0 +1,25 @@ +defmodule FzHttp.ConnectivityChecksFixtures do + @moduledoc """ + This module defines test helpers for creating + entities via the `FzHttp.ConnectivityChecks` context. + """ + + alias FzHttp.ConnectivityChecks + + @doc """ + Generate a connectivity_check. + """ + def connectivity_check_fixture(attrs \\ %{}) do + {:ok, connectivity_check} = + attrs + |> Enum.into(%{ + 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" + }) + |> ConnectivityChecks.create_connectivity_check() + + connectivity_check + end +end diff --git a/apps/fz_http/test/support/fixtures/devices_fixtures.ex b/apps/fz_http/test/support/fixtures/devices_fixtures.ex new file mode 100644 index 000000000..e4836575e --- /dev/null +++ b/apps/fz_http/test/support/fixtures/devices_fixtures.ex @@ -0,0 +1,27 @@ +defmodule FzHttp.DevicesFixtures do + @moduledoc """ + This module defines test helpers for creating + entities via the `FzHttp.Devices` context. + """ + + alias FzHttp.{Devices, UsersFixtures} + + @doc """ + Generate a device. + """ + def device(attrs \\ %{}) do + # Don't create a user if user_id is passed + user_id = Map.get_lazy(attrs, :user_id, fn -> UsersFixtures.user().id end) + + default_attrs = %{ + user_id: user_id, + public_key: "test-pubkey", + name: "factory", + private_key: "test-privkey", + server_public_key: "test-server-pubkey" + } + + {:ok, device} = Devices.create_device(Map.merge(default_attrs, attrs)) + device + end +end diff --git a/apps/fz_http/test/support/fixtures/rules_fixtures.ex b/apps/fz_http/test/support/fixtures/rules_fixtures.ex new file mode 100644 index 000000000..060b1cac8 --- /dev/null +++ b/apps/fz_http/test/support/fixtures/rules_fixtures.ex @@ -0,0 +1,25 @@ +defmodule FzHttp.RulesFixtures do + @moduledoc """ + This module defines test helpers for creating + entities via the `FzHttp.Rules` context. + """ + + alias FzHttp.Rules + + def rule4(attrs \\ %{}) do + rule(attrs) + end + + def rule6(attrs \\ %{}) do + rule(Map.merge(attrs, %{destination: "::/0"})) + end + + def rule(attrs \\ %{}) do + default_attrs = %{ + destination: "10.10.10.0/24" + } + + {:ok, rule} = Rules.create_rule(Map.merge(default_attrs, attrs)) + rule + end +end diff --git a/apps/fz_http/test/support/fixtures/sessions_fixtures.ex b/apps/fz_http/test/support/fixtures/sessions_fixtures.ex new file mode 100644 index 000000000..147422091 --- /dev/null +++ b/apps/fz_http/test/support/fixtures/sessions_fixtures.ex @@ -0,0 +1,15 @@ +defmodule FzHttp.SessionsFixtures do + @moduledoc """ + This module defines test helpers for creating + entities via the `FzHttp.Sessions` context. + """ + alias FzHttp.{Sessions, UsersFixtures} + + def session(_attrs \\ %{}) do + email = UsersFixtures.user().email + record = Sessions.get_session!(email: email) + create_params = %{email: email, password: "testtest"} + {:ok, session} = Sessions.create_session(record, create_params) + session + end +end diff --git a/apps/fz_http/test/support/fixtures/settings_fixtures.ex b/apps/fz_http/test/support/fixtures/settings_fixtures.ex new file mode 100644 index 000000000..7b818a339 --- /dev/null +++ b/apps/fz_http/test/support/fixtures/settings_fixtures.ex @@ -0,0 +1,15 @@ +defmodule FzHttp.SettingsFixtures do + @moduledoc """ + This module defines test helpers for creating + entities via the `FzHttp.Settings` context. + """ + + alias FzHttp.Settings + + @doc """ + Generate a setting. + """ + def setting_fixture(key \\ "default.device.dns_servers") do + Settings.get_setting!(key: key) + end +end diff --git a/apps/fz_http/test/support/fixtures/users_fixtures.ex b/apps/fz_http/test/support/fixtures/users_fixtures.ex new file mode 100644 index 000000000..2508b637c --- /dev/null +++ b/apps/fz_http/test/support/fixtures/users_fixtures.ex @@ -0,0 +1,32 @@ +defmodule FzHttp.UsersFixtures do + @moduledoc """ + This module defines test helpers for creating + entities via the `FzHttp.Users` context. + """ + + alias FzHttp.{Repo, Users, Users.User} + + @doc """ + Generate a user specified by email, or generate a new otherwise. + """ + def user(attrs \\ %{}) do + email = Map.get(attrs, :email, "test-#{counter()}@test") + + case Repo.get_by(User, email: email) do + nil -> + {:ok, user} = + %{email: email, password: "testtest", password_confirmation: "testtest"} + |> Map.merge(attrs) + |> Users.create_user() + + user + + %User{} = user -> + user + end + end + + defp counter do + System.unique_integer([:positive]) + end +end diff --git a/apps/fz_http/test/support/mock_helpers.ex b/apps/fz_http/test/support/mock_helpers.ex deleted file mode 100644 index 423cdd7f1..000000000 --- a/apps/fz_http/test/support/mock_helpers.ex +++ /dev/null @@ -1,21 +0,0 @@ -defmodule FzHttp.MockHelpers do - @moduledoc """ - Helpers for test life cycle - """ - - import ExUnit.Callbacks - - def mock_enable_signup do - mock_disable_signup(false) - end - - def mock_disable_signup do - mock_disable_signup(true) - end - - def mock_disable_signup(val) when val in [true, false] do - old_val = Application.get_env(:fz_http, :disable_signup) - Application.put_env(:fz_http, :disable_signup, val) - on_exit(fn -> Application.put_env(:fz_http, :disable_signup, old_val) end) - end -end diff --git a/apps/fz_http/test/support/test_helpers.ex b/apps/fz_http/test/support/test_helpers.ex index f8012ff19..8f3ab5e5f 100644 --- a/apps/fz_http/test/support/test_helpers.ex +++ b/apps/fz_http/test/support/test_helpers.ex @@ -2,7 +2,17 @@ defmodule FzHttp.TestHelpers do @moduledoc """ Test setup helpers """ - alias FzHttp.{Fixtures, Repo, Users, Users.User} + + alias FzHttp.{ + ConnectivityChecksFixtures, + DevicesFixtures, + Repo, + RulesFixtures, + SessionsFixtures, + Users, + Users.User, + UsersFixtures + } def clear_users do Repo.delete_all(User) @@ -11,19 +21,19 @@ defmodule FzHttp.TestHelpers do def create_device(tags) do device = if tags[:unauthed] || is_nil(tags[:user_id]) do - Fixtures.device() + DevicesFixtures.device() else - Fixtures.device(%{user_id: tags[:user_id]}) + DevicesFixtures.device(%{user_id: tags[:user_id]}) end {:ok, device: device} end def create_other_user_device(_) do - user_id = Fixtures.user(%{email: "other_user@test"}).id + user_id = UsersFixtures.user(%{email: "other_user@test"}).id device = - Fixtures.device(%{ + DevicesFixtures.device(%{ user_id: user_id, name: "other device", public_key: "other-pubkey", @@ -33,17 +43,26 @@ defmodule FzHttp.TestHelpers do {:ok, other_device: device} end + def create_connectivity_checks(_tags) do + connectivity_checks = + Enum.map(1..5, fn _i -> + ConnectivityChecksFixtures.connectivity_check_fixture() + end) + + {:ok, connectivity_checks: connectivity_checks} + end + def create_devices(tags) do user_id = if tags[:unathed] || is_nil(tags[:user_id]) do - Fixtures.user().id + UsersFixtures.user().id else tags[:user_id] end devices = Enum.map(1..5, fn num -> - Fixtures.device(%{ + DevicesFixtures.device(%{ name: "device #{num}", public_key: "#{num}", private_key: "#{num}", @@ -55,37 +74,37 @@ defmodule FzHttp.TestHelpers do end def create_user(_) do - user = Fixtures.user() + user = UsersFixtures.user() {:ok, user: user} end def create_session(_) do - session = Fixtures.session() + session = SessionsFixtures.session() {:ok, session: session} end def create_accept_rule(_) do - rule = Fixtures.rule(%{action: :accept}) + rule = RulesFixtures.rule(%{action: :accept}) {:ok, rule: rule} end def create_drop_rule(_) do - rule = Fixtures.rule(%{action: :drop}) + rule = RulesFixtures.rule(%{action: :drop}) {:ok, rule: rule} end def create_rule(_) do - rule = Fixtures.rule(%{}) + rule = RulesFixtures.rule(%{}) {:ok, rule: rule} end def create_rule6(_) do - rule = Fixtures.rule6(%{}) + rule = RulesFixtures.rule6(%{}) {:ok, rule6: rule} end def create_rule4(_) do - rule = Fixtures.rule4(%{}) + rule = RulesFixtures.rule4(%{}) {:ok, rule4: rule} end @@ -97,26 +116,26 @@ defmodule FzHttp.TestHelpers do 1..5 |> Enum.map(fn num -> destination = "#{num}.#{num}.#{num}.0/24" - Fixtures.rule(%{destination: destination}) + RulesFixtures.rule(%{destination: destination}) end) {:ok, rules: rules} end def create_user_with_valid_sign_in_token(_) do - {:ok, user: %User{} = Fixtures.user(Users.sign_in_keys())} + {:ok, user: %User{} = UsersFixtures.user(Users.sign_in_keys())} end def create_user_with_expired_sign_in_token(_) do expired = DateTime.add(DateTime.utc_now(), -1 * 86_401) params = %{Users.sign_in_keys() | sign_in_token_created_at: expired} - {:ok, user: %User{} = Fixtures.user(params)} + {:ok, user: %User{} = UsersFixtures.user(params)} end def create_users(%{count: count}) do users = Enum.map(1..count, fn i -> - Fixtures.user(%{email: "userlist#{i}@test"}) + UsersFixtures.user(%{email: "userlist#{i}@test"}) end) {:ok, users: users} diff --git a/config/config.exs b/config/config.exs index e79f43ab5..6bd1acea8 100644 --- a/config/config.exs +++ b/config/config.exs @@ -36,13 +36,17 @@ github_sha = end config :fz_http, + supervision_tree_mode: :full, + http_client: HTTPoison, + connectivity_checks_enabled: true, + connectivity_checks_interval: 3_600, + connectivity_checks_url: "https://ping-dev.firez.one/", github_sha: github_sha, cookie_signing_salt: "Z9eq8iof", ecto_repos: [FzHttp.Repo], admin_email: "firezone@localhost", default_admin_password: "firezone", events_module: FzHttpWeb.Events, - disable_signup: true, server_process_opts: [name: {:global, :fz_http_server}] config :fz_wall, diff --git a/config/dev.exs b/config/dev.exs index 654d5030c..31bf0f676 100644 --- a/config/dev.exs +++ b/config/dev.exs @@ -115,5 +115,3 @@ config :phoenix, :stacktrace_depth, 20 # Initialize plugs at runtime for faster development compilation config :phoenix, :plug_init_mode, :runtime - -config(:fz_http, :disable_signup, true) diff --git a/config/prod.exs b/config/prod.exs index 8e76fab25..95f56bacd 100644 --- a/config/prod.exs +++ b/config/prod.exs @@ -27,3 +27,6 @@ config :fz_http, FzHttpWeb.Endpoint, # Do not print debug messages in production config :logger, level: :info + +config :fz_http, + connectivity_check_url: "https://ping.firez.one/" diff --git a/config/releases.exs b/config/releases.exs index 0c95cdda0..f9ec911ef 100644 --- a/config/releases.exs +++ b/config/releases.exs @@ -3,7 +3,7 @@ # although such is generally not recommended and you have to # remember to add this file to your .gitignore. import Config -alias FzCommon.CLI +alias FzCommon.{CLI, FzInteger, FzString} # For releases, require that all these are set database_name = System.fetch_env!("DATABASE_NAME") @@ -16,13 +16,20 @@ url_host = System.fetch_env!("URL_HOST") admin_email = System.fetch_env!("ADMIN_EMAIL") default_admin_password = System.fetch_env!("DEFAULT_ADMIN_PASSWORD") wireguard_interface_name = System.fetch_env!("WIREGUARD_INTERFACE_NAME") -wireguard_endpoint = System.fetch_env!("WIREGUARD_ENDPOINT") wireguard_port = String.to_integer(System.fetch_env!("WIREGUARD_PORT")) nft_path = System.fetch_env!("NFT_PATH") wg_path = System.fetch_env!("WG_PATH") egress_interface = System.fetch_env!("EGRESS_INTERFACE") wireguard_public_key = System.fetch_env!("WIREGUARD_PUBLIC_KEY") +connectivity_checks_enabled = + FzString.to_boolean(System.fetch_env!("CONNECTIVITY_CHECKS_ENABLED")) + +connectivity_checks_interval = + System.fetch_env!("CONNECTIVITY_CHECKS_INTERVAL") + |> String.to_integer() + |> FzInteger.clamp(60, 86_400) + # secrets encryption_key = System.fetch_env!("DATABASE_ENCRYPTION_KEY") secret_key_base = System.fetch_env!("SECRET_KEY_BASE") @@ -32,9 +39,6 @@ cookie_signing_salt = System.fetch_env!("COOKIE_SIGNING_SALT") # Password is not needed if using bundled PostgreSQL, so use nil if it's not set. database_password = System.get_env("DATABASE_PASSWORD") -config :fz_http, - disable_signup: true - # Database configuration connect_opts = [ database: database_name, @@ -83,9 +87,10 @@ config :fz_wall, config :fz_vpn, wireguard_public_key: wireguard_public_key, wireguard_interface_name: wireguard_interface_name, - wireguard_port: wireguard_port, - wireguard_endpoint: wireguard_endpoint + wireguard_port: wireguard_port config :fz_http, + connectivity_checks_enabled: connectivity_checks_enabled, + connectivity_checks_interval: connectivity_checks_interval, admin_email: admin_email, default_admin_password: default_admin_password diff --git a/config/test.exs b/config/test.exs index 2c545edfb..0bbb05dfc 100644 --- a/config/test.exs +++ b/config/test.exs @@ -37,8 +37,12 @@ config :fz_http, FzHttpWeb.Endpoint, ], server: true -config :fz_http, :sql_sandbox, true -config :fz_http, :events_module, FzHttpWeb.MockEvents +config :fz_http, + supervision_tree_mode: :test, + connectivity_checks_interval: 86_400, + sql_sandbox: true, + events_module: FzHttpWeb.MockEvents, + http_client: FzHttp.MockHttpClient # Print only warnings and errors during test config :logger, level: :warn diff --git a/mix.exs b/mix.exs index 47952a5a7..6232d1a42 100644 --- a/mix.exs +++ b/mix.exs @@ -30,6 +30,9 @@ defmodule FirezoneUmbrella.MixProject do extras: ["README.md", "SECURITY.md", "CONTRIBUTING.md"] ], deps: deps(), + dialyzer: [ + plt_file: {:no_warn, "priv/plts/dialyzer.plt"} + ], aliases: aliases(), default_release: :firezone, releases: [ diff --git a/mix.lock b/mix.lock index cfcbe42f6..3fe938921 100644 --- a/mix.lock +++ b/mix.lock @@ -10,37 +10,38 @@ "cowboy": {:hex, :cowboy, "2.9.0", "865dd8b6607e14cf03282e10e934023a1bd8be6f6bacf921a7e2a96d800cd452", [:make, :rebar3], [{:cowlib, "2.11.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "1.8.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "2c729f934b4e1aa149aff882f57c6372c15399a20d54f65c8d67bef583021bde"}, "cowboy_telemetry": {:hex, :cowboy_telemetry, "0.3.1", "ebd1a1d7aff97f27c66654e78ece187abdc646992714164380d8a041eda16754", [:rebar3], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "3a6efd3366130eab84ca372cbd4a7d3c3a97bdfcfb4911233b035d117063f0af"}, "cowlib": {:hex, :cowlib, "2.11.0", "0b9ff9c346629256c42ebe1eeb769a83c6cb771a6ee5960bd110ab0b9b872063", [:make, :rebar3], [], "hexpm", "2b3e9da0b21c4565751a6d4901c20d1b4cc25cbb7fd50d91d2ab6dd287bc86a9"}, - "credo": {:hex, :credo, "1.5.6", "e04cc0fdc236fefbb578e0c04bd01a471081616e741d386909e527ac146016c6", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2.8", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "4b52a3e558bd64e30de62a648518a5ea2b6e3e5d2b164ef5296244753fc7eb17"}, - "db_connection": {:hex, :db_connection, "2.4.0", "d04b1b73795dae60cead94189f1b8a51cc9e1f911c234cc23074017c43c031e5", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "ad416c21ad9f61b3103d254a71b63696ecadb6a917b36f563921e0de00d7d7c8"}, + "credo": {:hex, :credo, "1.6.1", "7dc76dcdb764a4316c1596804c48eada9fff44bd4b733a91ccbf0c0f368be61e", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2.8", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "698607fb5993720c7e93d2d8e76f2175bba024de964e160e2f7151ef3ab82ac5"}, + "db_connection": {:hex, :db_connection, "2.4.1", "6411f6e23f1a8b68a82fa3a36366d4881f21f47fc79a9efb8c615e62050219da", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "ea36d226ec5999781a9a8ad64e5d8c4454ecedc7a4d643e4832bf08efca01f00"}, "decimal": {:hex, :decimal, "2.0.0", "a78296e617b0f5dd4c6caf57c714431347912ffb1d0842e998e9792b5642d697", [:mix], [], "hexpm", "34666e9c55dea81013e77d9d87370fe6cb6291d1ef32f46a1600230b1d44f577"}, "dialyxir": {:hex, :dialyxir, "1.1.0", "c5aab0d6e71e5522e77beff7ba9e08f8e02bad90dfbeffae60eaf0cb47e29488", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "07ea8e49c45f15264ebe6d5b93799d4dd56a44036cf42d0ad9c960bc266c0b9a"}, - "earmark_parser": {:hex, :earmark_parser, "1.4.15", "b29e8e729f4aa4a00436580dcc2c9c5c51890613457c193cc8525c388ccb2f06", [:mix], [], "hexpm", "044523d6438ea19c1b8ec877ec221b008661d3c27e3b848f4c879f500421ca5c"}, + "earmark_parser": {:hex, :earmark_parser, "1.4.17", "6f3c7e94170377ba45241d394389e800fb15adc5de51d0a3cd52ae766aafd63f", [:mix], [], "hexpm", "f93ac89c9feca61c165b264b5837bf82344d13bebc634cd575cb711e2e342023"}, "ecto": {:hex, :ecto, "3.7.1", "a20598862351b29f80f285b21ec5297da1181c0442687f9b8329f0445d228892", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "d36e5b39fc479e654cffd4dbe1865d9716e4a9b6311faff799b6f90ab81b8638"}, "ecto_network": {:hex, :ecto_network, "1.3.0", "1e77fa37c20e0f6a426d3862732f3317b0fa4c18f123d325f81752a491d7304e", [:mix], [{:ecto_sql, ">= 3.0.0", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:phoenix_html, ">= 0.0.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:postgrex, ">= 0.14.0", [hex: :postgrex, repo: "hexpm", optional: false]}], "hexpm", "053a5e46ef2837e8ea5ea97c82fa0f5494699209eddd764e663c85f11b2865bd"}, - "ecto_sql": {:hex, :ecto_sql, "3.7.0", "2fcaad4ab0c8d76a5afbef078162806adbe709c04160aca58400d5cbbe8eeac6", [:mix], [{:db_connection, "~> 2.2", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.7.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.4.0 or ~> 0.5.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.15.0 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "a26135dfa1d99bf87a928c464cfa25bba6535a4fe761eefa56077a4febc60f70"}, - "elixir_make": {:hex, :elixir_make, "0.6.2", "7dffacd77dec4c37b39af867cedaabb0b59f6a871f89722c25b28fcd4bd70530", [:mix], [], "hexpm", "03e49eadda22526a7e5279d53321d1cced6552f344ba4e03e619063de75348d9"}, + "ecto_sql": {:hex, :ecto_sql, "3.7.1", "8de624ef50b2a8540252d8c60506379fbbc2707be1606853df371cf53df5d053", [:mix], [{:db_connection, "~> 2.2", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.7.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.4.0 or ~> 0.5.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.15.0 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "2b42a32e2ce92f64aba5c88617891ab3b0ba34f3f3a503fa20009eae1a401c81"}, + "elixir_make": {:hex, :elixir_make, "0.6.3", "bc07d53221216838d79e03a8019d0839786703129599e9619f4ab74c8c096eac", [:mix], [], "hexpm", "f5cbd651c5678bcaabdbb7857658ee106b12509cd976c2c2fca99688e1daf716"}, "erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"}, - "ex_doc": {:hex, :ex_doc, "0.25.3", "3edf6a0d70a39d2eafde030b8895501b1c93692effcbd21347296c18e47618ce", [:mix], [{:earmark_parser, "~> 1.4.0", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "9ebebc2169ec732a38e9e779fd0418c9189b3ca93f4a676c961be6c1527913f5"}, - "excoveralls": {:hex, :excoveralls, "0.14.2", "f9f5fd0004d7bbeaa28ea9606251bb643c313c3d60710bad1f5809c845b748f0", [:mix], [{:hackney, "~> 1.16", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "ca6fd358621cb4d29311b29d4732c4d47dac70e622850979bc54ed9a3e50f3e1"}, + "ex_doc": {:hex, :ex_doc, "0.26.0", "1922164bac0b18b02f84d6f69cab1b93bc3e870e2ad18d5dacb50a9e06b542a3", [:mix], [{:earmark_parser, "~> 1.4.0", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "2775d66e494a9a48355db7867478ffd997864c61c65a47d31c4949459281c78d"}, + "excoveralls": {:hex, :excoveralls, "0.14.4", "295498f1ae47bdc6dce59af9a585c381e1aefc63298d48172efaaa90c3d251db", [:mix], [{:hackney, "~> 1.16", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "e3ab02f2df4c1c7a519728a6f0a747e71d7d6e846020aae338173619217931c1"}, "file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"}, - "floki": {:hex, :floki, "0.31.0", "f05ee8a8e6a3ced4e62beeb2c79a63bc8e12ab98fbaaf6e6a3d9b76b1278e23f", [:mix], [{:html_entities, "~> 0.5.0", [hex: :html_entities, repo: "hexpm", optional: false]}], "hexpm", "b05afa372f5c345a5bf240ac25ea1f0f3d5fcfd7490ac0beeb4a203f9444891e"}, + "floki": {:hex, :floki, "0.32.0", "f915dc15258bc997d49be1f5ef7d3992f8834d6f5695270acad17b41f5bcc8e2", [:mix], [{:html_entities, "~> 0.5.0", [hex: :html_entities, repo: "hexpm", optional: false]}], "hexpm", "1c5a91cae1fd8931c26a4826b5e2372c284813904c8bacb468b5de39c7ececbd"}, "gettext": {:hex, :gettext, "0.18.2", "7df3ea191bb56c0309c00a783334b288d08a879f53a7014341284635850a6e55", [:mix], [], "hexpm", "f9f537b13d4fdd30f3039d33cb80144c3aa1f8d9698e47d7bcbcc8df93b1f5c5"}, "hackney": {:hex, :hackney, "1.18.0", "c4443d960bb9fba6d01161d01cd81173089686717d9490e5d3606644c48d121f", [:rebar3], [{:certifi, "~>2.8.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~>6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~>1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.3.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~>1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "9afcda620704d720db8c6a3123e9848d09c87586dc1c10479c42627b905b5c5e"}, "html_entities": {:hex, :html_entities, "0.5.2", "9e47e70598da7de2a9ff6af8758399251db6dbb7eebe2b013f2bbd2515895c3c", [:mix], [], "hexpm", "c53ba390403485615623b9531e97696f076ed415e8d8058b1dbaa28181f4fdcc"}, + "httpoison": {:hex, :httpoison, "1.8.0", "6b85dea15820b7804ef607ff78406ab449dd78bed923a49c7160e1886e987a3d", [:mix], [{:hackney, "~> 1.17", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "28089eaa98cf90c66265b6b5ad87c59a3729bea2e74e9d08f9b51eb9729b3c3a"}, "idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"}, "inet_cidr": {:hex, :inet_cidr, "1.0.4", "a05744ab7c221ca8e395c926c3919a821eb512e8f36547c062f62c4ca0cf3d6e", [:mix], [], "hexpm", "64a2d30189704ae41ca7dbdd587f5291db5d1dda1414e0774c29ffc81088c1bc"}, "inflex": {:hex, :inflex, "2.1.0", "a365cf0821a9dacb65067abd95008ca1b0bb7dcdd85ae59965deef2aa062924c", [:mix], [], "hexpm", "14c17d05db4ee9b6d319b0bff1bdf22aa389a25398d1952c7a0b5f3d93162dd8"}, "jason": {:hex, :jason, "1.2.2", "ba43e3f2709fd1aa1dce90aaabfd039d000469c05c56f0b8e31978e03fa39052", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "18a228f5f0058ee183f29f9eae0805c6e59d61c3b006760668d8d18ff0d12179"}, "makeup": {:hex, :makeup, "1.0.5", "d5a830bc42c9800ce07dd97fa94669dfb93d3bf5fcf6ea7a0c67b2e0e4a7f26c", [:mix], [{:nimble_parsec, "~> 0.5 or ~> 1.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "cfa158c02d3f5c0c665d0af11512fed3fba0144cf1aadee0f2ce17747fba2ca9"}, - "makeup_elixir": {:hex, :makeup_elixir, "0.15.1", "b5888c880d17d1cc3e598f05cdb5b5a91b7b17ac4eaf5f297cb697663a1094dd", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.1", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "db68c173234b07ab2a07f645a5acdc117b9f99d69ebf521821d89690ae6c6ec8"}, + "makeup_elixir": {:hex, :makeup_elixir, "0.15.2", "dc72dfe17eb240552857465cc00cce390960d9a0c055c4ccd38b70629227e97c", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.1", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "fd23ae48d09b32eff49d4ced2b43c9f086d402ee4fd4fcb2d7fad97fa8823e75"}, "makeup_erlang": {:hex, :makeup_erlang, "0.1.1", "3fcb7f09eb9d98dc4d208f49cc955a34218fc41ff6b84df7c75b3e6e533cc65f", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "174d0809e98a4ef0b3309256cbf97101c6ec01c4ab0b23e926a9e17df2077cbb"}, "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"}, - "mime": {:hex, :mime, "2.0.1", "0de4c81303fe07806ebc2494d5321ce8fb4df106e34dd5f9d787b637ebadc256", [:mix], [], "hexpm", "7a86b920d2aedce5fb6280ac8261ac1a739ae6c1a1ad38f5eadf910063008942"}, + "mime": {:hex, :mime, "2.0.2", "0b9e1a4c840eafb68d820b0e2158ef5c49385d17fb36855ac6e7e087d4b1dcc5", [:mix], [], "hexpm", "e6a3f76b4c277739e36c2e21a2c640778ba4c3846189d5ab19f97f126df5f9b7"}, "mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"}, "mix_test_watch": {:hex, :mix_test_watch, "1.1.0", "330bb91c8ed271fe408c42d07e0773340a7938d8a0d281d57a14243eae9dc8c3", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}], "hexpm", "52b6b1c476cbb70fd899ca5394506482f12e5f6b0d6acff9df95c7f1e0812ec3"}, - "nimble_parsec": {:hex, :nimble_parsec, "1.1.0", "3a6fca1550363552e54c216debb6a9e95bd8d32348938e13de5eda962c0d7f89", [:mix], [], "hexpm", "08eb32d66b706e913ff748f11694b17981c0b04a33ef470e33e11b3d3ac8f54b"}, + "nimble_parsec": {:hex, :nimble_parsec, "1.2.0", "b44d75e2a6542dcb6acf5d71c32c74ca88960421b6874777f79153bbbbd7dccc", [:mix], [], "hexpm", "52b2871a7515a5ac49b00f214e4165a40724cf99798d8e4a65e4fd64ebd002c1"}, "parse_trans": {:hex, :parse_trans, "3.3.1", "16328ab840cc09919bd10dab29e431da3af9e9e7e7e6f0089dd5a2d2820011d8", [:rebar3], [], "hexpm", "07cd9577885f56362d414e8c4c4e6bdf10d43a8767abb92d24cbe8b24c54888b"}, - "phoenix": {:hex, :phoenix, "1.6.0", "7b85023f7ddef9a5c70909a51cc37c8b868b474d853f90f4280efd26b0e7cce5", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.0", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 1.0", [hex: :phoenix_view, repo: "hexpm", optional: false]}, {:plug, "~> 1.10", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.2", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "52ffdd31f2daeb399b2e1eb57d468f99a1ad6eee5d8ea19d2353492f06c9fc96"}, + "phoenix": {:hex, :phoenix, "1.6.2", "6cbd5c8ed7a797f25a919a37fafbc2fb1634c9cdb12a4448d7a5d0b26926f005", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.0", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 1.0", [hex: :phoenix_view, repo: "hexpm", optional: false]}, {:plug, "~> 1.10", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.2", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7bbee475acae0c3abc229b7f189e210ea788e63bd168e585f60c299a4b2f9133"}, "phoenix_ecto": {:hex, :phoenix_ecto, "4.4.0", "0672ed4e4808b3fbed494dded89958e22fb882de47a97634c0b13e7b0b5f7720", [:mix], [{:ecto, "~> 3.3", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "09864e558ed31ee00bd48fcc1d4fc58ae9678c9e81649075431e69dbabb43cc1"}, "phoenix_html": {:hex, :phoenix_html, "3.0.4", "232d41884fe6a9c42d09f48397c175cd6f0d443aaa34c7424da47604201df2e1", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "ce17fd3cf815b2ed874114073e743507704b1f5288bb03c304a77458485efc8b"}, "phoenix_live_reload": {:hex, :phoenix_live_reload, "1.3.3", "3a53772a6118d5679bf50fc1670505a290e32a1d195df9e069d8c53ab040c054", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm", "766796676e5f558dbae5d1bdb066849673e956005e3730dfd5affd7a6da4abac"}, @@ -50,7 +51,7 @@ "plug": {:hex, :plug, "1.12.1", "645678c800601d8d9f27ad1aebba1fdb9ce5b2623ddb961a074da0b96c35187d", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "d57e799a777bc20494b784966dc5fbda91eb4a09f571f76545b72a634ce0d30b"}, "plug_cowboy": {:hex, :plug_cowboy, "2.5.2", "62894ccd601cf9597e2c23911ff12798a8a18d237e9739f58a6b04e4988899fe", [:mix], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:cowboy_telemetry, "~> 0.3", [hex: :cowboy_telemetry, repo: "hexpm", optional: false]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "ea6e87f774c8608d60c8d34022a7d073bd7680a0a013f049fc62bf35efea1044"}, "plug_crypto": {:hex, :plug_crypto, "1.2.2", "05654514ac717ff3a1843204b424477d9e60c143406aa94daf2274fdd280794d", [:mix], [], "hexpm", "87631c7ad914a5a445f0a3809f99b079113ae4ed4b867348dd9eec288cecb6db"}, - "postgrex": {:hex, :postgrex, "0.15.11", "50abbb50f33d22d79af402e549b9a566ba4f0451b4f5fd39b72d9bbd49743d24", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "6f0e5c3ea10f97468f5ff852277cb207f068399eb68b0c06c142ef68a4e82952"}, + "postgrex": {:hex, :postgrex, "0.15.13", "7794e697481799aee8982688c261901de493eb64451feee6ea58207d7266d54a", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "3ffb76e1a97cfefe5c6a95632a27ffb67f28871c9741fb585f9d1c3cd2af70f1"}, "ranch": {:hex, :ranch, "1.8.0", "8c7a100a139fd57f17327b6413e4167ac559fbc04ca7448e9be9057311597a1d", [:make, :rebar3], [], "hexpm", "49fbcfd3682fab1f5d109351b61257676da1a2fdbe295904176d5e521a2ddfe5"}, "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.6", "cf344f5692c82d2cd7554f5ec8fd961548d4fd09e7d22f5b62482e5aeaebd4b0", [:make, :mix, :rebar3], [], "hexpm", "bdb0d2471f453c88ff3908e7686f86f9be327d065cc1ec16fa4540197ea04680"}, "telemetry": {:hex, :telemetry, "0.4.3", "a06428a514bdbc63293cd9a6263aad00ddeb66f608163bdec7c8995784080818", [:rebar3], [], "hexpm", "eb72b8365ffda5bed68a620d1da88525e326cb82a75ee61354fc24b844768041"}, diff --git a/omnibus/cookbooks/firezone/attributes/default.rb b/omnibus/cookbooks/firezone/attributes/default.rb index f4cd199ab..25f6c98b4 100644 --- a/omnibus/cookbooks/firezone/attributes/default.rb +++ b/omnibus/cookbooks/firezone/attributes/default.rb @@ -16,7 +16,7 @@ require 'etc' # # { "postgresql": { "enable": false } } # -# for example, it will set the node['firezone']['postgresql']['enable'] attribute to false. +# for example, it will set the node['firezone']['postgresql']['enabled'] attribute to false. # ## Top-level attributes # @@ -66,7 +66,7 @@ default['firezone']['sysvinit_id'] = 'SUP' # These attributes control Firezone-specific portions of the Nginx # configuration and the virtual host for the Firezone Phoenix app. -default['firezone']['nginx']['enable'] = true +default['firezone']['nginx']['enabled'] = true default['firezone']['nginx']['force_ssl'] = true default['firezone']['nginx']['non_ssl_port'] = 80 default['firezone']['nginx']['ssl_port'] = 443 @@ -80,7 +80,7 @@ default['firezone']['nginx']['log_x_forwarded_for'] = false default['firezone']['nginx']['redirect_to_canonical'] = false # Controls nginx caching, used to cache some endpoints -default['firezone']['nginx']['cache']['enable'] = false +default['firezone']['nginx']['cache']['enabled'] = false default['firezone']['nginx']['cache']['directory'] = "#{node['firezone']['var_directory']}/nginx/cache" # These attributes control the main nginx.conf, including the events and http @@ -142,7 +142,7 @@ default['firezone']['nginx']['default']['modules'] = [] # ### Use the bundled Postgres instance (default, recommended): # -default['firezone']['postgresql']['enable'] = true +default['firezone']['postgresql']['enabled'] = true default['firezone']['postgresql']['username'] = node['firezone']['user'] default['firezone']['postgresql']['data_directory'] = "#{node['firezone']['var_directory']}/postgresql/13.3/data" @@ -150,7 +150,7 @@ default['firezone']['postgresql']['data_directory'] = "#{node['firezone']['var_d # # Disable the provided Postgres instance and connect to your own: # -# default['firezone']['postgresql']['enable'] = false +# default['firezone']['postgresql']['enabled'] = false # default['firezone']['database']['user'] = 'my_db_user_name' # default['firezone']['database']['name'] = 'my_db_name'' # default['firezone']['database']['host'] = 'my.db.server.address' @@ -189,20 +189,20 @@ default['firezone']['database']['extensions'] = { 'plpgsql' => true, 'pg_trgm' = # default['firezone']['database']['password'] = 'change_me' # ## Phoenix -# + # ### The Phoenix web app for Firezone -default['firezone']['phoenix']['enable'] = true +default['firezone']['phoenix']['enabled'] = true default['firezone']['phoenix']['port'] = 13000 default['firezone']['phoenix']['log_directory'] = "#{node['firezone']['log_directory']}/phoenix" default['firezone']['phoenix']['log_rotation']['file_maxbytes'] = 104857600 default['firezone']['phoenix']['log_rotation']['num_to_keep'] = 10 # ## WireGuard -# + # ### Interface Management # Enable management of the WireGuard interface itself. Set this to false if you # want to manually create your WireGuard interface and manage its interface properties. -default['firezone']['wireguard']['enable'] = true +default['firezone']['wireguard']['enabled'] = true default['firezone']['wireguard']['log_directory'] = "#{node['firezone']['log_directory']}/wireguard" default['firezone']['wireguard']['log_rotation']['file_maxbytes'] = 104857600 default['firezone']['wireguard']['log_rotation']['num_to_keep'] = 10 @@ -214,13 +214,6 @@ default['firezone']['wireguard']['port'] = 51820 # WireGuard interface MTU default['firezone']['wireguard']['mtu'] = 1420 -# ### WireGuard endpoint -# IPv4, IPv6, or hostname that device configs will use to connect to this server. -# If left blank, this will be set to the IPv4 address of the default egress interface. -# Override this to your publicly routable IP if you're behind a NAT and need to -# set up port forwarding to your Firezone server. -default['firezone']['wireguard']['endpoint'] = nil - # ## Runit # This is missing from the enterprise cookbook @@ -246,9 +239,9 @@ default['firezone']['ssl']['ssl_dhparam'] = nil # These are used in creating a self-signed cert if you haven't brought your own. default['firezone']['ssl']['country_name'] = 'US' -default['firezone']['ssl']['state_name'] = 'WA' -default['firezone']['ssl']['locality_name'] = 'Seattle' -default['firezone']['ssl']['company_name'] = 'My Firezone' +default['firezone']['ssl']['state_name'] = 'CA' +default['firezone']['ssl']['locality_name'] = 'San Francisco' +default['firezone']['ssl']['company_name'] = 'My Company' default['firezone']['ssl']['organizational_unit_name'] = 'Operations' default['firezone']['ssl']['email_address'] = 'you@example.com' @@ -289,3 +282,18 @@ default['firezone']['smtp_address'] = nil default['firezone']['smtp_password'] = nil default['firezone']['smtp_port'] = nil default['firezone']['smtp_user_name'] = nil + +# ## Diagnostics Settings + +# ### Connectivity Checks +# +# By default, Firezone periodically checks for WAN connectivity to the Internet +# by issuing a POST request with an empty body to https://ping.firez.one. This +# is used to determine the server's publicly routable IP address for populating +# device configurations and setting up firewall rules. Set this to false to +# disable. +default['firezone']['connectivity_checks']['enabled'] = true + +# Amount of time to sleep between connectivity checks, in seconds. +# Default: 3600 (1 hour). Minimum: 60 (1 minute). Maximum: 86400 (1 day). +default['firezone']['connectivity_checks']['interval'] = 3_600 diff --git a/omnibus/cookbooks/firezone/libraries/config.rb b/omnibus/cookbooks/firezone/libraries/config.rb index ce968e5eb..1654d9183 100644 --- a/omnibus/cookbooks/firezone/libraries/config.rb +++ b/omnibus/cookbooks/firezone/libraries/config.rb @@ -221,6 +221,7 @@ class Firezone def self.app_env(attributes, reject = []) attributes = attributes.reject { |k| reject.include?(k) } + # NOTE: All these variables must be Strings env = { 'EGRESS_INTERFACE' => attributes['egress_interface'], 'WG_PATH' => "#{attributes['install_directory']}/embedded/bin/wg", @@ -235,9 +236,10 @@ class Firezone 'URL_HOST' => attributes['fqdn'], 'ADMIN_EMAIL' => attributes['admin_email'], 'WIREGUARD_INTERFACE_NAME' => attributes['wireguard']['interface_name'], - 'WIREGUARD_ENDPOINT' => attributes['wireguard']['endpoint'], 'WIREGUARD_PORT' => attributes['wireguard']['port'].to_s, 'WIREGUARD_PUBLIC_KEY' => attributes['wireguard_public_key'], + 'CONNECTIVITY_CHECKS_ENABLED' => attributes['connectivity_checks']['enabled'].to_s, + 'CONNECTIVITY_CHECKS_INTERVAL' => attributes['connectivity_checks']['interval'].to_s, # secrets 'SECRET_KEY_BASE' => attributes['secret_key_base'], diff --git a/omnibus/cookbooks/firezone/recipes/config.rb b/omnibus/cookbooks/firezone/recipes/config.rb index fba672ca1..fbe450183 100644 --- a/omnibus/cookbooks/firezone/recipes/config.rb +++ b/omnibus/cookbooks/firezone/recipes/config.rb @@ -1,9 +1,10 @@ -# +# frozen_string_literal: true + # Cookbook:: firezone # Recipe:: config # # Copyright:: 2014 Chef Software, Inc. -# Copyright:: 2021 Firezone +# Copyright:: 2021 Firezone, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/omnibus/cookbooks/firezone/recipes/nginx.rb b/omnibus/cookbooks/firezone/recipes/nginx.rb index 13905a3d6..1afe3142c 100644 --- a/omnibus/cookbooks/firezone/recipes/nginx.rb +++ b/omnibus/cookbooks/firezone/recipes/nginx.rb @@ -46,7 +46,7 @@ template 'nginx.conf' do variables(nginx: node['firezone']['nginx']) end -if node['firezone']['nginx']['enable'] +if node['firezone']['nginx']['enabled'] component_runit_service 'nginx' do package 'firezone' action :enable diff --git a/omnibus/cookbooks/firezone/recipes/phoenix.rb b/omnibus/cookbooks/firezone/recipes/phoenix.rb index a6fef2602..d39145459 100644 --- a/omnibus/cookbooks/firezone/recipes/phoenix.rb +++ b/omnibus/cookbooks/firezone/recipes/phoenix.rb @@ -49,7 +49,7 @@ template 'phoenix.nginx.conf' do app_directory: node['firezone']['app_directory']) end -if node['firezone']['phoenix']['enable'] +if node['firezone']['phoenix']['enabled'] component_runit_service 'phoenix' do package 'firezone' control ['t'] diff --git a/omnibus/cookbooks/firezone/recipes/postgresql.rb b/omnibus/cookbooks/firezone/recipes/postgresql.rb index 7882265ce..21be3c248 100644 --- a/omnibus/cookbooks/firezone/recipes/postgresql.rb +++ b/omnibus/cookbooks/firezone/recipes/postgresql.rb @@ -35,7 +35,7 @@ directory node['firezone']['postgresql']['log_directory'] do recursive true end -if node['firezone']['postgresql']['enable'] +if node['firezone']['postgresql']['enabled'] enterprise_pg_cluster 'firezone' do data_dir node['firezone']['postgresql']['data_directory'] encoding 'UTF8' diff --git a/omnibus/cookbooks/firezone/recipes/wireguard.rb b/omnibus/cookbooks/firezone/recipes/wireguard.rb index df7cb6039..9884989c1 100644 --- a/omnibus/cookbooks/firezone/recipes/wireguard.rb +++ b/omnibus/cookbooks/firezone/recipes/wireguard.rb @@ -16,7 +16,7 @@ directory node['firezone']['wireguard']['log_directory'] do recursive true end -if node['firezone']['wireguard']['enable'] +if node['firezone']['wireguard']['enabled'] component_runit_service 'wireguard' do package 'firezone' action :enable diff --git a/omnibus/cookbooks/firezone/templates/phoenix.nginx.conf.erb b/omnibus/cookbooks/firezone/templates/phoenix.nginx.conf.erb index 82c776e9a..405bce942 100644 --- a/omnibus/cookbooks/firezone/templates/phoenix.nginx.conf.erb +++ b/omnibus/cookbooks/firezone/templates/phoenix.nginx.conf.erb @@ -6,7 +6,7 @@ upstream phoenix { server 127.0.0.1:<%= @phoenix['port'] %>; } -<% if @nginx['cache']['enable'] -%> +<% if @nginx['cache']['enabled'] -%> proxy_cache_path <%= @nginx['cache']['directory'] %>/firezone levels=1:2 keys_zone=firezone-cache:512m max_size=1000m inactive=600m; proxy_temp_path <%= @nginx['cache']['directory'] %>/tmp; @@ -55,7 +55,7 @@ server { } <% end -%> -<% if @nginx['cache']['enable'] -%> +<% if @nginx['cache']['enabled'] -%> access_log <%= @nginx['log_directory'] %>/cache.log cache; <% end -%> @@ -76,7 +76,7 @@ server { return 410; } -<% if @nginx['cache']['enable'] -%> +<% if @nginx['cache']['enabled'] -%> # TODO: Is this useful to Firezone? location ~ ^/api/v1/cookbooks/.*/versions/.*(/download)?$ { proxy_set_header HOST $host;