From a161393ea128e438c090343a112f3442971c3106 Mon Sep 17 00:00:00 2001 From: Simon Date: Mon, 19 Jan 2026 13:40:28 +0100 Subject: [PATCH] fix: change creation of admin user --- docs/development-progress-log.md | 2 +- priv/repo/seeds.exs | 67 +++++++++++++++++++++++--------- test/membership/member_test.exs | 20 ++++++++++ 3 files changed, 69 insertions(+), 20 deletions(-) diff --git a/docs/development-progress-log.md b/docs/development-progress-log.md index 629987e..f55c214 100644 --- a/docs/development-progress-log.md +++ b/docs/development-progress-log.md @@ -775,7 +775,7 @@ end ### Test Data Management **Seed Data:** -- Admin user: `admin@mv.local` / `testpassword` +- Admin user: `admin@localhost` / `testpassword` (configurable via `ADMIN_EMAIL` env var) - Sample members: Hans Müller, Greta Schmidt, Friedrich Wagner - Linked accounts: Maria Weber, Thomas Klein - CustomFieldValue types: String, Date, Boolean, Email diff --git a/priv/repo/seeds.exs b/priv/repo/seeds.exs index b89ba3c..6294353 100644 --- a/priv/repo/seeds.exs +++ b/priv/repo/seeds.exs @@ -9,6 +9,8 @@ alias Mv.Authorization alias Mv.MembershipFees.MembershipFeeType alias Mv.MembershipFees.CycleGenerator +require Ash.Query + # Create example membership fee types for fee_type_attrs <- [ %{ @@ -124,13 +126,10 @@ for attrs <- [ ) end -# Create admin user for testing -admin_user = - Accounts.create_user!(%{email: "admin@mv.local"}, upsert?: true, upsert_identity: :unique_email) - |> Ash.Changeset.for_update(:admin_set_password, %{password: "testpassword"}) - |> Ash.update!() +# Get admin email from environment variable or use default +admin_email = System.get_env("ADMIN_EMAIL") || "admin@localhost" -# Create admin role and assign it to admin user +# Create admin role (used for assigning to admin users) admin_role = case Authorization.list_roles() do {:ok, roles} -> @@ -154,23 +153,53 @@ admin_role = nil end -# Assign admin role to admin user if role was created/found -if admin_role do - admin_user - |> Ash.Changeset.for_update(:update, %{}) - |> Ash.Changeset.manage_relationship(:role, admin_role, type: :append_and_remove) - |> Ash.update!() +if is_nil(admin_role) do + raise "Failed to create or find admin role. Cannot proceed with member seeding." +end + +# Assign admin role to user with ADMIN_EMAIL (if user exists) +# This handles both existing users (e.g., from OIDC) and newly created users +case Accounts.User + |> Ash.Query.filter(email == ^admin_email) + |> Ash.read_one(domain: Mv.Accounts) do + {:ok, existing_admin_user} when not is_nil(existing_admin_user) -> + # User already exists (e.g., via OIDC) - assign admin role + existing_admin_user + |> Ash.Changeset.for_update(:update, %{}) + |> Ash.Changeset.manage_relationship(:role, admin_role, type: :replace) + |> Ash.update!() + + {:ok, nil} -> + # User doesn't exist - create admin user with password + Accounts.create_user!(%{email: admin_email}, upsert?: true, upsert_identity: :unique_email) + |> Ash.Changeset.for_update(:admin_set_password, %{password: "testpassword"}) + |> Ash.update!() + |> then(fn user -> + user + |> Ash.Changeset.for_update(:update, %{}) + |> Ash.Changeset.manage_relationship(:role, admin_role, type: :replace) + |> Ash.update!() + end) + + {:error, error} -> + raise "Failed to check for existing admin user: #{inspect(error)}" end # Load admin user with role for use as actor in member operations # This ensures all member operations have proper authorization -# If admin role creation failed, we cannot proceed with member operations admin_user_with_role = - if admin_role do - admin_user - |> Ash.load!(:role) - else - raise "Failed to create or find admin role. Cannot proceed with member seeding." + case Accounts.User + |> Ash.Query.filter(email == ^admin_email) + |> Ash.read_one(domain: Mv.Accounts) do + {:ok, user} when not is_nil(user) -> + user + |> Ash.load!(:role) + + {:ok, nil} -> + raise "Admin user not found after creation/assignment" + + {:error, error} -> + raise "Failed to load admin user: #{inspect(error)}" end # Load all membership fee types for assignment @@ -598,7 +627,7 @@ IO.puts("📝 Created sample data:") IO.puts(" - Global settings: club_name = #{default_club_name}") IO.puts(" - Membership fee types: 4 types (Yearly, Half-yearly, Quarterly, Monthly)") IO.puts(" - Custom fields: 12 fields (String, Date, Boolean, Email, + 8 realistic fields)") -IO.puts(" - Admin user: admin@mv.local (password: testpassword)") +IO.puts(" - Admin user: #{admin_email} (password: testpassword)") IO.puts(" - Sample members: Hans, Greta, Friedrich") IO.puts( diff --git a/test/membership/member_test.exs b/test/membership/member_test.exs index 258d8be..6919ec1 100644 --- a/test/membership/member_test.exs +++ b/test/membership/member_test.exs @@ -73,6 +73,26 @@ defmodule Mv.Membership.MemberTest do end end + describe "Authorization" do + @valid_attrs %{ + first_name: "John", + last_name: "Doe", + email: "john@example.com" + } + + test "user without role cannot create member" do + # Create a user without a role + user = Mv.Fixtures.user_fixture() + # Ensure user has no role (nil role) + user_without_role = %{user | role: nil} + + # Attempt to create a member with user without role as actor + # This should fail with Ash.Error.Forbidden containing a Policy error + assert {:error, %Ash.Error.Forbidden{errors: [%Ash.Error.Forbidden.Policy{}]}} = + Membership.create_member(@valid_attrs, actor: user_without_role) + end + end + # Helper function for error evaluation defp error_message(errors, field) do errors