{"id":31629468,"url":"https://github.com/enjaku4/rabarber","last_synced_at":"2026-04-08T13:31:20.563Z","repository":{"id":212752455,"uuid":"606374483","full_name":"enjaku4/rabarber","owner":"enjaku4","description":"Simple role-based authorization library for Ruby on Rails","archived":false,"fork":false,"pushed_at":"2026-04-01T16:38:05.000Z","size":577,"stargazers_count":184,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"main","last_synced_at":"2026-04-02T01:13:08.324Z","etag":null,"topics":["authorization","gem","multitenancy","rails","rbac","roles","ruby","ruby-on-rails","security"],"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/enjaku4.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE.txt","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"github":["enjaku4"],"patreon":null,"open_collective":null,"ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"lfx_crowdfunding":null,"polar":null,"buy_me_a_coffee":null,"thanks_dev":null,"custom":null}},"created_at":"2023-02-25T09:46:56.000Z","updated_at":"2026-04-01T16:33:37.000Z","dependencies_parsed_at":"2026-04-01T20:03:58.680Z","dependency_job_id":null,"html_url":"https://github.com/enjaku4/rabarber","commit_stats":null,"previous_names":["enjaku4/rabarber","brownboxdev/rabarber"],"tags_count":43,"template":false,"template_full_name":null,"purl":"pkg:github/enjaku4/rabarber","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/enjaku4%2Frabarber","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/enjaku4%2Frabarber/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/enjaku4%2Frabarber/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/enjaku4%2Frabarber/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/enjaku4","download_url":"https://codeload.github.com/enjaku4/rabarber/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/enjaku4%2Frabarber/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31558380,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-08T10:21:54.569Z","status":"ssl_error","status_checked_at":"2026-04-08T10:21:38.171Z","response_time":54,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: 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":["authorization","gem","multitenancy","rails","rbac","roles","ruby","ruby-on-rails","security"],"created_at":"2025-10-06T21:01:50.499Z","updated_at":"2026-04-08T13:31:20.556Z","avatar_url":"https://github.com/enjaku4.png","language":"Ruby","funding_links":["https://github.com/sponsors/enjaku4"],"categories":["Gems","Ruby"],"sub_categories":["Articles"],"readme":"# Rabarber: Simple Role-Based Authorization for Rails\n\n[![Gem Version](https://badge.fury.io/rb/rabarber.svg)](http://badge.fury.io/rb/rabarber)\n[![Downloads](https://img.shields.io/gem/dt/rabarber.svg)](https://rubygems.org/gems/rabarber)\n[![Github Actions badge](https://github.com/enjaku4/rabarber/actions/workflows/ci.yml/badge.svg)](https://github.com/enjaku4/rabarber/actions/workflows/ci.yml)\n[![License](https://img.shields.io/github/license/enjaku4/rabarber.svg)](LICENSE)\n\nRabarber is a role-based authorization library for Ruby on Rails. It provides a set of tools for managing user roles and defining access rules, with support for multi-tenancy through context.\n\n**Example of Usage:**\n\nConsider a CRM system where users with different roles have distinct access levels. For instance, the role `accountant` can access and manage invoice data, while the role `analyst` can only view it. Here's how you'd define that with Rabarber:\n\n```rb\nclass InvoicesController \u003c ApplicationController\n  grant_access roles: :accountant\n\n  grant_access action: :index, roles: :analyst\n  def index\n    # Accessible to accountant and analyst\n  end\n\n  def update\n    # Accessible to accountant only\n  end\nend\n```\n\n## Table of Contents\n\n**Gem Usage:**\n  - [Installation](#installation)\n  - [Configuration](#configuration)\n  - [User Role Methods](#user-role-methods)\n  - [Direct Role Management](#direct-role-management)\n  - [Authorization](#authorization)\n  - [Dynamic Authorization Rules](#dynamic-authorization-rules)\n  - [When Unauthorized](#when-unauthorized)\n  - [Context / Multi-tenancy](#context--multi-tenancy)\n  - [View Helpers](#view-helpers)\n\n**Community Resources:**\n  - [Getting Help and Contributing](#getting-help-and-contributing)\n  - [License](#license)\n  - [Code of Conduct](#code-of-conduct)\n  - [Old Versions](#old-versions)\n\n## Installation\n\nAdd Rabarber to your Gemfile:\n\n```rb\ngem \"rabarber\"\n```\n\nInstall the gem:\n\n```shell\nbundle install\n```\n\nGenerate the migration to store roles (replace `users` with your table name if different):\n\n```shell\n# For standard integer IDs\nrails generate rabarber:roles users\n\n# For UUID primary keys\nrails generate rabarber:roles users --uuid\n```\n\nRun the migration:\n\n```shell\nrails db:migrate\n```\n\n## Configuration\n\nCreate an initializer to customize Rabarber's behavior (optional):\n\n```rb\nRabarber.configure do |config|\n  # Enable/disable role caching (default: true)\n  config.cache_enabled = true\n  # Method to access current user (default: :current_user)\n  config.current_user_method = :current_user\n  # User model name (default: \"User\")\n  config.user_model_name = \"User\"\nend\n```\n\nRoles are cached by default for better performance. Clear the cache manually when needed:\n\n```rb\nRabarber::Cache.clear\n```\n\n## User Role Methods\n\nYour user model is automatically augmented with role management methods:\n\n### Role Assignment\n\n```rb\n# Assign roles (creates roles if they don't exist)\nuser.assign_roles(:admin, :manager)\n\n# Assign only existing roles (don't create new ones)\nuser.assign_roles(:accountant, :manager, create_new: false)\n\n# Revoke specific roles\nuser.revoke_roles(:admin, :manager)\n\n# Revoke all roles\nuser.revoke_all_roles\n```\n\nAll role assignment and revocation methods return the list of roles currently assigned to the user.\n\n### Role Queries\n\n```rb\n# Check if user has any of the specified roles\nuser.has_role?(:accountant, :manager)\n\n# Get user's roles in the global context\nuser.roles\n\n# Get all user's roles grouped by context\nuser.all_roles\n\n# Get users with any of the specified roles\nUser.with_role(:admin, :manager)\n```\n\n## Direct Role Management\n\nYou can also directly manage roles available in the application:\n\n```rb\n# Create a new role\nRabarber.create_role(:admin)\n# =\u003e true if created, false if already exists\n\n# Rename a role\nRabarber.rename_role(:admin, :administrator)\n# =\u003e true if renamed, false if new name exists or role is assigned\n\n# Force rename even if role is assigned\nRabarber.rename_role(:admin, :administrator, force: true)\n\n# Remove a role\nRabarber.delete_role(:admin)\n# =\u003e true if deleted, false if role is assigned\n\n# Force deletion even if role is assigned\nRabarber.delete_role(:admin, force: true)\n\n# List available roles in the global context\nRabarber.roles\n\n# List all available roles grouped by context\nRabarber.all_roles\n```\n\n## Authorization\n\n### Setup\n\nInclude `Rabarber::Authorization` module in your controllers and configure authorization:\n\n```rb\nclass ApplicationController \u003c ActionController::Base\n  include Rabarber::Authorization\n\n  # Enable authorization check for all actions in all controllers by default\n  with_authorization\nend\n```\n\nYou can also enable authorization checks selectively. Both `with_authorization` and `skip_authorization` work exactly the same as Rails' `before_action` and `skip_before_action` methods:\n\n```rb\nclass TicketsController \u003c ApplicationController\n  # Skip authorization for specific actions\n  skip_authorization only: [:index, :show]\nend\n\nclass InvoicesController \u003c ApplicationController\n  # Enable authorization for all actions except index\n  with_authorization except: [:index]\nend\n```\n\nAuthorization requires an authenticated user. Rabarber will raise an error if no user is found via the configured `current_user_method`. Ensure authentication happens before authorization.\n\n### Authorization Rules\n\nRabarber follows a deny-by-default principle: if no `grant_access` rule is defined for an action or controller, access is denied to everyone.\n\nDefine authorization rules using `grant_access`:\n\n```rb\nclass TicketsController \u003c ApplicationController\n  # Controller-wide access\n  grant_access roles: :admin\n\n  # Action-specific access\n  grant_access action: :index, roles: [:manager, :support]\n  def index\n    # Accessible to admin, manager, and support roles\n  end\n\n  def destroy\n    # Accessible to admin role only\n  end\nend\n```\n\nAuthorization rules are additive - they combine across inheritance chains and when defined multiple times for the same action or controller:\n\n```rb\nclass BaseController \u003c ApplicationController\n  # Admin can access everything\n  grant_access roles: :admin\nend\n\nclass InvoicesController \u003c BaseController\n  # Accountant can also access InvoicesController (along with admin)\n  grant_access roles: :accountant\n\n  grant_access action: :index, roles: :manager\n  grant_access action: :index, roles: :supervisor\n  def index\n    # Index is accessible to admin, accountant, manager, and supervisor\n  end\nend\n```\n\nIt's possible to omit roles to allow unrestricted access:\n\n```rb\nclass UnrestrictedController \u003c ApplicationController\n  # Allow all users to access all actions\n  grant_access\nend\n\nclass MixedController \u003c ApplicationController\n  # Unrestricted index action\n  grant_access action: :index\n  def index\n    # Accessible to all users\n  end\n\n  # Restricted show action\n  grant_access action: :show, roles: :member\n  def show\n    # Accessible to members only\n  end\nend\n```\n\n## Dynamic Authorization Rules\n\nFor more complex scenarios, Rabarber supports dynamic authorization rules:\n\n```rb\nclass OrdersController \u003c ApplicationController\n  grant_access roles: :manager, unless: -\u003e { current_user.suspended? }\n\n  grant_access action: :show, roles: :client, if: :user_company_matches_order?\n  def show\n    # ...\n  end\n\n  private\n\n  def user_company_matches_order?\n    current_user.company == Order.find(params[:id]).company\n  end\nend\n```\n\nYou can pass a dynamic rule as an `if` or `unless` argument, which can be a symbol or a proc. Symbols refer to instance methods, and procs are evaluated in the controller at request time.\n\nDynamic rules can also be used without roles at all, allowing you to define custom logic or even delegate to custom policy objects:\n\n```rb\nclass InvoicesController \u003c ApplicationController\n  grant_access action: :update, unless: -\u003e { invoice.period_closed? }\n  def update\n    # ...\n  end\n\n  grant_access action: :destroy, if: :destroy_allowed?\n  def destroy\n    # ...\n  end\n\n  private\n\n  def destroy_allowed?\n    InvoicePolicy.new(current_user).destroy?(invoice)\n  end\n\n  def invoice\n    @invoice ||= Invoice.find(params[:id])\n  end\nend\n```\n\n## When Unauthorized\n\nBy default, when unauthorized, Rabarber will redirect back (HTML format) or return 403 (other formats). You can override `when_unauthorized` method to customize unauthorized access behavior:\n\n```rb\nclass ApplicationController \u003c ActionController::Base\n  include Rabarber::Authorization\n\n  with_authorization\n\n  private\n\n  def when_unauthorized\n    # Custom behavior to hide existence of protected resources\n    head :not_found\n  end\nend\n```\n\nThe `when_unauthorized` method can be overridden in any controller to provide controller-specific unauthorized access handling.\n\n## Context / Multi-tenancy\n\nRabarber supports multi-tenancy through its context feature. All Rabarber methods accept a `context` parameter, allowing you to work with roles within specific scopes. By default, context is `nil`, meaning roles are global. Context can also be an instance of an `ActiveRecord` model or a class.\n\nFor example, in a project management app, you might want users to have different roles in different projects - someone could be an `owner` in one project but just a `member` in another.\n\n### Contextual Role Assignment and Queries\n\n```rb\n# Assign roles within a specific model instance\nuser.assign_roles(:owner, context: project)\nuser.assign_roles(:member, context: project)\n\n# Assign roles within a model class\nuser.assign_roles(:admin, context: Project)\n\n# Check contextual roles\nuser.has_role?(:owner, context: project)\nuser.has_role?(:admin, context: Project)\n\n# Revoke roles\nuser.revoke_roles(:owner, context: project)\n\n# Get user roles\nuser.roles(context: project)\n\n# Get users with a role\nUser.with_role(:member, context: project)\n```\n\n### Contextual Role Management\n\n```rb\n# Create a new role within a context\nRabarber.create_role(:admin, context: Project)\n\n# Rename a role within a context\nRabarber.rename_role(:admin, :owner, context: project)\n\n# Remove a contextual role\nRabarber.delete_role(:admin, context: project)\n\n# List available roles within a specific context\nRabarber.roles(context: project)\n```\n\n### Contextual Authorization\n\nIn authorization rules, in addition to specifying context explicitly, you can also provide a proc or a symbol (similar to dynamic rules):\n\n```rb\nclass ProjectsController \u003c ApplicationController\n  grant_access roles: :admin, context: Project\n\n  # Method-based context resolution\n  grant_access action: :show, roles: :member, context: :current_project\n  def show\n    # Accessible to Project admin and members of the current project\n  end\n\n  # Proc-based context resolution\n  grant_access action: :update, roles: :owner, context: -\u003e { Project.find(params[:id]) }\n  def update\n    # Accessible to Project admin and owner of the current project\n  end\n\n  private\n\n  def current_project\n    @current_project ||= Project.find(params[:id])\n  end\nend\n```\n\n### Orphaned Contextual Roles\n\nWhen a context object is deleted from your database, its associated roles become orphaned and ignored by Rabarber.\n\nTo clean up orphaned roles, use:\n\n```rb\nRabarber.prune\n```\n\n### Context Migrations\n\nWhen you rename or remove models used as contexts, you need to update Rabarber's stored context data accordingly. Use these irreversible data migrations:\n\n```rb\n# Rename a context class (e.g., when you rename your Ticket model to Task)\nmigrate_authorization_context!(\"Ticket\", \"Task\")\n\n# Remove context data (e.g., when you delete the Ticket model entirely)\ndelete_authorization_context!(\"Ticket\")\n```\n\n## View Helpers\n\nInclude view helpers in your application:\n\n```rb\nmodule ApplicationHelper\n  include Rabarber::Helpers\nend\n```\n\nUse conditional rendering based on roles:\n\n```erb\n\u003c%= visible_to(:admin, :manager) do %\u003e\n  \u003cdiv class=\"admin-panel\"\u003e\n    \u003c!-- Admin/Manager content --\u003e\n  \u003c/div\u003e\n\u003c% end %\u003e\n\n\u003c%= hidden_from(:guest) do %\u003e\n  \u003cdiv class=\"member-content\"\u003e\n    \u003c!-- Content hidden from guests --\u003e\n  \u003c/div\u003e\n\u003c% end %\u003e\n\n\u003c%= visible_to(:owner, context: @project) do %\u003e\n  \u003cdiv class=\"project-owner-panel\"\u003e\n    \u003c!-- Content visible to project owners --\u003e\n  \u003c/div\u003e\n\u003c% end %\u003e\n```\n\n## Getting Help and Contributing\n\n### Getting Help\nHave a question or need assistance? Open a discussion in the [discussions section](https://github.com/enjaku4/rabarber/discussions) for:\n- Usage questions\n- Implementation guidance\n- Feature suggestions\n\n### Reporting Issues\nFound a bug? Please [create an issue](https://github.com/enjaku4/rabarber/issues) with:\n- A clear description of the problem\n- Steps to reproduce the issue\n- Your environment details (Rails version, Ruby version, etc.)\n\n### Contributing Code\nReady to contribute? You can:\n- Fix bugs by submitting pull requests\n- Improve documentation\n- Add new features (please discuss first in the [discussions section](https://github.com/enjaku4/rabarber/discussions))\n\nBefore contributing, please read the [contributing guidelines](https://github.com/enjaku4/rabarber/blob/main/CONTRIBUTING.md)\n\n## License\n\nThe gem is available as open source under the terms of the [MIT License](https://github.com/enjaku4/rabarber/blob/main/LICENSE.txt).\n\n## Code of Conduct\n\nEveryone interacting in the Rabarber project is expected to follow the [code of conduct](https://github.com/enjaku4/rabarber/blob/main/CODE_OF_CONDUCT.md).\n\n## Old Versions\n\nOnly the latest major version is supported. Older versions are obsolete and not maintained, but their READMEs are available here for reference:\n\n[v5.x.x](https://github.com/enjaku4/rabarber/blob/12a23858e974f5cc0a4ebddfc18bdf84171dd554/README.md) | [v4.x.x](https://github.com/enjaku4/rabarber/blob/9353e70281971154d5acd70693620197a132c543/README.md) | [v3.x.x](https://github.com/enjaku4/rabarber/blob/3bb273de7e342004abc7ef07fa4d0a9a3ce3e249/README.md)\n | [v2.x.x](https://github.com/enjaku4/rabarber/blob/875b357ea949404ddc3645ad66eddea7ed4e2ee4/README.md) | [v1.x.x](https://github.com/enjaku4/rabarber/blob/b81428429404e197d70317b763e7b2a21e02c296/README.md)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fenjaku4%2Frabarber","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fenjaku4%2Frabarber","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fenjaku4%2Frabarber/lists"}