{"id":13483069,"url":"https://github.com/chaps-io/access-granted","last_synced_at":"2026-04-02T02:08:07.797Z","repository":{"id":11143264,"uuid":"13509989","full_name":"chaps-io/access-granted","owner":"chaps-io","description":"Multi-role and whitelist based authorization gem for Rails (and not only Rails!)","archived":false,"fork":false,"pushed_at":"2024-05-08T19:19:54.000Z","size":156,"stargazers_count":778,"open_issues_count":9,"forks_count":40,"subscribers_count":19,"default_branch":"master","last_synced_at":"2026-03-28T15:44:55.959Z","etag":null,"topics":["access-control","authorization","cancan","permissions","rails","ruby"],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/chaps-io.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2013-10-11T21:21:35.000Z","updated_at":"2026-03-22T18:20:42.000Z","dependencies_parsed_at":"2024-06-18T17:12:33.878Z","dependency_job_id":null,"html_url":"https://github.com/chaps-io/access-granted","commit_stats":{"total_commits":166,"total_committers":17,"mean_commits":9.764705882352942,"dds":"0.13855421686746983","last_synced_commit":"f4ade30ff1f68b25991d7cfd90212819555132b7"},"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/chaps-io/access-granted","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chaps-io%2Faccess-granted","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chaps-io%2Faccess-granted/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chaps-io%2Faccess-granted/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chaps-io%2Faccess-granted/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/chaps-io","download_url":"https://codeload.github.com/chaps-io/access-granted/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chaps-io%2Faccess-granted/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31229270,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-31T09:14:28.471Z","status":"ssl_error","status_checked_at":"2026-03-31T09:14:19.506Z","response_time":111,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["access-control","authorization","cancan","permissions","rails","ruby"],"created_at":"2024-07-31T17:01:07.991Z","updated_at":"2026-04-02T02:08:07.592Z","avatar_url":"https://github.com/chaps-io.png","language":"Ruby","readme":"# AccessGranted [![Code Climate](https://codeclimate.com/github/pokonski/access-granted.png)](https://codeclimate.com/github/pokonski/access-granted)\n\nAccessGranted is a multi-role and whitelist based authorization gem for Rails. And it's lightweight (~300 lines of code)!\n\n\n## Installation\n\nAdd the gem to your gemfile:\n\n```ruby\ngem 'access-granted', '~\u003e 1.3'\n```\nRun the bundle command to install it. Then run the generator:\n\n    rails generate access_granted:policy\n\n### Supported Ruby versions\n\nBecause it has **zero** runtime dependencies it is guaranteed to work on all supported MRI Ruby versions, see CI to check the up to date list.\nIt might and probably is working on Rubinius and JRuby but we are no longer testing against those.\n\n## Summary\n\nAccessGranted is meant as a replacement for CanCan to solve major problems:\n\n1. Performance\n\n    On average AccessGranted is **20 times faster** in resolving identical permissions and takes less memory.\n    See [benchmarks](https://github.com/chaps-io/access-granted/blob/master/benchmarks).\n\n2. Roles\n\n    Adds support for roles, so no more `if`s and `else`s in your Policy file. This makes it extremely easy to maintain and    read the code.\n\n3. Whitelists\n\n    This means that you define what the user can do, which results in clean, readable policies regardless of application complexity.\n    You don't have to worry about juggling `can`s and `cannot`s in a very convoluted way!\n\n    _Note_: `cannot` is still available, but has a very specifc use. See [Usage](#usage) below.\n\n4. Framework agnostic\n\n    Permissions can work on basically any object and AccessGranted is framework-agnostic,\n    but it has Rails support out of the box. :)\n    It does not depend on any libraries, pure and clean Ruby code. Guaranteed to always work,\n    even when software around changes.\n\n## Usage\n\nRoles are defined using blocks (or by passing custom classes to keep things tidy).\n\n**Order of the roles is VERY important**, because they are being traversed in top-to-bottom order.\nAt the top you must have an admin or some other important role giving the user top permissions, and as you go down you define less-privileged roles.\n\n**I recommend starting your adventure by reading the [wiki page on how to start with Access Granted](https://github.com/chaps-io/access-granted/wiki/Role-based-authorization-in-Rails), where I demonstrate its abilities on a real life example.**\n\n### Defining an access policy\n\nLet's start with a complete example of what can be achieved:\n\n```ruby\n# app/policies/access_policy.rb\n\nclass AccessPolicy\n  include AccessGranted::Policy\n\n  def configure\n    # The most important admin role, gets checked first\n    role :admin, { is_admin: true } do\n      can :manage, Post\n      can :manage, Comment\n    end\n\n    # Less privileged moderator role\n    role :moderator, proc {|u| u.moderator? } do\n      can [:update, :destroy], Post\n      can :update, User\n    end\n\n    # The basic role. Applies to every user.\n    role :member do\n      can :create, Post\n\n      can [:update, :destroy], Post do |post, user|\n        post.author == user \u0026\u0026 post.comments.empty?\n      end\n    end\n  end\nend\n```\n\n#### Defining roles\n\nEach `role` method accepts the name of the role you're creating and an optional matcher.\nMatchers are used to check if the user belongs to that role and if the permissions inside should be executed against it.\n\nThe simplest role can be defined as follows:\n\n```ruby\nrole :member do\n  can :read, Post\n  can :create, Post\nend\n```\n\nThis role will allow everyone (since we didn't supply a matcher) to read and create posts.\n\nBut now we want to let admins delete those posts.\nIn this case we can create a new role above the `:member` to add more permissions for the admin:\n\n```ruby\nrole :admin, { is_admin: true } do\n  can :destroy, Post\nend\n\nrole :member do\n  can :read, Post\n  can :create, Post\nend\n```\n\nThe `{ is_admin: true }` hash is compared with the user's attributes to see if the role should be applied to it.\nSo, if the user has an attribute `is_admin` set to `true`, then the role will be applied to it.\n\n**Note:** you can use more keys in the hash to check many attributes at once.\n\n#### Hash conditions\n\nHashes can be used as matchers to check if an action is permitted.\nFor example, we may allow users to only see published posts, like this:\n\n```ruby\nrole :member do\n  can :read, Post, { published: true }\nend\n```\n\n#### Block conditions\n\nSometimes you may need to dynamically check for ownership or other conditions,\nthis can be done using a block condition in `can` method, like so:\n\n```ruby\nrole :member do\n  can :update, Post do |post, user|\n    post.author_id == user.id\n  end\nend\n```\n\nWhen the given block evaluates to `true`, then `user` is allowed to update the post.\n\n#### Roles in order of importance\n\nAdditionally, we can allow admins to update **all** posts despite them not being authors like so:\n\n\n```ruby\nrole :admin, { is_admin: true } do\n  can :update, Post\nend\n\nrole :member do\n  can :update, Post do |post, user|\n    post.author_id == user.id\n  end\nend\n```\n\nAs stated before: **`:admin` role takes precedence over `:member`** role, so when AccessGranted sees that admin can update all posts, it stops looking at the less important roles.\n\nThat way you can keep a tidy and readable policy file which is basically human readable.\n\n### Usage with Rails\n\nAccessGranted comes with a set of helpers available in Ruby on Rails apps:\n\n#### Authorizing controller actions\n\n```ruby\nclass PostsController\n  def show\n    @post = Post.find(params[:id])\n    authorize! :read, @post\n  end\n\n  def create\n    authorize! :create, Post\n    # (...)\n  end\nend\n```\n\n`authorize!` throws an exception when `current_user` doesn't have a given permission.\nYou can rescue from it using `rescue_from`:\n\n```ruby\nclass ApplicationController \u003c ActionController::Base\n  rescue_from \"AccessGranted::AccessDenied\" do |exception|\n    redirect_to root_path, alert: \"You don't have permission to access this page.\"\n  end\nend\n```\n\nYou can also extract the action and subject which raised the error,\nif you want to handle authorization errors differently for some cases:\n```ruby\n  rescue_from \"AccessGranted::AccessDenied\" do |exception|\n    status = case exception.action\n      when :read # invocation like `authorize! :read, @something`\n        403\n      else\n        404\n      end\n\n    body = case exception.subject\n      when Post # invocation like `authorize! @some_action, Post`\n        \"failed to access a post\"\n      else\n        \"failed to access something else\"\n      end\n  end\n```\n\nYou can also have a custom exception message while authorizing a request.\nThis message will be associated with the exception object thrown.\n\n```ruby\nclass PostsController\n  def show\n    @post = Post.find(params[:id])\n    authorize! :read, @post, 'You do not have access to this post'\n    render json: { post: @post }\n  rescue AccessGranted::AccessDenied =\u003e e\n    render json: { error: e.message }, status: :forbidden\n  end\nend\n```\n\n#### Checking permissions in controllers\n\nTo check if the user has a permission to perform an action, use the `can?` and `cannot?` methods.\n\n**Example:**\n\n```ruby\nclass UsersController\n  def update\n    # (...)\n\n    # only admins can elevate users to moderator status\n\n    if can? :make_moderator, @user\n      @user.moderator = params[:user][:moderator]\n    end\n\n    # (...)\n  end\nend\n```\n\n#### Checking permissions in views\n\nUsually you don't want to show \"Create\" buttons for people who can't create something.\nYou can hide any part of the page from users without permissions like this:\n\n```erb\n# app/views/categories/index.html.erb\n\n\u003c% if can? :create, Category %\u003e\n  \u003c%= link_to \"Create new category\", new_category_path %\u003e\n\u003c% end %\u003e\n```\n\n#### Customizing policy\n\nBy default, AccessGranted adds this method to your controllers:\n\n```ruby\n  def current_policy\n    @current_policy ||= ::AccessPolicy.new(current_user)\n  end\n```\n\nIf you have a different policy class or if your user is not stored in the `current_user` variable, then you can override it in any controller and modify the logic as you please.\n\nYou can even have different policies for different controllers!\n\n### Usage with pure Ruby\n\nInitialize the Policy class:\n\n```ruby\npolicy = AccessPolicy.new(current_user)\n```\n\nCheck the ability to do something:\n\nwith `can?`:\n\n```ruby\npolicy.can?(:create, Post) #=\u003e true\npolicy.can?(:update, @post) #=\u003e false\n```\n\nor with `cannot?`:\n\n```ruby\npolicy.cannot?(:create, Post) #=\u003e false\npolicy.cannot?(:update, @post) #=\u003e true\n```\n\n## Common examples\n\n### Extracting roles to separate files\n\nLet's say your app is getting bigger and more complex. This means your policy file is also getting longer.\n\nBelow you can see an extracted `:member` role:\n\n```ruby\nclass AccessPolicy\n  include AccessGranted::Policy\n\n  def configure\n    role :administrator, is_admin: true do\n      can :manage, User\n    end\n\n    role :member, MemberRole, -\u003e { |user| !u.guest? }\n  end\nend\n\n```\n\nAnd roles should look like this:\n\n```ruby\n# app/roles/member_role.rb\n\nclass MemberRole \u003c AccessGranted::Role\n  def configure\n    can :create, Post\n    can :destroy, Post do |post, user|\n      post.author == user\n    end\n  end\nend\n```\n\n## Compatibility with CanCan\n\nThis gem has been created as a replacement for CanCan and therefore it requires minimum work to switch.\n\n### Main differences\n\n1. AccessGranted does not extend ActiveRecord in any way, so it does not have the `accessible_by?`\n   method which could be used for querying objects available to current user.\n   This was very complex and only worked with permissions defined using hash conditions, so\n   I decided to not implement this functionality as it was mostly ignored by CanCan users.\n\n2. Both `can?`/`cannot?` and `authorize!` methods work in Rails controllers and views, just like in CanCan.\n   The only change you have to make is to replace all `can? :manage, Class` with the exact action to check against.\n   `can :manage` is still available for **defining** permissions and serves as a shortcut for defining `:create`, `:read`, `:update`, `:destroy` all in one line.\n\n3. Syntax for defining permissions in the AccessPolicy file (Ability in CanCan) is exactly the same,\n   with roles added on top. See [Usage](#usage) above.\n\n\n## Contributing\n\n1. Fork it\n2. Create your feature branch (`git checkout -b my-new-feature`)\n3. Commit your changes (`git commit -am 'Add some feature'`)\n4. Push to the branch (`git push origin my-new-feature`)\n5. Create new pull request\n","funding_links":[],"categories":["Ruby","Authorization"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchaps-io%2Faccess-granted","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fchaps-io%2Faccess-granted","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchaps-io%2Faccess-granted/lists"}