diff --git a/lib/accounts/user.ex b/lib/accounts/user.ex index 3f0381d..08d1130 100644 --- a/lib/accounts/user.ex +++ b/lib/accounts/user.ex @@ -269,46 +269,31 @@ defmodule Mv.Accounts.User do # Authorization Policies # Order matters: Most specific policies first, then general permission check policies do - # ASHAUTHENTICATION BYPASS: Allow authentication actions (registration, login) - # These actions are called internally by AshAuthentication and need to bypass - # normal authorization policies. This must come FIRST because User is an - # authentication resource and authentication flows should have priority. + # AshAuthentication bypass (registration/login without actor) bypass AshAuthentication.Checks.AshAuthenticationInteraction do description "Allow AshAuthentication internal operations (registration, login)" authorize_if always() end - # SYSTEM OPERATIONS: Allow CRUD operations without actor (TEST ENVIRONMENT ONLY) - # In test: All operations allowed (for test fixtures) - # In production/dev: ALL operations denied without actor (fail-closed for security) - # NoActor.check uses compile-time environment detection to prevent security issues + # NoActor bypass (test fixtures only, see no_actor.ex) bypass action_type([:create, :read, :update, :destroy]) do description "Allow system operations without actor (test environment only)" authorize_if Mv.Authorization.Checks.NoActor end - # SPECIAL CASE: Users can always READ their own account - # This allows users with ANY permission set to read their own user record - # Uses bypass with expr filter to enable auto_filter behavior for reads/lists - # (consistent with Member "always read linked member" pattern) + # READ bypass for list queries (scope :own via expr) bypass action_type(:read) do description "Users can always read their own account" authorize_if expr(id == ^actor(:id)) end - # GENERAL: Check permissions from user's role - # HasPermission handles permissions correctly: - # - :own_data → can update own user (scope :own) - # - :read_only → can update own user (scope :own) - # - :normal_user → can update own user (scope :own) - # - :admin → can read/create/update/destroy all users (scope :all) + # UPDATE/DESTROY via HasPermission (evaluates PermissionSets scope) policy action_type([:read, :create, :update, :destroy]) do description "Check permissions from user's role and permission set" authorize_if Mv.Authorization.Checks.HasPermission end - # DEFAULT: Ash implicitly forbids if no policy authorizes - # No explicit forbid needed, as Ash's default behavior is fail-closed + # Default: Ash implicitly forbids if no policy authorizes (fail-closed) end # Global validations - applied to all relevant actions