diff --git a/Makefile b/Makefile index b496919e72..d0e6124aac 100644 --- a/Makefile +++ b/Makefile @@ -93,6 +93,14 @@ sso-stop: docker stop idp docker remove idp +generate-corefile: + $(call require, integration_id) + integration_id=$(integration_id) envsubst < $(PWD)/extra/fixture/Corefile.template > $(PWD)/extra/fixture/Corefile.gen.$(integration_id) + +mock-dns: generate-corefile + $(call require, integration_id) + docker run --rm -p 5353:53/udp -v $(PWD)/extra/fixture/Corefile.gen.$(integration_id):/Corefile coredns/coredns:latest -conf Corefile + loadtest-server: @echo "Ensure your OTP installation is built with --enable-lock-counter" MIX_ENV=load ERL_FLAGS="-emu_type lcnt +Mdai max" iex -S mix do phx.digest + phx.server diff --git a/config/.env.dev b/config/.env.dev index 87cfab1031..a2122f288d 100644 --- a/config/.env.dev +++ b/config/.env.dev @@ -15,6 +15,7 @@ SHOW_CITIES=true PADDLE_VENDOR_AUTH_CODE=895e20d4efaec0575bb857f44b183217b332d9592e76e69b8a PADDLE_VENDOR_ID=3942 SSO_ENABLED=true +SSO_VERIFICATION_NAMESERVERS=0.0.0.0:5353 GOOGLE_CLIENT_ID=875387135161-l8tp53dpt7fdhdg9m1pc3vl42si95rh0.apps.googleusercontent.com GOOGLE_CLIENT_SECRET=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 diff --git a/config/runtime.exs b/config/runtime.exs index 6322db7532..bf937a7c06 100644 --- a/config/runtime.exs +++ b/config/runtime.exs @@ -312,6 +312,23 @@ sso_saml_adapter = "real" -> PlausibleWeb.SSO.RealSAMLAdapter end +sso_verification_nameservers = + case get_var_from_path_or_env(config_dir, "SSO_VERIFICATION_NAMESERVERS") do + nil -> + [] + + some -> + some + |> String.split(",") + |> Enum.map(fn addr -> + uri = URI.parse("dns://#{addr}") + host = uri.host + port = uri.port || 53 + {:ok, addr} = :inet.parse_address(to_charlist(host)) + {addr, port} + end) + end + config :plausible, environment: env, mailer_email: mailer_email, @@ -323,7 +340,8 @@ config :plausible, data_dir: data_dir, session_transfer_dir: session_transfer_dir, sso_enabled: sso_enabled, - sso_saml_adapter: sso_saml_adapter + sso_saml_adapter: sso_saml_adapter, + sso_verification_nameservers: sso_verification_nameservers config :plausible, :selfhost, enable_email_verification: enable_email_verification, diff --git a/extra/fixture/.gitignore b/extra/fixture/.gitignore new file mode 100644 index 0000000000..1d43f5366c --- /dev/null +++ b/extra/fixture/.gitignore @@ -0,0 +1 @@ +Corefile.gen.* diff --git a/extra/fixture/Corefile.template b/extra/fixture/Corefile.template new file mode 100644 index 0000000000..c7decfab01 --- /dev/null +++ b/extra/fixture/Corefile.template @@ -0,0 +1,9 @@ +. { + bind 0.0.0.0 + template IN TXT plausible.test { + answer "{{ .Name }} 60 IN TXT \"plausible-sso-verification=${integration_id}\"" + fallthrough + } + log + errors +} diff --git a/extra/lib/plausible/auth/sso.ex b/extra/lib/plausible/auth/sso.ex index 781caa6ea9..ecebc0a774 100644 --- a/extra/lib/plausible/auth/sso.ex +++ b/extra/lib/plausible/auth/sso.ex @@ -11,6 +11,8 @@ defmodule Plausible.Auth.SSO do alias Plausible.Repo alias Plausible.Teams + use Plausible.Auth.SSO.Domain.Status + @type policy_attr() :: {:sso_default_role, Teams.Policy.sso_member_role()} | {:sso_session_timeout_minutes, non_neg_integer()} @@ -184,15 +186,23 @@ defmodule Plausible.Auth.SSO do case {check, force_deprovision?} do {:ok, _} -> - Repo.delete!(integration) + {:ok, :ok} = + Repo.transaction(fn -> + integration = Repo.preload(integration, :sso_domains) + Enum.each(integration.sso_domains, &SSO.Domains.cancel_verification(&1.domain)) + Repo.delete!(integration) + :ok + end) + :ok {{:error, :sso_users_present}, true} -> - users = Repo.preload(integration, :users).users - {:ok, :ok} = Repo.transaction(fn -> + users = Repo.preload(integration, :users).users + integration = Repo.preload(integration, :sso_domains) Enum.each(users, &deprovision_user!/1) + Enum.each(integration.sso_domains, &SSO.Domains.cancel_verification(&1.domain)) Repo.delete!(integration) :ok end) @@ -216,7 +226,7 @@ defmodule Plausible.Auth.SSO do ) domains = Enum.flat_map(integrations, & &1.sso_domains) - no_verified_domains? = Enum.all?(domains, &(&1.status != :verified)) + no_verified_domains? = Enum.all?(domains, &(&1.status != Status.verified())) cond do integrations == [] -> {:error, :no_integration} diff --git a/extra/lib/plausible/auth/sso/domain.ex b/extra/lib/plausible/auth/sso/domain.ex index 938ceb15dc..cf4c722e38 100644 --- a/extra/lib/plausible/auth/sso/domain.ex +++ b/extra/lib/plausible/auth/sso/domain.ex @@ -25,6 +25,8 @@ defmodule Plausible.Auth.SSO.Domain do @spec verification_methods() :: list(verification_method()) def verification_methods(), do: @verification_methods + use Plausible.Auth.SSO.Domain.Status + schema "sso_domains" do field :identifier, Ecto.UUID field :domain, :string @@ -32,8 +34,8 @@ defmodule Plausible.Auth.SSO.Domain do field :last_verified_at, :naive_datetime field :status, Ecto.Enum, - values: [:pending, :in_progress, :verified, :unverified], - default: :pending + values: Status.all(), + default: Status.pending() belongs_to :sso_integration, Plausible.Auth.SSO.Integration @@ -59,11 +61,11 @@ defmodule Plausible.Auth.SSO.Domain do |> change() |> put_change(:verified_via, method) |> put_change(:last_verified_at, now) - |> put_change(:status, :verified) + |> put_change(:status, Status.verified()) end @spec unverified_changeset(t(), NaiveDateTime.t(), atom()) :: Ecto.Changeset.t() - def unverified_changeset(sso_domain, now, status \\ :in_progress) do + def unverified_changeset(sso_domain, now, status \\ Status.in_progress()) do sso_domain |> change() |> put_change(:verified_via, nil) diff --git a/extra/lib/plausible/auth/sso/domain/status.ex b/extra/lib/plausible/auth/sso/domain/status.ex new file mode 100644 index 0000000000..730417f37a --- /dev/null +++ b/extra/lib/plausible/auth/sso/domain/status.ex @@ -0,0 +1,21 @@ +defmodule Plausible.Auth.SSO.Domain.Status do + @moduledoc false + + defmacro __using__(opts) do + as = Keyword.get(opts, :as, Status) + + quote do + require Plausible.Auth.SSO.Domain.Status + alias Plausible.Auth.SSO.Domain.Status, as: unquote(as) + end + end + + defmacro pending(), do: :pending + defmacro in_progress(), do: :in_progress + defmacro verified(), do: :verified + defmacro unverified(), do: :unverified + + defmacro all() do + [pending(), in_progress(), verified(), unverified()] + end +end diff --git a/extra/lib/plausible/auth/sso/domain/verification/worker.ex b/extra/lib/plausible/auth/sso/domain/verification/worker.ex index 68a1027355..d50b275b34 100644 --- a/extra/lib/plausible/auth/sso/domain/verification/worker.ex +++ b/extra/lib/plausible/auth/sso/domain/verification/worker.ex @@ -9,29 +9,33 @@ defmodule Plausible.Auth.SSO.Domain.Verification.Worker do queue: :sso_domain_ownership_verification, unique: true + use Plausible.Auth.SSO.Domain.Status + alias Plausible.Auth.SSO alias Plausible.Repo # roughly around 34h, given the snooze back-off @max_snoozes 14 + @spec cancel(String.t()) :: :ok + def cancel(domain) do + {:ok, job} = + %{domain: domain} + |> new() + |> Oban.insert() + + Oban.cancel_job(job) + end + + @spec enqueue(String.t()) :: {:ok, Oban.Job.t()} def enqueue(domain) do - {:ok, result} = - Repo.transaction(fn -> - with {:ok, sso_domain} <- SSO.Domains.get(domain) do - SSO.Domains.mark_unverified!(sso_domain, :in_progress) - end + {:ok, job} = + %{domain: domain} + |> new() + |> Oban.insert() - {:ok, job} = - %{domain: domain} - |> new() - |> Oban.insert() - - :ok = Oban.retry_job(job) - {:ok, job} - end) - - result + :ok = Oban.retry_job(job) + {:ok, job} end @impl true @@ -42,7 +46,10 @@ defmodule Plausible.Auth.SSO.Domain.Verification.Worker do }) when attempt <= @max_snoozes do service_opts = [ - skip_checks?: meta["skip_checks"] == true + skip_checks?: meta["skip_checks"] == true, + verification_opts: [ + nameservers: Application.get_env(:plausible, :sso_verification_nameservers) || [] + ] ] service_opts = @@ -55,7 +62,7 @@ defmodule Plausible.Auth.SSO.Domain.Verification.Worker do case SSO.Domains.get(domain) do {:ok, sso_domain} -> case SSO.Domains.verify(sso_domain, service_opts) do - %SSO.Domain{status: :verified} = verified -> + %SSO.Domain{status: Status.verified()} = verified -> verification_complete(sso_domain) {:ok, verified} @@ -82,7 +89,7 @@ defmodule Plausible.Auth.SSO.Domain.Verification.Worker do defp verification_failure(domain) do with {:ok, sso_domain} <- SSO.Domains.get(domain) do sso_domain - |> SSO.Domains.mark_unverified!(:unverified) + |> SSO.Domains.mark_unverified!(Status.unverified()) |> send_failure_notification() end diff --git a/extra/lib/plausible/auth/sso/domains.ex b/extra/lib/plausible/auth/sso/domains.ex index ad47e7a05e..f30a3cd785 100644 --- a/extra/lib/plausible/auth/sso/domains.ex +++ b/extra/lib/plausible/auth/sso/domains.ex @@ -7,8 +7,11 @@ defmodule Plausible.Auth.SSO.Domains do alias Plausible.Auth alias Plausible.Auth.SSO + alias Plausible.Auth.SSO.Domain.Verification alias Plausible.Repo + use Plausible.Auth.SSO.Domain.Status + @spec add(SSO.Integration.t(), String.t()) :: {:ok, SSO.Domain.t()} | {:error, Ecto.Changeset.t()} def add(integration, domain) do @@ -17,6 +20,34 @@ defmodule Plausible.Auth.SSO.Domains do Repo.insert(changeset) end + @spec start_verification(String.t()) :: SSO.Domain.t() + def start_verification(domain) when is_binary(domain) do + {:ok, result} = + Repo.transaction(fn -> + with {:ok, sso_domain} <- get(domain) do + sso_domain = mark_unverified!(sso_domain, Status.in_progress()) + {:ok, _} = Verification.Worker.enqueue(domain) + {:ok, sso_domain} + end + end) + + result + end + + @spec cancel_verification(String.t()) :: :ok + def cancel_verification(domain) when is_binary(domain) do + {:ok, :ok} = + Repo.transaction(fn -> + with {:ok, sso_domain} <- get(domain) do + mark_unverified!(sso_domain, Status.unverified()) + end + + :ok = Verification.Worker.cancel(domain) + end) + + :ok + end + @spec verify(SSO.Domain.t(), Keyword.t()) :: SSO.Domain.t() def verify(%SSO.Domain{} = sso_domain, opts \\ []) do skip_checks? = Keyword.get(opts, :skip_checks?, false) @@ -69,7 +100,7 @@ defmodule Plausible.Auth.SSO.Domains do inner_join: i in assoc(d, :sso_integration), inner_join: t in assoc(i, :team), where: d.domain == ^search, - where: d.status == :verified, + where: d.status == ^Status.verified(), preload: [sso_integration: {i, team: t}] ) |> Repo.one() @@ -90,17 +121,21 @@ defmodule Plausible.Auth.SSO.Domains do case {check, force_deprovision?} do {:ok, _} -> - Repo.delete!(sso_domain) + {:ok, :ok} = + Repo.transaction(fn -> + Repo.delete!(sso_domain) + :ok = cancel_verification(sso_domain.domain) + end) + :ok {{:error, :sso_users_present}, true} -> - domain_users = users_by_domain(sso_domain) - {:ok, :ok} = Repo.transaction(fn -> + domain_users = users_by_domain(sso_domain) Enum.each(domain_users, &SSO.deprovision_user!/1) Repo.delete!(sso_domain) - :ok + cancel_verification(sso_domain.domain) end) :ok diff --git a/extra/lib/plausible_web/live/sso_management.ex b/extra/lib/plausible_web/live/sso_management.ex index 89b280d214..b47d1c6248 100644 --- a/extra/lib/plausible_web/live/sso_management.ex +++ b/extra/lib/plausible_web/live/sso_management.ex @@ -8,13 +8,16 @@ defmodule PlausibleWeb.Live.SSOManagement do alias Plausible.Teams alias PlausibleWeb.Router.Helpers, as: Routes + use Plausible.Auth.SSO.Domain.Status - @fake_verify_interval :timer.seconds(10) + @refresh_integration_interval :timer.seconds(5) def mount(_params, _session, socket) do socket = load_integration(socket, socket.assigns.current_team) - Process.send_after(self(), :fake_domain_verify, @fake_verify_interval) + if connected?(socket) do + Process.send_after(self(), :refresh_integration, @refresh_integration_interval) + end {:ok, route_mode(socket)} end @@ -172,7 +175,7 @@ defmodule PlausibleWeb.Live.SSOManagement do

Verifying domain {@domain.domain}

-

You can verify the domain using one of 3 methods:

+

You can verify ownership of the domain using one of 3 methods:

-
- <.button type="submit">Continue + <.notice> + We'll keep checking your domain ownership. Once any of the above verification methods succeeds, we'll send you an e-mail. Thank you for your patience. + + + + <.input type="hidden" name="identifier" value={@domain.identifier} /> + <.button + :if={@domain.status in [Status.in_progress(), Status.unverified(), Status.verified()]} + type="submit" + > + Run verification now + + + <.button :if={@domain.status == Status.pending()} type="submit">Continue
""" @@ -296,16 +311,31 @@ defmodule PlausibleWeb.Live.SSOManagement do <.table rows={@integration.sso_domains}> <:thead> <.th>Domain - <.th>Added at + <.th hide_on_mobile>Added at <.th>Status <.th invisible>Actions <:tbody :let={domain}> <.td>{domain.domain} - <.td>{Calendar.strftime(domain.inserted_at, "%b %-d, %Y at %H:%m UTC")} - <.td>{domain.status} + <.td hide_on_mobile> + {Calendar.strftime(domain.inserted_at, "%b %-d, %Y at %H:%m UTC")} + + <.td :if={domain.status != Status.in_progress()}>{domain.status} + <.td :if={domain.status == Status.in_progress()}> +
+ <.spinner class="w-4 h-4" /> + <.styled_link + id={"cancel-verify-domain-#{domain.identifier}"} + phx-click="cancel-verify-domain" + phx-value-identifier={domain.identifier} + > + Cancel + +
+ <.td actions> <.styled_link + :if={domain.status not in [Status.in_progress(), Status.verified()]} id={"verify-domain-#{domain.identifier}"} phx-click="verify-domain" phx-value-identifier={domain.identifier} @@ -458,8 +488,16 @@ defmodule PlausibleWeb.Live.SSOManagement do {:noreply, socket} end - def handle_event("show-manage", _params, socket) do - {:noreply, route_mode(socket, :manage)} + def handle_event("verify-domain-submit", params, socket) do + integration = socket.assigns.integration + sso_domain = Enum.find(integration.sso_domains, &(&1.identifier == params["identifier"])) + + if sso_domain do + SSO.Domains.start_verification(sso_domain.domain) + {:noreply, route_mode(load_integration(socket, socket.assigns.current_team), :manage)} + else + {:noreply, socket} + end end def handle_event("show-domain-setup", _params, socket) do @@ -483,6 +521,21 @@ defmodule PlausibleWeb.Live.SSOManagement do end end + def handle_event("cancel-verify-domain", params, socket) do + integration = socket.assigns.integration + domain = Enum.find(integration.sso_domains, &(&1.identifier == params["identifier"])) + + socket = + if domain do + :ok = SSO.Domains.cancel_verification(domain.domain) + load_integration(socket, socket.assigns.current_team) + else + socket + end + + {:noreply, socket} + end + def handle_event("remove-domain", params, socket) do integration = socket.assigns.integration domain = Enum.find(integration.sso_domains, &(&1.identifier == params["identifier"])) @@ -551,29 +604,9 @@ defmodule PlausibleWeb.Live.SSOManagement do {:noreply, socket} end - def handle_info(:fake_domain_verify, %{assigns: %{integration: integration}} = socket) - when not is_nil(integration) do - sso_domains = - integration.sso_domains - |> Enum.map(fn domain -> - if domain.status == :pending do - SSO.Domains.verify(domain, skip_checks?: true) - else - domain - end - end) - - Process.send_after(self(), :fake_domain_verify, @fake_verify_interval) - - integration = %{integration | sso_domains: sso_domains} - - {:noreply, assign(socket, :integration, integration)} - end - - def handle_info(:fake_domain_verify, socket) do - Process.send_after(self(), :fake_domain_verify, @fake_verify_interval) - - {:noreply, socket} + def handle_info(:refresh_integration, socket) do + Process.send_after(self(), :refresh_integration, @refresh_integration_interval) + {:noreply, load_integration(socket, socket.assigns.current_team)} end defp load_integration(socket, team) do diff --git a/test/plausible/auth/sso/domain/validation_test.exs b/test/plausible/auth/sso/domain/verification_test.exs similarity index 100% rename from test/plausible/auth/sso/domain/validation_test.exs rename to test/plausible/auth/sso/domain/verification_test.exs diff --git a/test/plausible/auth/sso/domains_test.exs b/test/plausible/auth/sso/domains_test.exs index c2332f307f..e3501ff4f8 100644 --- a/test/plausible/auth/sso/domains_test.exs +++ b/test/plausible/auth/sso/domains_test.exs @@ -6,6 +6,8 @@ defmodule Plausible.Auth.SSO.DomainsTest do on_ee do use Plausible.Teams.Test + use Plausible.Auth.SSO.Domain.Status + use Oban.Testing, repo: Plausible.Repo alias Plausible.Auth.SSO alias Plausible.Teams @@ -29,7 +31,7 @@ defmodule Plausible.Auth.SSO.DomainsTest do assert is_binary(sso_domain.identifier) refute sso_domain.verified_via refute sso_domain.last_verified_at - assert sso_domain.status == :pending + assert sso_domain.status == Status.pending() end test "normalizes domain before adding", %{integration: integration} do @@ -110,7 +112,7 @@ defmodule Plausible.Auth.SSO.DomainsTest do assert verified_domain.id == sso_domain.id assert verified_domain.verified_via == :dns_txt - assert verified_domain.status == :verified + assert verified_domain.status == Status.verified() assert verified_domain.last_verified_at end @@ -124,11 +126,49 @@ defmodule Plausible.Auth.SSO.DomainsTest do assert unverified_domain.id == sso_domain.id refute unverified_domain.verified_via - assert unverified_domain.status == :in_progress + assert unverified_domain.status == Status.in_progress() assert unverified_domain.last_verified_at end end + describe "start_verification/1" do + test "no domain" do + assert {:error, :not_found} = SSO.Domains.start_verification("example.com") + end + + test "sets domain status to in progress", %{integration: integration} do + domain = generate_domain() + {:ok, _} = SSO.Domains.add(integration, domain) + assert {:ok, sso_domain} = SSO.Domains.start_verification(domain) + assert sso_domain.status == Status.in_progress() + end + + test "enqueues background work", %{integration: integration} do + domain = generate_domain() + {:ok, _} = SSO.Domains.add(integration, domain) + assert {:ok, _} = SSO.Domains.start_verification(domain) + + assert_enqueued( + worker: Plausible.Auth.SSO.Domain.Verification.Worker, + args: %{domain: domain} + ) + end + end + + describe "cancel_verification/1" do + test "no domain" do + assert :ok = SSO.Domains.cancel_verification("example.com") + end + + test "sets domain status to unverified", %{integration: integration} do + domain = generate_domain() + {:ok, _} = SSO.Domains.add(integration, domain) + assert {:ok, sso_domain} = SSO.Domains.start_verification(domain) + assert :ok = SSO.Domains.cancel_verification(domain) + assert Repo.reload!(sso_domain).status == Status.unverified() + end + end + describe "lookup/1" do test "looks up domain by email", %{integration: integration} do domain = generate_domain() diff --git a/test/plausible/auth/sso_test.exs b/test/plausible/auth/sso_test.exs index 76d676ef7e..813aabaf82 100644 --- a/test/plausible/auth/sso_test.exs +++ b/test/plausible/auth/sso_test.exs @@ -3,6 +3,7 @@ defmodule Plausible.Auth.SSOTest do use Plausible on_ee do + use Oban.Testing, repo: Plausible.Repo use Plausible.Teams.Test alias Plausible.Auth @@ -746,6 +747,50 @@ defmodule Plausible.Auth.SSOTest do refute sso_user.sso_identity_id refute sso_user.sso_integration_id end + + test "cancels verification jobs for all domains when integration is removed" do + team = new_site().team + + integration = SSO.initiate_saml_integration(team) + domain1 = "example-#{Enum.random(1..10_000)}.com" + domain2 = "test-#{Enum.random(1..10_000)}.com" + + {:ok, _} = SSO.Domains.add(integration, domain1) + {:ok, _} = SSO.Domains.add(integration, domain2) + + {:ok, _} = SSO.Domains.start_verification(domain1) + {:ok, _} = SSO.Domains.start_verification(domain2) + + assert_enqueued(worker: SSO.Domain.Verification.Worker, args: %{domain: domain1}) + assert_enqueued(worker: SSO.Domain.Verification.Worker, args: %{domain: domain2}) + + assert :ok = SSO.remove_integration(integration) + + refute Repo.reload(integration) + refute_enqueued(worker: SSO.Domain.Verification.Worker, args: %{domain: domain1}) + refute_enqueued(worker: SSO.Domain.Verification.Worker, args: %{domain: domain2}) + end + + test "cancels verification jobs when integration is force removed with SSO users" do + team = new_site().team + + integration = SSO.initiate_saml_integration(team) + domain = "example-#{Enum.random(1..10_000)}.com" + + {:ok, sso_domain} = SSO.Domains.add(integration, domain) + SSO.Domains.verify(sso_domain, skip_checks?: true) + + identity = new_identity("Test User", "test@" <> domain) + {:ok, _, _, _} = SSO.provision_user(identity) + + {:ok, _} = SSO.Domains.start_verification(domain) + assert_enqueued(worker: SSO.Domain.Verification.Worker, args: %{domain: domain}) + + assert :ok = SSO.remove_integration(integration, force_deprovision?: true) + + refute Repo.reload(integration) + refute_enqueued(worker: SSO.Domain.Verification.Worker, args: %{domain: domain}) + end end defp new_identity(name, email, id \\ Ecto.UUID.generate()) do diff --git a/test/plausible_web/live/sso_management_test.exs b/test/plausible_web/live/sso_management_test.exs index f2ea62906c..688aa4859c 100644 --- a/test/plausible_web/live/sso_management_test.exs +++ b/test/plausible_web/live/sso_management_test.exs @@ -1,5 +1,6 @@ defmodule PlausibleWeb.Live.SSOMangementTest do use PlausibleWeb.ConnCase, async: false + use Oban.Testing, repo: Plausible.Repo @moduletag :ee_only @@ -108,7 +109,12 @@ defmodule PlausibleWeb.Live.SSOMangementTest do text = render(lv) |> text() assert text =~ "Verifying domain" - lv |> element("form#show-manage") |> render_submit() + lv |> element("form#verify-domain-submit") |> render_submit() + + assert_enqueued( + worker: SSO.Domain.Verification.Worker, + args: %{domain: "example.com"} + ) html = render(lv) text = text(html) @@ -185,7 +191,12 @@ defmodule PlausibleWeb.Live.SSOMangementTest do html = render(lv) assert text(html) =~ "Verifying domain new.example.com" - lv |> element("form#show-manage") |> render_submit() + lv |> element("form#verify-domain-submit") |> render_submit() + + assert_enqueued( + worker: SSO.Domain.Verification.Worker, + args: %{domain: "new.example.com"} + ) text = text(render(lv)) assert text =~ "org.example.com" diff --git a/test/workers/sso_domain_verification_worker_test.exs b/test/workers/sso_domain_verification_worker_test.exs index 70daad72e2..6ecbfa0e91 100644 --- a/test/workers/sso_domain_verification_worker_test.exs +++ b/test/workers/sso_domain_verification_worker_test.exs @@ -3,15 +3,13 @@ defmodule Plausible.Auth.SSO.Domain.Verification.WorkerTest do use Plausible on_ee do - use Oban.Testing, repo: Plausible.Repo - use Bamboo.Test, shared: true - - alias Plausible.Auth.SSO.Domain.Verification.Worker + use Oban.Testing, repo: Plausible.Repo + use Plausible.Auth.SSO.Domain.Status + use Plausible.Teams.Test alias Plausible.Auth.SSO - - use Plausible.Teams.Test + alias Plausible.Auth.SSO.Domain.Verification.Worker test "no sso domain cancels the job" do assert {:cancel, :domain_not_found} = @@ -29,6 +27,13 @@ defmodule Plausible.Auth.SSO.Domain.Verification.WorkerTest do assert_enqueued(worker: Worker, args: %{domain: "example.com"}) end + test "enqueue then cancel" do + {:ok, _} = Worker.enqueue("example.com") + assert_enqueued(worker: Worker, args: %{domain: "example.com"}) + :ok = Worker.cancel("example.com") + refute_enqueued(worker: Worker, args: %{domain: "example.com"}) + end + describe "integration set up" do setup do owner = new_user() @@ -45,19 +50,13 @@ defmodule Plausible.Auth.SSO.Domain.Verification.WorkerTest do sso_domain: sso_domain} end - test "enqueue resets domain status", %{sso_domain: sso_domain} do - %{status: :unverified} = SSO.Domains.mark_unverified!(sso_domain, :unverified) - {:ok, _} = Worker.enqueue(sso_domain.domain) - assert Plausible.Repo.reload!(sso_domain).status == :in_progress - end - test "domain is marked as in progress and job is snoozed", %{domain: domain} do - assert {:ok, %{status: :pending}} = SSO.Domains.get(domain) + assert {:ok, %{status: Status.pending()}} = SSO.Domains.get(domain) assert {:snooze, 15} = perform_job(Worker, %{"domain" => domain}, meta: %{bypass_checks: true}) - assert {:ok, %{status: :in_progress}} = SSO.Domains.get(domain) + assert {:ok, %{status: Status.in_progress()}} = SSO.Domains.get(domain) assert {:snooze, 7680} = perform_job(Worker, %{"domain" => domain}, @@ -65,7 +64,7 @@ defmodule Plausible.Auth.SSO.Domain.Verification.WorkerTest do meta: %{bypass_checks: true} ) - assert {:ok, %{status: :in_progress}} = SSO.Domains.get(domain) + assert {:ok, %{status: Status.in_progress()}} = SSO.Domains.get(domain) end test "domain is marked as verified and emails are sent", %{ @@ -75,7 +74,7 @@ defmodule Plausible.Auth.SSO.Domain.Verification.WorkerTest do } do owner2 = add_member(team, role: :owner) - assert {:ok, %{status: :verified}} = + assert {:ok, %{status: Status.verified()}} = perform_job(Worker, %{"domain" => domain}, meta: %{skip_checks: true}) assert_email_delivered_with(