Improve forms (#3380)

* Make client-facing user changesets accept only editable fields

* Add controller test
This commit is contained in:
Adrian Gruntkowski 2023-09-28 11:44:39 +02:00 committed by GitHub
parent aee6f105c2
commit 777b4b3741
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 88 additions and 7 deletions

View File

@ -44,7 +44,7 @@ defmodule Plausible.Auth do
Ecto.Multi.new() Ecto.Multi.new()
|> Ecto.Multi.update( |> Ecto.Multi.update(
:user, :user,
Plausible.Auth.User.changeset(user, %{email_verified: true}) Ecto.Changeset.change(user, email_verified: true)
) )
|> Ecto.Multi.update_all( |> Ecto.Multi.update_all(
:codes, :codes,

View File

@ -26,7 +26,7 @@ defmodule Plausible.Auth.User do
field :name, :string field :name, :string
field :last_seen, :naive_datetime field :last_seen, :naive_datetime
field :trial_expiry_date, :date field :trial_expiry_date, :date
field :theme, :string field :theme, Ecto.Enum, values: [:system, :light, :dark]
field :email_verified, :boolean field :email_verified, :boolean
embeds_one :grace_period, Plausible.Auth.GracePeriod, on_replace: :update embeds_one :grace_period, Plausible.Auth.GracePeriod, on_replace: :update
@ -54,6 +54,13 @@ defmodule Plausible.Auth.User do
|> unique_constraint(:email) |> unique_constraint(:email)
end end
def settings_changeset(user, attrs \\ %{}) do
user
|> cast(attrs, [:email, :name, :theme])
|> validate_required([:email, :name, :theme])
|> unique_constraint(:email)
end
def changeset(user, attrs \\ %{}) do def changeset(user, attrs \\ %{}) do
user user
|> cast(attrs, [:email, :name, :email_verified, :theme, :trial_expiry_date]) |> cast(attrs, [:email, :name, :email_verified, :theme, :trial_expiry_date])

View File

@ -331,12 +331,12 @@ defmodule PlausibleWeb.AuthController do
end end
def user_settings(conn, _params) do def user_settings(conn, _params) do
changeset = Auth.User.changeset(conn.assigns[:current_user]) changeset = Auth.User.settings_changeset(conn.assigns[:current_user])
render_settings(conn, changeset) render_settings(conn, changeset)
end end
def save_settings(conn, %{"user" => user_params}) do def save_settings(conn, %{"user" => user_params}) do
changes = Auth.User.changeset(conn.assigns[:current_user], user_params) changes = Auth.User.settings_changeset(conn.assigns[:current_user], user_params)
case Repo.update(changes) do case Repo.update(changes) do
{:ok, _user} -> {:ok, _user} ->

View File

@ -27,9 +27,9 @@ defmodule PlausibleWeb.Live.RegisterForm do
else else
changeset = changeset =
if invitation = socket.assigns.invitation do if invitation = socket.assigns.invitation do
Auth.User.changeset(%Auth.User{email: invitation.email}) Auth.User.settings_changeset(%Auth.User{email: invitation.email})
else else
Auth.User.changeset(%Auth.User{}) Auth.User.settings_changeset(%Auth.User{})
end end
{:ok, {:ok,

View File

@ -19,7 +19,7 @@ defmodule PlausibleWeb.Live.ResetPasswordForm do
Repo.get_by!(Auth.User, email: email) Repo.get_by!(Auth.User, email: email)
end) end)
changeset = Auth.User.changeset(socket.assigns.user) changeset = Auth.User.settings_changeset(socket.assigns.user)
{:ok, {:ok,
assign(socket, assign(socket,

View File

@ -3,6 +3,68 @@ defmodule Plausible.Auth.UserTest do
alias Plausible.Auth.User alias Plausible.Auth.User
describe "settings_changeset/2" do
test "fails for empty input and user" do
changeset = User.settings_changeset(%User{}, %{})
refute changeset.valid?
end
test "succeeds for non-empty user even if input is empty" do
changeset =
User.settings_changeset(
%User{name: "Mary Jane", email: "mary@plausible.test", theme: "system"},
%{}
)
assert changeset.valid?
end
test "succeeds for valid user details input" do
changeset =
User.settings_changeset(
%User{name: "Mary Jane", email: "mary@plausible.test", theme: "system"},
%{name: "Tony Mangle", email: "tony@plausible.test"}
)
assert changeset.valid?
assert changeset.changes == %{name: "Tony Mangle", email: "tony@plausible.test"}
end
test "succeeds for valid theme input" do
changeset =
User.settings_changeset(
%User{name: "Mary Jane", email: "mary@plausible.test", theme: "system"},
%{theme: "dark"}
)
assert changeset.valid?
assert changeset.changes == %{theme: :dark}
end
test "fails on invalid user details input" do
changeset =
User.settings_changeset(
%User{name: "Mary Jane", email: "mary@plausible.test", theme: "system"},
%{name: ""}
)
refute changeset.valid?
assert changeset.errors[:name]
end
test "fails on invalid theme input" do
changeset =
User.settings_changeset(
%User{name: "Mary Jane", email: "mary@plausible.test", theme: "system"},
%{theme: "invalid"}
)
refute changeset.valid?
assert changeset.errors[:theme]
end
end
describe "password_strength/1" do describe "password_strength/1" do
test "scores password with all arguments in changes" do test "scores password with all arguments in changes" do
assert %{score: score, warning: warning, suggestions: suggestions} = assert %{score: score, warning: warning, suggestions: suggestions} =

View File

@ -546,6 +546,18 @@ defmodule PlausibleWeb.AuthControllerTest do
assert user.name == "New name" assert user.name == "New name"
end end
test "does not allow setting non-profile fields", %{conn: conn, user: user} do
expiry_date = user.trial_expiry_date
assert %Date{} = expiry_date
put(conn, "/settings", %{
"user" => %{"name" => "New name", "trial_expiry_date" => "2023-07-14"}
})
assert Repo.reload!(user).trial_expiry_date == expiry_date
end
test "redirects user to /settings", %{conn: conn} do test "redirects user to /settings", %{conn: conn} do
conn = put(conn, "/settings", %{"user" => %{"name" => "New name"}}) conn = put(conn, "/settings", %{"user" => %{"name" => "New name"}})