traveltechdeluxe / ash-rbac

Ash extension that allows for easier application of policies.
https://hexdocs.pm/ash_rbac/AshRbac.html
MIT License
13 stars 5 forks source link

Updating from 0.3.2 to 0.4.0 broke some rules #27

Open sezaru opened 8 months ago

sezaru commented 8 months ago

Hey, in my resource I have the following rbac rules:

  rbac do
    bypass :super_admin

    role :guest do
      actions [:read_valids, :get_valid]

      fields [
        ...
      ]
    end

    role :investor do
      actions [
        :set_as_favorite,
        ...
      ]

      fields [:*]
    end

    role :support do
      actions [...]

      fields [:*]
    end

    role :admin do
      actions [...]

      fields [:*]
    end

    role :agent do
      roles_field :organization_roles

      actions [
        ...
      ]

      fields [:*]
    end
  end

In my tests, I call set_as_favorite with a :guest role actor expecting to get a forbidden error, in 0.3.2 that's what I got, updating to 0.4.0, now the test fails because the result is returned normally instead of being blocked by the policy

sezaru commented 8 months ago

In case this helps, here is the resource policies with 0.3.2:

[
  %Ash.Policy.Policy{
    condition: [
      {AshRbac.HasRole, [role: [roles: :super_admin], access_type: :filter]}
    ],
    policies: [
      %Ash.Policy.Check{
        check: {Ash.Policy.Check.Static, [result: true]},
        check_module: Ash.Policy.Check.Static,
        check_opts: [result: true, access_type: :filter],
        type: :authorize_if
      }
    ],
    bypass?: true,
    checks: nil,
    description: nil,
    access_type: :filter
  },
  %Ash.Policy.Policy{
    condition: [
      {Ash.Policy.Check.ActionType, [type: :read, access_type: :filter]}
    ],
    policies: [
      %Ash.Policy.Check{
        check: {Ash.Policy.Check.ActorAttributeEquals,
         [attribute: :active?, value: true]},
        check_module: Ash.Policy.Check.ActorAttributeEquals,
        check_opts: [attribute: :active?, value: true, access_type: :filter],
        type: :forbid_unless
      },
      %Ash.Policy.Check{
        check: {Marketplace.Markets.Property.Checks.IsConfirmed, []},
        check_module: Marketplace.Markets.Property.Checks.IsConfirmed,
        check_opts: [access_type: :filter],
        type: :forbid_unless
      },
      %Ash.Policy.Check{
        check: {Ash.Policy.Check.Static, [result: true]},
        check_module: Ash.Policy.Check.Static,
        check_opts: [result: true, access_type: :filter],
        type: :authorize_if
      }
    ],
    bypass?: nil,
    checks: nil,
    description: nil,
    access_type: :filter
  },
  %Ash.Policy.Policy{
    condition: [
      {Ash.Policy.Check.Action,
       [
         action: [:create, :update, :open_property, :inactive_property,
          :active_property, :update_imaages],
         access_type: :filter
       ]}
    ],
    policies: [
      %Ash.Policy.Check{
        check: {Marketplace.Markets.Property.Checks.HasAnOrganization, []},
        check_module: Marketplace.Markets.Property.Checks.HasAnOrganization,
        check_opts: [access_type: :filter],
        type: :forbid_unless
      },
      %Ash.Policy.Check{
        check: {Ash.Policy.Check.Static, [result: true]},
        check_module: Ash.Policy.Check.Static,
        check_opts: [result: true, access_type: :filter],
        type: :authorize_if
      }
    ],
    bypass?: nil,
    checks: nil,
    description: nil,
    access_type: :filter
  },
  %Ash.Policy.Policy{
    condition: [
      {Ash.Policy.Check.ActionType,
       [type: [:create, :update, :destroy], access_type: :filter]}
    ],
    policies: [
      %Ash.Policy.Check{
        check: {Ash.Policy.Check.ActorAttributeEquals,
         [attribute: :active?, value: true]},
        check_module: Ash.Policy.Check.ActorAttributeEquals,
        check_opts: [attribute: :active?, value: true, access_type: :filter],
        type: :forbid_unless
      },
      %Ash.Policy.Check{
        check: {Marketplace.Markets.Property.Checks.IsConfirmed, []},
        check_module: Marketplace.Markets.Property.Checks.IsConfirmed,
        check_opts: [access_type: :filter],
        type: :forbid_unless
      },
      %Ash.Policy.Check{
        check: {Ash.Policy.Check.Static, [result: true]},
        check_module: Ash.Policy.Check.Static,
        check_opts: [result: true, access_type: :filter],
        type: :authorize_if
      }
    ],
    bypass?: nil,
    checks: nil,
    description: nil,
    access_type: :filter
  },
  %Ash.Policy.Policy{
    condition: [{Ash.Policy.Check.Action, [action: :all, access_type: :filter]}],
    policies: [
      %Ash.Policy.Check{
        check: {AshRbac.HasRole, [role: [roles: [:admin, :support, :investor]]]},
        check_module: AshRbac.HasRole,
        check_opts: [
          role: [roles: [:admin, :support, :investor]],
          access_type: :filter
        ],
        type: :authorize_if
      }
    ],
    bypass?: nil,
    checks: nil,
    description: nil,
    access_type: :filter
  },
  %Ash.Policy.Policy{
    condition: [
      {Ash.Policy.Check.Action, [action: :update, access_type: :filter]}
    ],
    policies: [
      %Ash.Policy.Check{
        check: {AshRbac.HasRole, [role: [organization_roles: [:agent]]]},
        check_module: AshRbac.HasRole,
        check_opts: [role: [organization_roles: [:agent]], access_type: :filter],
        type: :authorize_if
      }
    ],
    bypass?: nil,
    checks: nil,
    description: nil,
    access_type: :filter
  },
  %Ash.Policy.Policy{
    condition: [
      {Ash.Policy.Check.Action, [action: :create, access_type: :filter]}
    ],
    policies: [
      %Ash.Policy.Check{
        check: {AshRbac.HasRole, [role: [organization_roles: [:agent]]]},
        check_module: AshRbac.HasRole,
        check_opts: [role: [organization_roles: [:agent]], access_type: :filter],
        type: :authorize_if
      }
    ],
    bypass?: nil,
    checks: nil,
    description: nil,
    access_type: :filter
  },
  %Ash.Policy.Policy{
    condition: [
      {Ash.Policy.Check.Action, [action: :destroy, access_type: :filter]}
    ],
    policies: [
      %Ash.Policy.Check{
        check: {AshRbac.HasRole, [role: [organization_roles: [:agent]]]},
        check_module: AshRbac.HasRole,
        check_opts: [role: [organization_roles: [:agent]], access_type: :filter],
        type: :authorize_if
      }
    ],
    bypass?: nil,
    checks: nil,
    description: nil,
    access_type: :filter
  },
  %Ash.Policy.Policy{
    condition: [
      {Ash.Policy.Check.Action, [action: :get_valid, access_type: :filter]}
    ],
    policies: [
      %Ash.Policy.Check{
        check: {AshRbac.HasRole,
         [role: [roles: [:admin, :support, :investor, :guest]]]},
        check_module: AshRbac.HasRole,
        check_opts: [
          role: [roles: [:admin, :support, :investor, :guest]],
          access_type: :filter
        ],
        type: :authorize_if
      }
    ],
    bypass?: nil,
    checks: nil,
    description: nil,
    access_type: :filter
  },
  %Ash.Policy.Policy{
    condition: [
      {Ash.Policy.Check.Action, [action: :get_any, access_type: :filter]}
    ],
    policies: [
      %Ash.Policy.Check{
        check: {AshRbac.HasRole, [role: [roles: [:admin, :support, :investor]]]},
        check_module: AshRbac.HasRole,
        check_opts: [
          role: [roles: [:admin, :support, :investor]],
          access_type: :filter
        ],
        type: :authorize_if
      }
    ],
    bypass?: nil,
    checks: nil,
    description: nil,
    access_type: :filter
  },
  %Ash.Policy.Policy{
    condition: [
      {Ash.Policy.Check.Action, [action: :open_property, access_type: :filter]}
    ],
    policies: [
      %Ash.Policy.Check{
        check: {AshRbac.HasRole, [role: [organization_roles: [:agent]]]},
        check_module: AshRbac.HasRole,
        check_opts: [role: [organization_roles: [:agent]], access_type: :filter],
        type: :authorize_if
      }
    ],
    bypass?: nil,
    checks: nil,
    description: nil,
    access_type: :filter
  },
  %Ash.Policy.Policy{
    condition: [
      {Ash.Policy.Check.Action,
       [action: :inactive_property, access_type: :filter]}
    ],
    policies: [
      %Ash.Policy.Check{
        check: {AshRbac.HasRole, [role: [organization_roles: [:agent]]]},
        check_module: AshRbac.HasRole,
        check_opts: [role: [organization_roles: [:agent]], access_type: :filter],
        type: :authorize_if
      }
    ],
    bypass?: nil,
    checks: nil,
    description: nil,
    access_type: :filter
  },
  %Ash.Policy.Policy{
    condition: [
      {Ash.Policy.Check.Action,
       [action: :active_property, access_type: :filter]}
    ],
    policies: [
      %Ash.Policy.Check{
        check: {AshRbac.HasRole, [role: [organization_roles: [:agent]]]},
        check_module: AshRbac.HasRole,
        check_opts: [role: [organization_roles: [:agent]], access_type: :filter],
        type: :authorize_if
      }
    ],
    bypass?: nil,
    checks: nil,
    description: nil,
    access_type: :filter
  },
  %Ash.Policy.Policy{
    condition: [
      {Ash.Policy.Check.Action, [action: :update_images, access_type: :filter]}
    ],
    policies: [
      %Ash.Policy.Check{
        check: {AshRbac.HasRole, [role: [organization_roles: [:agent]]]},
        check_module: AshRbac.HasRole,
        check_opts: [role: [organization_roles: [:agent]], access_type: :filter],
        type: :authorize_if
      }
    ],
    bypass?: nil,
    checks: nil,
    description: nil,
    access_type: :filter
  },
  %Ash.Policy.Policy{
    condition: [
      {Ash.Policy.Check.Action, [action: :read_admin, access_type: :filter]}
    ],
    policies: [
      %Ash.Policy.Check{
        check: {AshRbac.HasRole, [role: [roles: [:admin, :support, :investor]]]},
        check_module: AshRbac.HasRole,
        check_opts: [
          role: [roles: [:admin, :support, :investor]],
          access_type: :filter
        ],
        type: :authorize_if
      }
    ],
    bypass?: nil,
    checks: nil,
    description: nil,
    access_type: :filter
  },
  %Ash.Policy.Policy{
    condition: [
      {Ash.Policy.Check.Action, [action: :read_valids, access_type: :filter]}
    ],
    policies: [
      %Ash.Policy.Check{
        check: {AshRbac.HasRole,
         [role: [roles: [:admin, :support, :investor, :guest]]]},
        check_module: AshRbac.HasRole,
        check_opts: [
          role: [roles: [:admin, :support, :investor, :guest]],
          access_type: :filter
        ],
        type: :authorize_if
      }
    ],
    bypass?: nil,
    checks: nil,
    description: nil,
    access_type: :filter
  },
  %Ash.Policy.Policy{
    condition: [
      {Ash.Policy.Check.Action,
       [action: :list_user_favorites, access_type: :filter]}
    ],
    policies: [
      %Ash.Policy.Check{
        check: {AshRbac.HasRole, [role: [roles: [:admin, :support, :investor]]]},
        check_module: AshRbac.HasRole,
        check_opts: [
          role: [roles: [:admin, :support, :investor]],
          access_type: :filter
        ],
        type: :authorize_if
      }
    ],
    bypass?: nil,
    checks: nil,
    description: nil,
    access_type: :filter
  },
  %Ash.Policy.Policy{
    condition: [
      {Ash.Policy.Check.Action,
       [action: :set_as_favorite, access_type: :filter]}
    ],
    policies: [
      %Ash.Policy.Check{
        check: {AshRbac.HasRole, [role: [roles: [:investor]]]},
        check_module: AshRbac.HasRole,
        check_opts: [role: [roles: [:investor]], access_type: :filter],
        type: :authorize_if
      }
    ],
    bypass?: nil,
    checks: nil,
    description: nil,
    access_type: :filter
  },
  %Ash.Policy.Policy{
    condition: [
      {Ash.Policy.Check.Action,
       [action: :remove_from_favorite, access_type: :filter]}
    ],
    policies: [
      %Ash.Policy.Check{
        check: {AshRbac.HasRole, [role: [roles: [:investor]]]},
        check_module: AshRbac.HasRole,
        check_opts: [role: [roles: [:investor]], access_type: :filter],
        type: :authorize_if
      }
    ],
    bypass?: nil,
    checks: nil,
    description: nil,
    access_type: :filter
  },
  %Ash.Policy.Policy{
    condition: [
      {Ash.Policy.Check.Action, [action: :changelog, access_type: :filter]}
    ],
    policies: [
      %Ash.Policy.Check{
        check: {AshRbac.HasRole, [role: [organization_roles: [:agent]]]},
        check_module: AshRbac.HasRole,
        check_opts: [role: [organization_roles: [:agent]], access_type: :filter],
        type: :authorize_if
      }
    ],
    bypass?: nil,
    checks: nil,
    description: nil,
    access_type: :filter
  }
]

And here is the same policies but with 0.4.0:

[
  %Ash.Policy.Policy{
    condition: [
      {AshRbac.HasRole, [role: [roles: :super_admin], access_type: :filter]}
    ],
    policies: [
      %Ash.Policy.Check{
        check: {Ash.Policy.Check.Static, [result: true]},
        check_module: Ash.Policy.Check.Static,
        check_opts: [result: true, access_type: :filter],
        type: :authorize_if
      }
    ],
    bypass?: true,
    checks: nil,
    description: nil,
    access_type: :filter
  },
  %Ash.Policy.Policy{
    condition: [
      {Ash.Policy.Check.ActionType, [type: :read, access_type: :filter]}
    ],
    policies: [
      %Ash.Policy.Check{
        check: {Ash.Policy.Check.ActorAttributeEquals,
         [attribute: :active?, value: true]},
        check_module: Ash.Policy.Check.ActorAttributeEquals,
        check_opts: [attribute: :active?, value: true, access_type: :filter],
        type: :forbid_unless
      },
      %Ash.Policy.Check{
        check: {Marketplace.Markets.Property.Checks.IsConfirmed, []},
        check_module: Marketplace.Markets.Property.Checks.IsConfirmed,
        check_opts: [access_type: :filter],
        type: :forbid_unless
      },
      %Ash.Policy.Check{
        check: {Ash.Policy.Check.Static, [result: true]},
        check_module: Ash.Policy.Check.Static,
        check_opts: [result: true, access_type: :filter],
        type: :authorize_if
      }
    ],
    bypass?: nil,
    checks: nil,
    description: nil,
    access_type: :filter
  },
  %Ash.Policy.Policy{
    condition: [
      {Ash.Policy.Check.Action,
       [
         action: [:create, :update, :open_property, :inactive_property,
          :active_property, :update_imaages],
         access_type: :filter
       ]}
    ],
    policies: [
      %Ash.Policy.Check{
        check: {Marketplace.Markets.Property.Checks.HasAnOrganization, []},
        check_module: Marketplace.Markets.Property.Checks.HasAnOrganization,
        check_opts: [access_type: :filter],
        type: :forbid_unless
      },
      %Ash.Policy.Check{
        check: {Ash.Policy.Check.Static, [result: true]},
        check_module: Ash.Policy.Check.Static,
        check_opts: [result: true, access_type: :filter],
        type: :authorize_if
      }
    ],
    bypass?: nil,
    checks: nil,
    description: nil,
    access_type: :filter
  },
  %Ash.Policy.Policy{
    condition: [
      {Ash.Policy.Check.ActionType,
       [type: [:create, :update, :destroy], access_type: :filter]}
    ],
    policies: [
      %Ash.Policy.Check{
        check: {Ash.Policy.Check.ActorAttributeEquals,
         [attribute: :active?, value: true]},
        check_module: Ash.Policy.Check.ActorAttributeEquals,
        check_opts: [attribute: :active?, value: true, access_type: :filter],
        type: :forbid_unless
      },
      %Ash.Policy.Check{
        check: {Marketplace.Markets.Property.Checks.IsConfirmed, []},
        check_module: Marketplace.Markets.Property.Checks.IsConfirmed,
        check_opts: [access_type: :filter],
        type: :forbid_unless
      },
      %Ash.Policy.Check{
        check: {Ash.Policy.Check.Static, [result: true]},
        check_module: Ash.Policy.Check.Static,
        check_opts: [result: true, access_type: :filter],
        type: :authorize_if
      }
    ],
    bypass?: nil,
    checks: nil,
    description: nil,
    access_type: :filter
  },
  %Ash.Policy.Policy{
    condition: [
      {Ash.Policy.Check.Action, [action: :all, access_type: :filter]},
      {AshRbac.HasRole,
       [role: [roles: [:admin, :support, :investor]], access_type: :filter]}
    ],
    policies: [
      %Ash.Policy.Check{
        check: {Ash.Policy.Check.Static, [result: true]},
        check_module: Ash.Policy.Check.Static,
        check_opts: [result: true, access_type: :filter],
        type: :authorize_if
      }
    ],
    bypass?: nil,
    checks: nil,
    description: nil,
    access_type: :filter
  },
  %Ash.Policy.Policy{
    condition: [
      {Ash.Policy.Check.Action, [action: :update, access_type: :filter]},
      {AshRbac.HasRole,
       [role: [organization_roles: [:agent]], access_type: :filter]}
    ],
    policies: [
      %Ash.Policy.Check{
        check: {Ash.Policy.Check.Static, [result: true]},
        check_module: Ash.Policy.Check.Static,
        check_opts: [result: true, access_type: :filter],
        type: :authorize_if
      }
    ],
    bypass?: nil,
    checks: nil,
    description: nil,
    access_type: :filter
  },
  %Ash.Policy.Policy{
    condition: [
      {Ash.Policy.Check.Action, [action: :create, access_type: :filter]},
      {AshRbac.HasRole,
       [role: [organization_roles: [:agent]], access_type: :filter]}
    ],
    policies: [
      %Ash.Policy.Check{
        check: {Ash.Policy.Check.Static, [result: true]},
        check_module: Ash.Policy.Check.Static,
        check_opts: [result: true, access_type: :filter],
        type: :authorize_if
      }
    ],
    bypass?: nil,
    checks: nil,
    description: nil,
    access_type: :filter
  },
  %Ash.Policy.Policy{
    condition: [
      {Ash.Policy.Check.Action, [action: :destroy, access_type: :filter]},
      {AshRbac.HasRole,
       [role: [organization_roles: [:agent]], access_type: :filter]}
    ],
    policies: [
      %Ash.Policy.Check{
        check: {Ash.Policy.Check.Static, [result: true]},
        check_module: Ash.Policy.Check.Static,
        check_opts: [result: true, access_type: :filter],
        type: :authorize_if
      }
    ],
    bypass?: nil,
    checks: nil,
    description: nil,
    access_type: :filter
  },
  %Ash.Policy.Policy{
    condition: [
      {Ash.Policy.Check.Action, [action: :get_valid, access_type: :filter]},
      {AshRbac.HasRole,
       [
         role: [roles: [:admin, :support, :investor, :guest]],
         access_type: :filter
       ]}
    ],
    policies: [
      %Ash.Policy.Check{
        check: {Ash.Policy.Check.Static, [result: true]},
        check_module: Ash.Policy.Check.Static,
        check_opts: [result: true, access_type: :filter],
        type: :authorize_if
      }
    ],
    bypass?: nil,
    checks: nil,
    description: nil,
    access_type: :filter
  },
  %Ash.Policy.Policy{
    condition: [
      {Ash.Policy.Check.Action, [action: :get_any, access_type: :filter]},
      {AshRbac.HasRole,
       [role: [roles: [:admin, :support, :investor]], access_type: :filter]}
    ],
    policies: [
      %Ash.Policy.Check{
        check: {Ash.Policy.Check.Static, [result: true]},
        check_module: Ash.Policy.Check.Static,
        check_opts: [result: true, access_type: :filter],
        type: :authorize_if
      }
    ],
    bypass?: nil,
    checks: nil,
    description: nil,
    access_type: :filter
  },
  %Ash.Policy.Policy{
    condition: [
      {Ash.Policy.Check.Action, [action: :open_property, access_type: :filter]},
      {AshRbac.HasRole,
       [role: [organization_roles: [:agent]], access_type: :filter]}
    ],
    policies: [
      %Ash.Policy.Check{
        check: {Ash.Policy.Check.Static, [result: true]},
        check_module: Ash.Policy.Check.Static,
        check_opts: [result: true, access_type: :filter],
        type: :authorize_if
      }
    ],
    bypass?: nil,
    checks: nil,
    description: nil,
    access_type: :filter
  },
  %Ash.Policy.Policy{
    condition: [
      {Ash.Policy.Check.Action,
       [action: :inactive_property, access_type: :filter]},
      {AshRbac.HasRole,
       [role: [organization_roles: [:agent]], access_type: :filter]}
    ],
    policies: [
      %Ash.Policy.Check{
        check: {Ash.Policy.Check.Static, [result: true]},
        check_module: Ash.Policy.Check.Static,
        check_opts: [result: true, access_type: :filter],
        type: :authorize_if
      }
    ],
    bypass?: nil,
    checks: nil,
    description: nil,
    access_type: :filter
  },
  %Ash.Policy.Policy{
    condition: [
      {Ash.Policy.Check.Action,
       [action: :active_property, access_type: :filter]},
      {AshRbac.HasRole,
       [role: [organization_roles: [:agent]], access_type: :filter]}
    ],
    policies: [
      %Ash.Policy.Check{
        check: {Ash.Policy.Check.Static, [result: true]},
        check_module: Ash.Policy.Check.Static,
        check_opts: [result: true, access_type: :filter],
        type: :authorize_if
      }
    ],
    bypass?: nil,
    checks: nil,
    description: nil,
    access_type: :filter
  },
  %Ash.Policy.Policy{
    condition: [
      {Ash.Policy.Check.Action, [action: :update_images, access_type: :filter]},
      {AshRbac.HasRole,
       [role: [organization_roles: [:agent]], access_type: :filter]}
    ],
    policies: [
      %Ash.Policy.Check{
        check: {Ash.Policy.Check.Static, [result: true]},
        check_module: Ash.Policy.Check.Static,
        check_opts: [result: true, access_type: :filter],
        type: :authorize_if
      }
    ],
    bypass?: nil,
    checks: nil,
    description: nil,
    access_type: :filter
  },
  %Ash.Policy.Policy{
    condition: [
      {Ash.Policy.Check.Action, [action: :read_admin, access_type: :filter]},
      {AshRbac.HasRole,
       [role: [roles: [:admin, :support, :investor]], access_type: :filter]}
    ],
    policies: [
      %Ash.Policy.Check{
        check: {Ash.Policy.Check.Static, [result: true]},
        check_module: Ash.Policy.Check.Static,
        check_opts: [result: true, access_type: :filter],
        type: :authorize_if
      }
    ],
    bypass?: nil,
    checks: nil,
    description: nil,
    access_type: :filter
  },
  %Ash.Policy.Policy{
    condition: [
      {Ash.Policy.Check.Action, [action: :read_valids, access_type: :filter]},
      {AshRbac.HasRole,
       [
         role: [roles: [:admin, :support, :investor, :guest]],
         access_type: :filter
       ]}
    ],
    policies: [
      %Ash.Policy.Check{
        check: {Ash.Policy.Check.Static, [result: true]},
        check_module: Ash.Policy.Check.Static,
        check_opts: [result: true, access_type: :filter],
        type: :authorize_if
      }
    ],
    bypass?: nil,
    checks: nil,
    description: nil,
    access_type: :filter
  },
  %Ash.Policy.Policy{
    condition: [
      {Ash.Policy.Check.Action,
       [action: :list_user_favorites, access_type: :filter]},
      {AshRbac.HasRole,
       [role: [roles: [:admin, :support, :investor]], access_type: :filter]}
    ],
    policies: [
      %Ash.Policy.Check{
        check: {Ash.Policy.Check.Static, [result: true]},
        check_module: Ash.Policy.Check.Static,
        check_opts: [result: true, access_type: :filter],
        type: :authorize_if
      }
    ],
    bypass?: nil,
    checks: nil,
    description: nil,
    access_type: :filter
  },
  %Ash.Policy.Policy{
    condition: [
      {Ash.Policy.Check.Action,
       [action: :set_as_favorite, access_type: :filter]},
      {AshRbac.HasRole, [role: [roles: [:investor]], access_type: :filter]}
    ],
    policies: [
      %Ash.Policy.Check{
        check: {Ash.Policy.Check.Static, [result: true]},
        check_module: Ash.Policy.Check.Static,
        check_opts: [result: true, access_type: :filter],
        type: :authorize_if
      }
    ],
    bypass?: nil,
    checks: nil,
    description: nil,
    access_type: :filter
  },
  %Ash.Policy.Policy{
    condition: [
      {Ash.Policy.Check.Action,
       [action: :remove_from_favorite, access_type: :filter]},
      {AshRbac.HasRole, [role: [roles: [:investor]], access_type: :filter]}
    ],
    policies: [
      %Ash.Policy.Check{
        check: {Ash.Policy.Check.Static, [result: true]},
        check_module: Ash.Policy.Check.Static,
        check_opts: [result: true, access_type: :filter],
        type: :authorize_if
      }
    ],
    bypass?: nil,
    checks: nil,
    description: nil,
    access_type: :filter
  },
  %Ash.Policy.Policy{
    condition: [
      {Ash.Policy.Check.Action, [action: :changelog, access_type: :filter]},
      {AshRbac.HasRole,
       [role: [organization_roles: [:agent]], access_type: :filter]}
    ],
    policies: [
      %Ash.Policy.Check{
        check: {Ash.Policy.Check.Static, [result: true]},
        check_module: Ash.Policy.Check.Static,
        check_opts: [result: true, access_type: :filter],
        type: :authorize_if
      }
    ],
    bypass?: nil,
    checks: nil,
    description: nil,
    access_type: :filter
  }
]
barnabasJ commented 8 months ago

Sorry, I didn't check the issues in a bit. I will take a look this week.

barnabasJ commented 8 months ago

@sezaru just looking over the policies I have a feeling that the problem might be with this policy

  %Ash.Policy.Policy{
    condition: [
      {Ash.Policy.Check.ActionType,
       [type: [:create, :update, :destroy], access_type: :filter]}
    ],
    policies: [
      %Ash.Policy.Check{
        check: {Ash.Policy.Check.ActorAttributeEquals,
         [attribute: :active?, value: true]},
        check_module: Ash.Policy.Check.ActorAttributeEquals,
        check_opts: [attribute: :active?, value: true, access_type: :filter],
        type: :forbid_unless
      },
      %Ash.Policy.Check{
        check: {Marketplace.Markets.Property.Checks.IsConfirmed, []},
        check_module: Marketplace.Markets.Property.Checks.IsConfirmed,
        check_opts: [access_type: :filter],
        type: :forbid_unless
      },
      %Ash.Policy.Check{
        check: {Ash.Policy.Check.Static, [result: true]},
        check_module: Ash.Policy.Check.Static,
        check_opts: [result: true, access_type: :filter],
        type: :authorize_if
      }
    ],
    bypass?: nil,
    checks: nil,
    description: nil,
    access_type: :filter
  },

Have you already tried setting the config for logging the policy breakdowns to see which policy is actually allowing access?

https://hexdocs.pm/ash/policies.html#debugging-and-logging

config :ash, :policies, show_policy_breakdowns?: true
config :ash, :policies, log_policy_breakdowns: :error # Use whatever log level you'd like to use here
config :ash, :policies, log_successful_policy_breakdowns: :error # Use whatever log level you'd like to use here
barnabasJ commented 8 months ago

During the last change, I moved the role check from being inside the policy to the policy condition.

before:

policy [action(:read)] do
    authorize_if {hAshRbac.HasRole, [role: [:user]]}
end

after:

policy [action(:read), {hAshRbac.HasRole, [role: [:user]]}] do
    authorize_if always()
end

This should make it easier to add other policies without them conflicting. What I think is happening on your side is that this policy applied before the change and forbade access because the role wasn't there. Now, the policy is not applying anymore, but another policy, one you regularly added to the policy block, is and is allowing access.

Please let me know if this helped, or if you have any feedback.

sezaru commented 4 months ago

Sorry taking so long to reply to this..

I'm not sure if I understand how to solve the issue, for example, I have these policies/rbac:

rbac do
  role :agent do
    roles_field :organization_roles
    actions [:create_without_persisting]
    fields [:*]
  end
end

policies do
  policy action([:create_without_persisting]) do
    forbid_unless Checks.IsConfirmed
    forbid_unless Checks.HasAnOrganization
    authorize_if always()
  end
end

I have a test that should return forbidden for the action create_without_persisting because the field organization_roles is an empty list.

If I run it with the above code, the action fill ignore the rbac rule and not block the call even though the organization_roles is []. If I remove the policies code block, then it works correctly and the rbac rule is enforced.

Do I need to rewrite my policy in a different way to make it work with ash_rbac 0.4.0?

barnabasJ commented 1 month ago

Because of the change we made, the role check is now part of the condition, which means the whole policy block is not applied because the role does not match.

That is why your check now allows access to the action.

If you want the agent to only be able to access the action if they confirmed and have an organization you can now add those checks to the action insist the rbac block

rbac do
  role :agent do
    roles_field :organization_roles
    actions [create_without_persisting: [Checks.IsConfirmed, Checks.HasAnOrganization]
    fields [:*]
  end
end