mirror of
https://github.com/outbackdingo/firezone.git
synced 2026-01-27 18:18:55 +00:00
Send welcome emails (#2618)
And block colliding account slugs from being created. Closes #2599
This commit is contained in:
@@ -30,6 +30,16 @@ defmodule Domain.Accounts.Account.Changeset do
|
||||
|> validate_format(:slug, ~r/^[a-zA-Z0-9_]+$/,
|
||||
message: "can only contain letters, numbers, and underscores"
|
||||
)
|
||||
|> validate_exclusion(:slug, [
|
||||
"sign_up",
|
||||
"sign_in",
|
||||
"sign_out",
|
||||
"account",
|
||||
"admin",
|
||||
"system",
|
||||
"me",
|
||||
"you"
|
||||
])
|
||||
|> validate_change(:slug, fn field, slug ->
|
||||
if valid_uuid?(slug) do
|
||||
[{field, "cannot be a valid UUID"}]
|
||||
|
||||
@@ -428,7 +428,7 @@ defmodule Web.Auth do
|
||||
end)
|
||||
end
|
||||
|
||||
defp real_ip(socket) do
|
||||
def real_ip(socket) do
|
||||
peer_data = Phoenix.LiveView.get_connect_info(socket, :peer_data)
|
||||
x_headers = Phoenix.LiveView.get_connect_info(socket, :x_headers)
|
||||
|
||||
|
||||
@@ -31,6 +31,9 @@ defmodule Web.SignUp do
|
||||
end
|
||||
|
||||
def mount(_params, _session, socket) do
|
||||
user_agent = Phoenix.LiveView.get_connect_info(socket, :user_agent)
|
||||
real_ip = Web.Auth.real_ip(socket)
|
||||
|
||||
changeset =
|
||||
Registration.changeset(%Registration{}, %{
|
||||
account: %{slug: "placeholder"},
|
||||
@@ -41,6 +44,8 @@ defmodule Web.SignUp do
|
||||
assign(socket,
|
||||
form: to_form(changeset),
|
||||
account: nil,
|
||||
user_agent: user_agent,
|
||||
real_ip: real_ip,
|
||||
sign_up_enabled?: Config.sign_up_enabled?()
|
||||
)
|
||||
|
||||
@@ -96,7 +101,7 @@ defmodule Web.SignUp do
|
||||
~H"""
|
||||
<div class="space-y-6">
|
||||
<div class="text-center text-gray-900 dark:text-white">
|
||||
Your account has been created!
|
||||
Your account has been created! Please check your email for a sign in link.
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<div class="px-12">
|
||||
@@ -281,8 +286,18 @@ defmodule Web.SignUp do
|
||||
)
|
||||
|
||||
case Domain.Repo.transaction(multi) do
|
||||
{:ok, result} ->
|
||||
socket = assign(socket, account: result.account)
|
||||
{:ok, %{account: account, identity: identity}} ->
|
||||
{:ok, _} =
|
||||
Web.Mailer.AuthEmail.sign_up_link_email(
|
||||
account,
|
||||
identity,
|
||||
identity.provider_virtual_state.sign_in_token,
|
||||
socket.assigns.user_agent,
|
||||
socket.assigns.real_ip
|
||||
)
|
||||
|> Web.Mailer.deliver()
|
||||
|
||||
socket = assign(socket, account: account)
|
||||
{:noreply, socket}
|
||||
|
||||
{:error, :account, err_changeset, _effects_so_far} ->
|
||||
|
||||
@@ -6,6 +6,43 @@ defmodule Web.Mailer.AuthEmail do
|
||||
embed_templates "auth_email/*.html", suffix: "_html"
|
||||
embed_templates "auth_email/*.text", suffix: "_text"
|
||||
|
||||
def sign_up_link_email(
|
||||
%Domain.Accounts.Account{} = account,
|
||||
%Domain.Auth.Identity{} = identity,
|
||||
email_secret,
|
||||
user_agent,
|
||||
remote_ip
|
||||
) do
|
||||
params =
|
||||
%{
|
||||
identity_id: identity.id,
|
||||
secret: email_secret
|
||||
}
|
||||
|
||||
sign_in_url =
|
||||
url(
|
||||
~p"/#{account}/sign_in/providers/#{identity.provider_id}/verify_sign_in_token?#{params}"
|
||||
)
|
||||
|
||||
sign_in_form_url = url(~p"/#{account}")
|
||||
|
||||
default_email()
|
||||
|> subject("Welcome to Firezone")
|
||||
|> to(identity.provider_identifier)
|
||||
|> render_body(__MODULE__, :sign_up_link,
|
||||
account: account,
|
||||
sign_in_token_created_at:
|
||||
Cldr.DateTime.to_string!(identity.provider_state["sign_in_token_created_at"], Web.CLDR,
|
||||
format: :short
|
||||
) <> " UTC",
|
||||
secret: email_secret,
|
||||
sign_in_url: sign_in_url,
|
||||
sign_in_form_url: sign_in_form_url,
|
||||
user_agent: user_agent,
|
||||
remote_ip: "#{:inet.ntoa(remote_ip)}"
|
||||
)
|
||||
end
|
||||
|
||||
def sign_in_link_email(
|
||||
%Domain.Auth.Identity{} = identity,
|
||||
email_secret,
|
||||
@@ -19,7 +56,7 @@ defmodule Web.Mailer.AuthEmail do
|
||||
secret: email_secret
|
||||
})
|
||||
|
||||
sign_in_link =
|
||||
sign_in_url =
|
||||
url(
|
||||
~p"/#{identity.account}/sign_in/providers/#{identity.provider_id}/verify_sign_in_token?#{params}"
|
||||
)
|
||||
@@ -35,7 +72,7 @@ defmodule Web.Mailer.AuthEmail do
|
||||
format: :short
|
||||
) <> " UTC",
|
||||
secret: email_secret,
|
||||
link: sign_in_link,
|
||||
sign_in_url: sign_in_url,
|
||||
user_agent: user_agent,
|
||||
remote_ip: "#{:inet.ntoa(remote_ip)}"
|
||||
)
|
||||
|
||||
@@ -6,13 +6,13 @@
|
||||
|
||||
<div :if={is_nil(@client_platform)}>
|
||||
<p>
|
||||
Here is the <a href={@link} target="_blank">magic sign-in link</a>
|
||||
Here is the <a href={@sign_in_url} target="_blank">magic sign-in link</a>
|
||||
you requested to sign in to <b>"<%= @account.name %>"</b>.
|
||||
It is valid for 15 minutes.
|
||||
</p>
|
||||
|
||||
<small>
|
||||
If the link didn't work, please copy this link and open it in your browser. <%= @link %>
|
||||
If the link didn't work, please copy this link and open it in your browser: <%= @sign_in_url %>
|
||||
</small>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ Dear Firezone user,
|
||||
<%= if is_nil(@client_platform) do %>
|
||||
Here is the magic sign-in link you requested to sign in to "<%= @account.name %>":
|
||||
|
||||
<%= @link %>
|
||||
<%= @sign_in_url %>
|
||||
|
||||
Please copy this link and open it in your browser. It is valid for 15 minutes.
|
||||
<% else %>
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
<h3>Thank you for signing up for Firezone!</h3>
|
||||
|
||||
<div>
|
||||
<p>
|
||||
Here is the <a href={@sign_in_url} target="_blank">sign-in link</a>
|
||||
to access your account <b>"<%= @account.name %>"</b>.
|
||||
It is valid for 15 minutes.
|
||||
</p>
|
||||
|
||||
<small>
|
||||
If the link didn't work, please copy this link and open it in your browser. <%= @sign_in_url %>
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p>In future you can always access the sign in form at the following URL:</p>
|
||||
|
||||
<small>
|
||||
<%= @sign_in_form_url %>
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<p>
|
||||
If you did not request this action and have received this email in error, you can safely ignore
|
||||
and discard this email. However, if you continue to receive multiple unsolicited emails of this nature,
|
||||
we strongly recommend contacting your system administrator to report the issue.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<b>Request details:</b>
|
||||
<br /> Time: <%= @sign_in_token_created_at %>
|
||||
<br /> IP address: <%= @remote_ip %>
|
||||
<br /> User Agent: <%= @user_agent %>
|
||||
<br /> Account ID: <%= @account.id %>
|
||||
<br /> Account Slug: <%= @account.slug %>
|
||||
</p>
|
||||
@@ -0,0 +1,23 @@
|
||||
Thank you for signing up for Firezone!
|
||||
|
||||
|
||||
Here is the sign-in link to access your account "<%= @account.name %>":
|
||||
|
||||
<%= @sign_in_url %>
|
||||
|
||||
Please copy this link and open it in your browser. It is valid for 15 minutes.
|
||||
|
||||
In future you can always access the sign in form at the following URL:
|
||||
|
||||
<%= @sign_in_form_url %>
|
||||
|
||||
If you did not request this action and have received this email in error, you can safely ignore
|
||||
and discard this email. However, if you continue to receive multiple unsolicited emails of this nature,
|
||||
we strongly recommend contacting your system administrator to report the issue.
|
||||
|
||||
Request details:
|
||||
Time: <%= @sign_in_token_created_at %>
|
||||
IP address: <%= @remote_ip %>
|
||||
User Agent: <%= @user_agent %>
|
||||
Account ID: <%= @account.id %>
|
||||
Account Slug: <%= @account.slug %>
|
||||
@@ -1,7 +1,7 @@
|
||||
defmodule Web.Live.SignUpTest do
|
||||
use Web.ConnCase, async: true
|
||||
|
||||
test "renders signup form", %{conn: conn} do
|
||||
test "renders sign up form", %{conn: conn} do
|
||||
{:ok, lv, _html} = live(conn, ~p"/sign_up")
|
||||
|
||||
form = form(lv, "form")
|
||||
@@ -17,7 +17,7 @@ defmodule Web.Live.SignUpTest do
|
||||
]
|
||||
end
|
||||
|
||||
test "creates new account", %{conn: conn} do
|
||||
test "creates new account and sends a welcome email", %{conn: conn} do
|
||||
Domain.Config.put_system_env_override(:outbound_email_adapter, Swoosh.Adapters.Postmark)
|
||||
|
||||
account_name = "FooBar"
|
||||
@@ -37,6 +37,33 @@ defmodule Web.Live.SignUpTest do
|
||||
|
||||
assert html =~ "Your account has been created!"
|
||||
assert html =~ account_name
|
||||
|
||||
account = Repo.one(Domain.Accounts.Account)
|
||||
assert account.name == account_name
|
||||
|
||||
provider = Repo.one(Domain.Auth.Provider)
|
||||
assert provider.account_id == account.id
|
||||
|
||||
actor = Repo.one(Domain.Actors.Actor)
|
||||
assert actor.account_id == account.id
|
||||
assert actor.name == "John Doe"
|
||||
|
||||
identity = Repo.one(Domain.Auth.Identity)
|
||||
assert identity.account_id == account.id
|
||||
assert identity.provider_identifier == "jdoe@test.local"
|
||||
|
||||
assert_email_sent(fn email ->
|
||||
assert email.subject == "Welcome to Firezone"
|
||||
|
||||
verify_sign_in_token_path =
|
||||
~p"/#{account.id}/sign_in/providers/#{provider.id}/verify_sign_in_token"
|
||||
|
||||
assert email.text_body =~ "#{verify_sign_in_token_path}"
|
||||
assert email.text_body =~ "identity_id=#{identity.id}"
|
||||
assert email.text_body =~ "secret="
|
||||
|
||||
assert email.text_body =~ url(~p"/#{account.id}")
|
||||
end)
|
||||
end
|
||||
|
||||
test "renders changeset errors on input change", %{conn: conn} do
|
||||
@@ -79,7 +106,7 @@ defmodule Web.Live.SignUpTest do
|
||||
}
|
||||
end
|
||||
|
||||
test "renders signup disabled message", %{conn: conn} do
|
||||
test "renders sign up disabled message", %{conn: conn} do
|
||||
Domain.Config.feature_flag_override(:signups, false)
|
||||
|
||||
{:ok, _lv, html} = live(conn, ~p"/sign_up")
|
||||
|
||||
Reference in New Issue
Block a user