[FEATURE]: Member Email Validation for Linked Members #397

Closed
opened 2026-02-03 13:23:54 +01:00 by moritz · 0 comments
Owner

Description

Implement special validation: Only admins, or the user linked to that member, can edit a member’s email when that member is linked to a user. This prevents breaking email synchronization for other users’ accounts while allowing users to change their own linked member’s email (which syncs to their own user).

Tasks

  1. Open lib/membership/member.ex (Member resource; domain Mv.Membership).
  2. Add a custom validation in the validations block that runs only when the email attribute is changing on update (e.g. via where: [changing(:email)], on: [:update] or equivalent Ash 3 syntax).
  3. Implement the validation logic (e.g. in a module such as Mv.Membership.Member.Validations.EmailChangePermission, or as an inline validation):
    • If the member is not linked to a user (no user with member_id == member.id), allow the change.
    • If the member is linked and the email is changing: allow only if either
      • the actor has User.update permission with scope :all (admin), or
      • the actor is the user linked to this member (actor.member_id == changeset.data.id).
    • Otherwise return an error with a clear message (e.g. “Only administrators can change email for members linked to users”).
  4. Use PermissionSets.get_permissions/1 (via the actor’s role permission_set_name) to determine whether the actor has User.update with scope :all. For “own linked member”, check actor.member_id == changeset.data.id. Do not use the system actor as a fallback when the actor is missing; treat missing actor as not allowed.
  5. Determine “member is linked” from the member record (e.g. load the :user relationship or resolve the linked user from changeset.data); the Member resource has has_one :user and the link is stored on User as member_id.
  6. Add tests for all relevant cases (see Test Strategy). Use Gettext for the error message if that matches the rest of the codebase.

Acceptance Criteria

  • Non-admin can edit email of an unlinked member.
  • Non-admin cannot edit email of another user’s linked member (validation error).
  • User with own_data can edit email of their own linked member (actor.member_id == member.id).
  • User with normal_user can edit email of their own linked member when they have one (actor.member_id == member.id).
  • Admin can edit email of any linked member.
  • Validation runs only when the email attribute is changing on update.
  • Error message is clear and helpful (e.g. mentions “administrators” and “linked to users”). Document in the validation/module that the rule allows “admin or the linked user (own member)”.

Test Strategy (TDD)

Unlinked member

  • User with :normal_user can update email of an unlinked member.
  • User with :read_only cannot update any member (rejected by policy, not by this validation).
  • Validation does not block when the member has no linked user (e.g. member.user / linked user is nil).

Linked member – another user’s member

  • User with :normal_user cannot update email of another user’s linked member (validation error).
  • Error message mentions “administrators” and “linked to users”.
  • User with :admin can update email of that linked member (validation passes).

Linked member – own member

  • User with :own_data can update email of their own linked member (validation passes; actor.member_id == member.id).
  • Optionally: User with :normal_user who has a linked member can update email of that same member (validation passes).

No-op / other fields

  • Validation does not run when the email did not change.
  • Updating other attributes (e.g. name, address) works as before and is not affected by this validation.

Test file

test/mv/membership/member_email_validation_test.exs

Reuse helpers from test/mv/membership/member_policies_test.exs (e.g. create_user_with_permission_set, create_linked_member_for_user, create_unlinked_member) where appropriate.

**Description** Implement special validation: Only admins, or the user linked to that member, can edit a member’s email when that member is linked to a user. This prevents breaking email synchronization for other users’ accounts while allowing users to change their own linked member’s email (which syncs to their own user). **Tasks** 1. Open `lib/membership/member.ex` (Member resource; domain `Mv.Membership`). 2. Add a custom validation in the `validations` block that runs only when the email attribute is changing on update (e.g. via `where: [changing(:email)], on: [:update]` or equivalent Ash 3 syntax). 3. Implement the validation logic (e.g. in a module such as `Mv.Membership.Member.Validations.EmailChangePermission`, or as an inline validation): - If the member is not linked to a user (no user with `member_id == member.id`), allow the change. - If the member is linked and the email is changing: allow only if **either** - the actor has User.update permission with scope `:all` (admin), **or** - the actor is the user linked to this member (`actor.member_id == changeset.data.id`). - Otherwise return an error with a clear message (e.g. “Only administrators can change email for members linked to users”). 4. Use `PermissionSets.get_permissions/1` (via the actor’s role `permission_set_name`) to determine whether the actor has User.update with scope `:all`. For “own linked member”, check `actor.member_id == changeset.data.id`. Do not use the system actor as a fallback when the actor is missing; treat missing actor as not allowed. 5. Determine “member is linked” from the member record (e.g. load the `:user` relationship or resolve the linked user from `changeset.data`); the Member resource has `has_one :user` and the link is stored on User as `member_id`. 6. Add tests for all relevant cases (see Test Strategy). Use Gettext for the error message if that matches the rest of the codebase. **Acceptance Criteria** - [ ] Non-admin can edit email of an unlinked member. - [ ] Non-admin cannot edit email of another user’s linked member (validation error). - [ ] User with own_data can edit email of their own linked member (actor.member_id == member.id). - [ ] User with normal_user can edit email of their own linked member when they have one (actor.member_id == member.id). - [ ] Admin can edit email of any linked member. - [ ] Validation runs only when the email attribute is changing on update. - [ ] Error message is clear and helpful (e.g. mentions “administrators” and “linked to users”). Document in the validation/module that the rule allows “admin or the linked user (own member)”. **Test Strategy (TDD)** **Unlinked member** - User with :normal_user can update email of an unlinked member. - User with :read_only cannot update any member (rejected by policy, not by this validation). - Validation does not block when the member has no linked user (e.g. `member.user` / linked user is nil). **Linked member – another user’s member** - User with :normal_user cannot update email of another user’s linked member (validation error). - Error message mentions “administrators” and “linked to users”. - User with :admin can update email of that linked member (validation passes). **Linked member – own member** - User with :own_data can update email of their own linked member (validation passes; actor.member_id == member.id). - Optionally: User with :normal_user who has a linked member can update email of that same member (validation passes). **No-op / other fields** - Validation does not run when the email did not change. - Updating other attributes (e.g. name, address) works as before and is not affected by this validation. **Test file** `test/mv/membership/member_email_validation_test.exs` Reuse helpers from `test/mv/membership/member_policies_test.exs` (e.g. `create_user_with_permission_set`, `create_linked_member_for_user`, `create_unlinked_member`) where appropriate.
moritz added this to the Accounts & Logins milestone 2026-02-03 13:23:54 +01:00
moritz added the
M
label 2026-02-03 13:23:54 +01:00
moritz self-assigned this 2026-02-03 13:23:54 +01:00
moritz added this to the Sprint 12: 29.01.- 19.02 project 2026-02-03 13:23:54 +01:00
moritz modified the milestone from Accounts & Logins to We have different roles and permissions 2026-02-03 16:38:29 +01:00
Sign in to join this conversation.
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: local-it/mitgliederverwaltung#397
No description provided.