Search code examples
ruby-on-railsauthorizationcancancancancan

Rails + CanCan: Disallow User from Joining a Group if Already a Member


I have a Rails app, using CanCan for authorization. I have Users that can have Memberships in Groups. I would like to create a CanCan rule that authorizes a user to create a group membership only if they are not already a member of that group. So far I have been unable to figure out the magic:

class Group < ActiveRecord::Base
  has_many :memberships
  has_many :members, :through => :memberships
end

class Membership < ActiveRecord::Base
  belongs_to :member, class_name: 'User'
  belongs_to :group
end

class User < ActiveRecord::Base
  has_many :groups, through: :memberships
  has_many :memberships, foreign_key: 'member_id'
end

class Ability
  include CanCan::Ability

  def initialize(user)

    user ||= User.new

    # One of my sad attempts...
    can :create, Membership do |membership|
      !membership.group.has_member(user)
    end

  end

end

<% if can? :create, Membership %>
   View code for creating memberships...
<% end %>

Your assistnace is appreciated. Also, I do understand that CanCanCan is the successor of CanCan. A CanCanCan answer will be fine.


Solution

  • CanCan blocks only work on model instances (see this wiki page) and right now your can? sends the model class, not an instance.

    In order for this to work, you need to pass an instance of Membership. You can do something like this (assuming @group is the group the user wants to join)

    <% if can? :create, Membership.new(group: @group) %>
       View code for creating memberships...
    <% end %>
    

    I would recommend, however, that you put the ability on the group. In my opinion, it is a more direct approach.

    ability.rb

    class Ability
      include CanCan::Ability
    
      def initialize(user)
        user ||= User.new
    
        can :join, Group do |group|
          !group.members.include?(user)
        end
      end
    end
    

    View

    <% if can? :join, @group %>
       View code for creating memberships...
    <% end %>