User group membership with roles?

I’m trying to create a many_to_many relationship between User (Ash Authentication) and Group (or a team, etc) with a role. I’m looking to use this to track which users belong to which groups and to enforce policies.

I’ve created a join resource GroupMember with a role attribute and belongs_to relationships but for the life of me I can’t figure out manage_relationships with setting the :role on the join.

Anyone have an example I could learn from?

defmodule GroupMember do
   attributes do
    uuid_primary_key :id

    attribute :role, :atom,  constraints [one_of: [:admin, :moderator, :member]]
  end

  relationships do
    belongs_to :group, Group
    belongs_to :user, User
   end
end
defmodule User do
...
  relationships do
    many_to_many :groups, Group do
      through GroupMember
    end
  end

 actions do
    update :join_group do
      require_atomic? false

      argument :group_id, :uuid do
        allow_nil? false
      end

      argument :role, Hestia.Accounts.Role do
        allow_nil? false
        default Hestia.Accounts.Role.default()
      end

      change manage_relationship(???)
    end
end
...
 end

Since you aren’t taking a :map argument, you’ll need to call Ash.Changeset.manage_relationship in a change yourself. Additionally, this can be made much simpler by managing the join relationship directly. It can be done when managing the many_to_many its just a bit more annoying.

  1. Give the join relationship a better name. A has_many join relationship is always created, called <relationship_name>_join_assoc for many_to_many. It exists to make the underlying logic work, but is given an ugly name to encourage you to name it yourself if you want to use it for something. You don’t need to here, but this also allows you to define the join relationship has_many and customize its behavior if you need to.
    many_to_many :groups, Group do
      through GroupMember
      join_relationship :group_members
    end
  1. now, manage the join relationship directly
change fn changeset, context ->
  Ash.Changeset.manage_relationship(
    changeset,
    :group_members, 
    [%{
      role: changeset.arguments[:role], 
      group_id:changeset.arguments[:group_id]
    }],
    type: :create
  )
end
1 Like

Meaning just call the :create action on my join resource GroupMember?

I meant by doing what I was showing in #2, instead of manage_relationship(:groups) doing manage_relationship(:group_members)

Do you recommend a different approach? (My code is in the earliest phases and could easily be changed)

:thinking: I’m confused. I made a recommendation on the approach here:

That is managing group_members as opposed to groups which is what I was suggesting.

Didn’t mean confuse or befuddle. I was being open to alternate approaches like passing in the group instead of maps or tracking roles differently.

Ah, understood :smile:

I think the way you are doing it is perfectly reasonable :+1: