Simple policy checks based with relationships

Hello, friends! So on the website documentation, we have an example of a simple policy check:
# we're inside of a module here
def match?(%MyApp.User{age: age} = _actor, %{resource: MyApp.Beer} = _context, _opts) do
age >= 21
end
# we're inside of a module here
def match?(%MyApp.User{age: age} = _actor, %{resource: MyApp.Beer} = _context, _opts) do
age >= 21
end
All well and good, but in a more real world example we'd probably have: - Roles for Users - Many permissions for those roles - Potentially many permissions for users So let's say we have something like the following: A User resource:
defmodule MyApp.Accounts.User do
@moduledoc """
Close to a real world example of a basic users, but
obviously there's a lot more stuff here.
Shortened for the sake of brevity.
"""
use Ash.Resource,
data_layer: AshPostgres.DataLayer,
extensions: [AshAuthentication]

postgres do
table "app_users"
repo MyApp.Repo
end

attributes do
uuid_primary_key :id
attribute :first_name, :ci_string, allow_nil?: false
attribute :last_name, :ci_string, allow_nil?: false
attribute :email, :ci_string, allow_nil?: false, sensitive?: true
attribute :hashed_password, :string, allow_nil?: false, sensitive?: true

create_timestamp(:created_at)
update_timestamp(:updated_at)
end

relationships do
has_one :role, MyApp.Accounts.Role
many_to_many :permissions, MyApp.Accounts.User do
through MyApp.Accounts.UserPermission
source_attribute_on_join_resource :user_id
destination_attribute_on_join_resource :permission_id
end
end
defmodule MyApp.Accounts.User do
@moduledoc """
Close to a real world example of a basic users, but
obviously there's a lot more stuff here.
Shortened for the sake of brevity.
"""
use Ash.Resource,
data_layer: AshPostgres.DataLayer,
extensions: [AshAuthentication]

postgres do
table "app_users"
repo MyApp.Repo
end

attributes do
uuid_primary_key :id
attribute :first_name, :ci_string, allow_nil?: false
attribute :last_name, :ci_string, allow_nil?: false
attribute :email, :ci_string, allow_nil?: false, sensitive?: true
attribute :hashed_password, :string, allow_nil?: false, sensitive?: true

create_timestamp(:created_at)
update_timestamp(:updated_at)
end

relationships do
has_one :role, MyApp.Accounts.Role
many_to_many :permissions, MyApp.Accounts.User do
through MyApp.Accounts.UserPermission
source_attribute_on_join_resource :user_id
destination_attribute_on_join_resource :permission_id
end
end
And you want to check both the role, the permissions a role has and user permissions. My question would be how do we go about doing the simple policy checks when we have a role-permission setup like this? Because the check itself isn't complex, it's more of a "how do we get there" question.
3 Replies
AlecStewart1#1125
AlecStewart1#1125OP•2y ago
To give some more clarity, here's some other resources: A RolePermission resource:
defmodule MyApp.Accounts.RolePermission do
use Ash.Resource,
data_layer: AshPostgres.DataLayer

postgres do
table "role_permissions"
repo MyApp.Repo
end

relationships do
belongs_to :role, MyApp.Accounts.Role do
primary_key? true
allow_nil? false
end
belongs_to :permission, MyApp.Accounts.Permission do
primary_key? true
allow_nil? false
end
end
end
defmodule MyApp.Accounts.RolePermission do
use Ash.Resource,
data_layer: AshPostgres.DataLayer

postgres do
table "role_permissions"
repo MyApp.Repo
end

relationships do
belongs_to :role, MyApp.Accounts.Role do
primary_key? true
allow_nil? false
end
belongs_to :permission, MyApp.Accounts.Permission do
primary_key? true
allow_nil? false
end
end
end
A UserPermission resource:
defmodule MyApp.Accounts.UserPermission do
use Ash.Resource,
data_layer: AshPostgres.DataLayer

postgres do
table "user_permissions"
repo MyApp.Repo
end

relationships do
belongs_to :user, MyApp.Accounts.User do
primary_key? true
allow_nil? false
end
belongs_to :permission, MyApp.Accounts.Permission do
primary_key? true
allow_nil? false
end
end
end
defmodule MyApp.Accounts.UserPermission do
use Ash.Resource,
data_layer: AshPostgres.DataLayer

postgres do
table "user_permissions"
repo MyApp.Repo
end

relationships do
belongs_to :user, MyApp.Accounts.User do
primary_key? true
allow_nil? false
end
belongs_to :permission, MyApp.Accounts.Permission do
primary_key? true
allow_nil? false
end
end
end
And of course... A Role resource:
defmodule MyApp.Accounts.Role do
use Ash.Resource,
data_layer: AshPostgres.DataLayer

postgres do
table "roles"
repo MyApp.Repo
end

attributes do
uuid_primary_key :id
attribute :name, :string do
allow_nil? false
end
attribute :description, :string do
allow_nil? false
end
end

relationships do
has_many :users, MyApp.Accounts.User
many_to_many :permissions, MyApp.Accounts.Role do
through MyApp.Accounts.RolePermission
source_attribute_on_join_resource :role_id
destination_attribute_on_join_resource :permission_id
end
end
end
defmodule MyApp.Accounts.Role do
use Ash.Resource,
data_layer: AshPostgres.DataLayer

postgres do
table "roles"
repo MyApp.Repo
end

attributes do
uuid_primary_key :id
attribute :name, :string do
allow_nil? false
end
attribute :description, :string do
allow_nil? false
end
end

relationships do
has_many :users, MyApp.Accounts.User
many_to_many :permissions, MyApp.Accounts.Role do
through MyApp.Accounts.RolePermission
source_attribute_on_join_resource :role_id
destination_attribute_on_join_resource :permission_id
end
end
end
A Permission resource:
defmodule MyApp.Accounts.Permission do
use Ash.Resource,
data_layer: AshPostgres.DataLayer

postgres do
table "permissiones"
repo MyApp.Repo
end

attributes do
uuid_primary_key :id
attribute :name, :string do
allow_nil? false
end
attribute :description, :string do
allow_nil? false
end
end

relationships do
many_to_many :roles, MyApp.Accounts.Permission do
through MyApp.Accounts.RolePermission
source_attribute_on_join_resource :permission_id
destination_attribute_on_join_resource :role_id
end
many_to_many :users, MyApp.Accounts.Permission do
through MyApp.Accounts.UserPermission
source_attribute_on_join_resource :permission_id
destination_attribute_on_join_resource :user_id
end
end
end
defmodule MyApp.Accounts.Permission do
use Ash.Resource,
data_layer: AshPostgres.DataLayer

postgres do
table "permissiones"
repo MyApp.Repo
end

attributes do
uuid_primary_key :id
attribute :name, :string do
allow_nil? false
end
attribute :description, :string do
allow_nil? false
end
end

relationships do
many_to_many :roles, MyApp.Accounts.Permission do
through MyApp.Accounts.RolePermission
source_attribute_on_join_resource :permission_id
destination_attribute_on_join_resource :role_id
end
many_to_many :users, MyApp.Accounts.Permission do
through MyApp.Accounts.UserPermission
source_attribute_on_join_resource :permission_id
destination_attribute_on_join_resource :user_id
end
end
end
ZachDaniel
ZachDaniel•2y ago
👋 this support channel is archived, would you mind posting on the Elixir Forum instead please?
AlecStewart1#1125
AlecStewart1#1125OP•2y ago
Oh whoops! Of course!

Did you find this page helpful?