{"id":13879295,"url":"https://github.com/rails-engine/role_core","last_synced_at":"2025-05-15T04:05:50.660Z","repository":{"id":30351416,"uuid":"124671458","full_name":"rails-engine/role_core","owner":"rails-engine","description":"🔐A Rails engine providing essential industry of Role-based access control.","archived":false,"fork":false,"pushed_at":"2025-02-08T15:31:18.000Z","size":206,"stargazers_count":302,"open_issues_count":4,"forks_count":25,"subscribers_count":9,"default_branch":"master","last_synced_at":"2025-04-14T05:55:50.573Z","etag":null,"topics":["access-control","cancancan","pundit","rails","rails-engine","rbac","role","role-based-access-control","rolify"],"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/rails-engine.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"MIT-LICENSE","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":"2018-03-10T16:02:19.000Z","updated_at":"2025-03-31T15:58:27.000Z","dependencies_parsed_at":"2024-04-18T17:52:01.781Z","dependency_job_id":"337fc30a-f5e0-4f03-85b0-0f177ae8411e","html_url":"https://github.com/rails-engine/role_core","commit_stats":{"total_commits":112,"total_committers":4,"mean_commits":28.0,"dds":0.0357142857142857,"last_synced_commit":"e00a54b22ad962b34856e8b38ead1fbbe65faf03"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rails-engine%2Frole_core","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rails-engine%2Frole_core/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rails-engine%2Frole_core/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rails-engine%2Frole_core/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rails-engine","download_url":"https://codeload.github.com/rails-engine/role_core/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254270646,"owners_count":22042859,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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","cancancan","pundit","rails","rails-engine","rbac","role","role-based-access-control","rolify"],"created_at":"2024-08-06T08:02:16.490Z","updated_at":"2025-05-15T04:05:45.639Z","avatar_url":"https://github.com/rails-engine.png","language":"Ruby","funding_links":[],"categories":["Ruby"],"sub_categories":[],"readme":"RoleCore\n====\n\nRoleCore is a Rails engine which could provide essential industry of Role-based access control.\n\n## Demo\n\nThe dummy app shows a simple multiple roles with CanCanCan integration including a management UI.\n\n\u003cimg width=\"550\" alt=\"RoleCore dummy preview\" src=\"https://user-images.githubusercontent.com/5518/37262401-e6c9d604-25dd-11e8-849d-7f7d923d5f18.png\"\u003e\n\nClone the repository.\n\n```sh\n$ git clone https://github.com/rails-engine/role_core.git\n```\n\nChange directory\n\n```sh\n$ cd role_core\n```\n\nRun bundler\n\n```sh\n$ bundle install\n```\n\nPreparing database\n\n```sh\n$ bin/rails db:migrate\n```\n\nStart the Rails server\n\n```sh\n$ bin/rails s\n```\n\nOpen your browser, and visit `http://localhost:3000`\n\n## What's does the RoleCore do\n\n### The role model\n\nThe essence of RBAC is the role, despite your application, there are many possibilities: single-role, multi-roles, extendable-role and the role may associate to different kinds of resources (e.g: users and groups)\n\nRoleCore provides an essential definition of Role,\nyou have to add association to adapt to your application,\nfor example:\n\n- single-role: adding `one-to-many` association between Role and User\n- multi-roles: adding `many-to-many` association between Role and User\n- extendable-role: adding a self-association to Role\n- polymorphic-asscociated-role: consider using polymorphic association technique\n\nAlthough it's not out-of-box, but it will give you fully flexibility to suit your needs.\n\n### Permissions definition\n\nRoleCore provides a DSL (which inspired by [Redmine](https://github.com/redmine/redmine/blob/master/lib/redmine.rb#L76-L186)) that allows you define permissions for your application.\n\nEmpowered by virtual model technique,\nthese permissions your defined can be persisted through serialization,\nand can be used with OO-style, for example: `role.permissions.project.create?`\n\nThere also support permission groups, and groups support nesting.\n\n**You don't need to any migration when you changing definitions.**\n\nI18n is supported too.\n\nIn fact, the essence of permissions is Hash, keys are permissions, and values are booleans. so computing of permissions with many roles, can be understood as computing of Hashes.\n\n### Management UI\n\nBuilding a management UI is difficult,\nbut virtual model technique will translates permissions to a virtual model's (a class that conforms to ActiveModel) attributes,\nand groups will translates to nested virtual models,\nthat means you can use all Rails view helpers including the mighty form builder,\nand can benefit to Strong Parameter.\n\nThe dummy app shows that rendering a permission list [only about 20 lines](https://github.com/rails-engine/role_core/blob/master/test/dummy/app/views/roles/_permissions.html.erb).\n\nIf your application is API-only, you can simply dumping the role's permissions to JSON, and can still be benefit to StrongParameter.\n\n### Checking permission\n\nRoleCore **DOES NOT** handle the authentication or authorization directly,\nyou have to integrate with CanCanCan, Pundit or other solutions by yourself.\n\nRoleCore can be working with CanCanCan, Pundit easily and happily.\n\n## Installation\n\nAdd this line to your Gemfile:\n\n```ruby\ngem \"role_core\"\n```\n\nif your Rails \u003c 7 or met `visit_Psych_Nodes_Alias': Unknown alias: redis (Psych::BadAlias)`, add this line:\n\n```ruby\ngem \"psych\", \"~\u003e 3.3\"\n```\n\nThen execute:\n\n```sh\n$ bundle\n```\n\nCopy migrations\n\n```sh\n$ bin/rails role_core:install:migrations\n```\n\nThen do migrate\n\n```sh\n$ bin/rails db:migrate\n```\n\nRun config generator\n\n```sh\n$ bin/rails g role_core:config\n```\n\nRun model generator\n\n```sh\n$ bin/rails g role_core:model\n```\n\n## Getting start\n\n### Define permissions\n\nPermissions are defined in `config/initializers/role_core.rb`,\nchecking it to know how to define permissions.\n\n(If you want to define permissions in runtime, check [Dynamic Permissions](https://github.com/rails-engine/role_core#dynamic-permissions))\n\nIn addition, there also includes a directive about how to integrate with CanCanCan.\n\n#### I18n\n\nCheck `config/locales/role_core.en.yml`\n\n### Hook application\n\nIn order to obtain maximum customizability, you need to hooking up role(s) to your user model by yourself.\n\n#### User who has single role\n\n##### Create `one-to-many` relationship between Role and User\n\nGenerate `one-to-many` migration, adding `role_id` to `User` model\n\n```sh\n$ bin/rails g migration AddRoleToUsers role:references\n```\n\nThen do migrate\n\n```sh\n$ bin/rails db:migrate\n```\n\nDeclare `a User belongs to a Role` association\n\n```ruby\nclass User \u003c ApplicationRecord\n  belongs_to :role\n\n  # ...\nend\n```\n\nDeclare `a Role has many Users` association\n\n```ruby\nclass Role \u003c RoleCore::Role\n  has_many :users\nend\n```\n\n##### Checking permission\n\nPermissions you've defined will translate to a virtual model (a Class which implemented ActiveModel interface),\n`permission` would be an attribute, `group` would be a nested virtual model (like ActiveRecord's `has_one` association).\n\nSo you can simply check permission like:\n\n```ruby\nuser.role.permissions.read_public?\nuser.role.permissions.project.read? # `project` is a `group`\n```\n\nFor better usage, you may delegate the `permissions` from `Role` model to `User`:\n\n```ruby\nclass User \u003c ApplicationRecord\n  belongs_to :role\n\n  delegate :permissions, to: :role\n\n  # ...\nend\n```\n\nThen you can\n\n```ruby\nuser.permissions.read_public?\nuser.permissions.project.read?\n```\n\n_Keep in mind: fetching `role` will made a SQL query, you may need eager loading to avoid N+1 problem in some cases._\n\n#### User who has multiple roles\n\n##### Create `many-to-many` relationship between Role and User\n\nGenerate a `many-to-many` intervening model\n\n```sh\n$ bin/rails g model RoleAssignment user:references role:references\n```\n\nThen do migrate\n\n```sh\n$ bin/rails db:migrate\n```\n\nDeclare `a User has many Roles through RoleAssignments` association\n\n```ruby\nclass User \u003c ApplicationRecord\n  has_many :role_assignments, dependent: :destroy\n  has_many :roles, through: :role_assignments\n\n  # ...\nend\n```\n\nDeclare `a Role has many Users through RoleAssignments` association\n\n```ruby\nclass Role \u003c RoleCore::Role\n  has_many :role_assignments, dependent: :destroy\n  has_many :users, through: :role_assignments\nend\n```\n\n##### Check permission\n\nPermissions you've defined will translate to a virtual model (a Class which implemented ActiveModel interface),\n`permission` would be an attribute, `group` would be a nested virtual model (like ActiveRecord's `has_one` association).\n\nSo you can simply check permission like:\n\n```ruby\nuser.roles.any? { |role| role.permissions.read_public? }\nuser.roles.any? { |role| role.permissions.project.read? } # `project` is a `group`\n```\n\nFor better usage, you could declare a `can?` helper method:\n\n```ruby\nclass User \u003c ApplicationRecord\n  has_many :role_assignments, dependent: :destroy\n  has_many :roles, through: :role_assignments\n\n  def can?(\u0026block)\n    roles.map(\u0026:permissions).any?(\u0026block)\n  end\n\n  # ...\nend\n```\n\nThen you can\n\n```ruby\nuser.can? { |permissions| permissions.read_public? }\nuser.can? { |permissions| permissions.project.read? }\n```\n\n_Keep in mind: fetching `roles` will made a SQL query, you may need eager loading to avoid N+1 problem in some cases._\n\n### Integrate with Pundit\n\nJust call permissions' method (see `checking permission` above) in Pundit's policy.\n\ne.g:\n\n```ruby\nclass PostPolicy\n  attr_reader :user, :post\n\n  def initialize(user, post)\n    @user = user\n    @post = post\n  end\n\n  def update?\n    user.permissions.post.update?\n  end\n\n  def update_my_own?\n    return true if user.permissions.post.update?\n    return unless user.permissions.post.update_my_own?\n    post.author == user\n  end\nend\n```\n\n### Integrate with CanCanCan\n\nOpen `config/initializers/role_core.rb`, uncomment CanCanCan integration codes and follows samples to define permissions for CanCanCan\n\nOpen your User model:\n\n- For a user who has single role:\n\n  Add a delegate to User model:\n\n  ```ruby\n    delegate :computed_permissions, to: :role\n  ```\n\n- For a user who has multiple roles:\n\n  Add a `computed_permissions` public method to User model:\n\n  ```ruby\n  def computed_permissions\n    roles.map(\u0026:computed_permissions).reduce(RoleCore::ComputedPermissions.new, \u0026:concat)\n  end\n  ```\n\nOpen `app/models/ability.rb`, add `user.computed_permissions.call(self, user)` to `initialize` method.\n\n```ruby\nclass Ability\n  include CanCan::Ability\n\n  def initialize(user)\n    # Apply RoleCole managing permissions\n    user.computed_permissions.call(self, user)\n\n    # You still can add other permissions\n    can :read_public, :all\n  end\nend\n```\n\nYou can check dummy app for better understanding.\n\n### Management UI\n\nSee [RolesController in dummy app](https://github.com/rails-engine/role_core/blob/master/test/dummy/app/controllers/roles_controller.rb)\nand relates [view](https://github.com/rails-engine/role_core/blob/master/test/dummy/app/views/roles/_form.html.erb) for details.\n\n## Hacking\n\nRoleCore is a Rails engine, and following [the official best practice](http://guides.rubyonrails.org/engines.html#overriding-models-and-controllers), so you can  extend RoleCore by the article suggests.\n\n### Integrate to existing role model\n\nFor some reason, you want to use RoleCore's ability and keeping use your own role model, e.g: integrate with [rolify](https://github.com/RolifyCommunity/rolify).\n\nYou can archive this goal by:\n\n- Modify the migration file which RoleCore generated, changing the role table name\n- Add `include RoleCore::Concerns::Models::Role` to your role model\n\n*Note: If you want another column name or there's no name in your role model, you need to lookup `RoleCore::Concerns::Models::Role` source code, copy and modify to fit your needs*\n\n### Dynamic permissions\n\nBy design, RoleCore is for static permissions, but dynamic permissions is easy to support.\n\nSee [example](test/dummy/app/models/team.rb) in dummy app and relates [view](test/dummy/app/views/teams/_permissions.html.erb) for details.\n\n## Contributing\n\nBug report or pull request are welcome.\n\n### Make a pull request\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\nPlease write unit test with your code if necessary.\n\n## License\n\nThe gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frails-engine%2Frole_core","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frails-engine%2Frole_core","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frails-engine%2Frole_core/lists"}