feat: prevent join requests with equal mail
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
Simon 2026-03-13 11:18:34 +01:00
parent 40a4461d23
commit 086ecdcb1b
Signed by: simon
GPG key ID: 40E7A58C4AA1EDB2
22 changed files with 534 additions and 11 deletions

View file

@ -67,6 +67,18 @@ defmodule Mv.Membership.JoinRequestApprovalDomainTest do
end
end
describe "reviewed_by_display" do
test "get_join_request returns reviewed_by_display so UI can show reviewer without loading User" do
request = Fixtures.submitted_join_request_fixture()
reviewer = Fixtures.user_with_role_fixture("normal_user")
assert {:ok, _} = Membership.approve_join_request(request.id, actor: reviewer)
assert {:ok, loaded} = Membership.get_join_request(request.id, actor: reviewer)
assert loaded.reviewed_by_display == to_string(reviewer.email)
end
end
describe "reject_join_request/2" do
test "reject does not create a member" do
request = Fixtures.submitted_join_request_fixture()

View file

@ -49,6 +49,7 @@ defmodule Mv.Membership.JoinRequestApprovalPolicyTest do
assert approved.status == :approved
assert approved.approved_at != nil
assert approved.reviewed_by_user_id == user.id
assert approved.reviewed_by_display == to_string(user.email)
end
test "admin can approve a submitted join request", %{request: request} do
@ -89,6 +90,7 @@ defmodule Mv.Membership.JoinRequestApprovalPolicyTest do
assert rejected.status == :rejected
assert rejected.rejected_at != nil
assert rejected.reviewed_by_user_id == user.id
assert rejected.reviewed_by_display == to_string(user.email)
end
test "admin can reject a submitted join request", %{request: request} do

View file

@ -12,7 +12,12 @@ defmodule Mv.Membership.JoinRequestTest do
"""
use Mv.DataCase, async: true
require Ash.Query
import Ash.Expr
alias Mv.Fixtures
alias Mv.Membership
alias Mv.Membership.JoinRequest
# Valid minimal attributes for submit (email required; confirmation_token optional for tests)
@valid_submit_attrs %{
@ -136,6 +141,60 @@ defmodule Mv.Membership.JoinRequestTest do
end
end
describe "submit_join_request/2 anti-enumeration (already member / already pending)" do
test "returns {:ok, :notified_already_member} and creates no JoinRequest when email is already a member" do
member =
Fixtures.member_fixture(%{
email: "already_member#{System.unique_integer([:positive])}@example.com"
})
attrs = %{
email: member.email,
confirmation_token: "token-#{System.unique_integer([:positive])}"
}
assert {:ok, :notified_already_member} = Membership.submit_join_request(attrs, actor: nil)
system_actor = Mv.Helpers.SystemActor.get_system_actor()
{:ok, requests} =
JoinRequest
|> Ash.Query.filter(expr(email == ^member.email))
|> Ash.read(actor: system_actor, domain: Mv.Membership)
assert requests == []
end
test "returns {:ok, :notified_already_pending} and does not create duplicate when same email submits again (resend)" do
email = "resend#{System.unique_integer([:positive])}@example.com"
token1 = "first-token-#{System.unique_integer([:positive])}"
attrs1 = %{email: email, confirmation_token: token1}
assert {:ok, request1} = Membership.submit_join_request(attrs1, actor: nil)
assert request1.status == :pending_confirmation
attrs2 = %{
email: email,
confirmation_token: "second-token-#{System.unique_integer([:positive])}"
}
assert {:ok, :notified_already_pending} = Membership.submit_join_request(attrs2, actor: nil)
system_actor = Mv.Helpers.SystemActor.get_system_actor()
{:ok, requests} =
JoinRequest
|> Ash.Query.filter(expr(email == ^email))
|> Ash.read(actor: system_actor, domain: Mv.Membership)
assert length(requests) == 1
assert hd(requests).id == request1.id
# Resend path updates the request (new token stored); confirmation_sent_at will have been set/updated
assert hd(requests).confirmation_sent_at != nil
end
end
describe "allowlist (server-side field filter)" do
test "submit with non-allowlisted form_data keys does not persist those keys" do
# Allowlist restricts which fields are accepted; extra keys must not be stored.