Skip to content

Instantly share code, notes, and snippets.

@tbcooney
Last active November 25, 2021 12:21
Show Gist options
  • Save tbcooney/aca326ab31d2d3ba820ec092be00a7cb to your computer and use it in GitHub Desktop.
Save tbcooney/aca326ab31d2d3ba820ec092be00a7cb to your computer and use it in GitHub Desktop.

Here is a plain-ruby approach for a flexible and expendable authorization solution to authorize actions that a user can? perform within an account or organization based on their access_level.

class CreateMembers < ActiveRecord::Migration[6.0]
  def change
    create_table :members do |t|
      t.references :account, null: false, foreign_key: true
      t.integer :access_level
      t.references :user, foreign_key: true

      t.timestamps
    end
    add_index :members, :access_level
  end
end

In a single class Ability, we have defined a number of class methods to set the permissions. For instance, for a Manager:

# app/models/ability.rb
def manager_rules
  @manager_rules ||= staff_rules + [
    :edit_post
  ]
end

Ability has defined an allowed? method that check whether a permission exists for a certain object, based on the abilities that you've defined for that class (such as above for a Manager). To make use of a similar syntax as the often-used Cancan authorization gem, you can use a similar can? method with Ability:

# app/models/user.rb
def can?(object, action, subject)
  Ability.allowed?(self, action, subject)
end

Then all we have to do is check the permission wherever we need it. For instance to check whether someone is allowed to edit a Post:

def destroy
  return access_denied! unless can?(current_user, :edit_post, @post)

  @post.update(post_params)
  redirect_to @post
end
@stevepolitodesign
Copy link

I really like how simple this is. I do have a few questions:

  1. What do you think about updating the can? method to be called on an instance of a user?
# app/models/user.rb
def can?(action, subject)
  Ability.allowed?(self, action, subject)
end

current_user.can?(:edit_post, @post)

I think it reads a little better this way, but maybe I'm overlooking why you had an object parameter 🤔

  1. Is there a way to set default values?
  2. Is there a way to dynamically set an ability? For example, you may want someone with a low access level to still be able to edit their post, but not others.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment