### Install and Run VitePress Documentation Source: https://github.com/cancancommunity/cancancan/blob/develop/CONTRIBUTING.md Commands to install dependencies, run the development server, and build the documentation site for CanCanCan. ```bash npm install npm run dev # build for production, resulting in a static site in docs/.vitepress/dist npm run build ``` -------------------------------- ### Complete Authorized Controller Example Source: https://github.com/cancancommunity/cancancan/blob/develop/docs/controller_helpers.md A comprehensive example of a controller using `load_and_authorize_resource` for all standard RESTful actions, including resource loading, authorization, and parameter handling. ```ruby class ArticlesController < ApplicationController load_and_authorize_resource def index # @articles are already loaded...see details in later chapter end def show # the @article to show is already loaded and authorized end def create # the @article to create is already loaded, authorized, and params set from article_params @article.create end def edit # the @article to edit is already loaded and authorized end def update # the @article to update is already loaded and authorized @article.update(article_params) end def destroy # the @article to destroy is already loaded and authorized @article.destroy end protected def article_params params.require(:article).permit(:body) end end ``` -------------------------------- ### Basic Controller Setup with CanCan Source: https://github.com/cancancommunity/cancancan/wiki/Controller-Authorization-Example Include `load_and_authorize_resource` at the top of your controller to automatically set up before filters for resource loading and authorization. ```ruby class ProjectsController < ApplicationController load_and_authorize_resource # ... end ``` -------------------------------- ### Controller Setup for Nested Resources Source: https://github.com/cancancommunity/cancancan/wiki/Nested-Resources Controller setup for loading and authorizing nested resources, specifically when dealing with a 'group' and resources through that group. ```ruby class UsersController < ApplicationController load_and_authorize_resource :group load_and_authorize_resource through: :group end ``` -------------------------------- ### Single Ability File Example Source: https://github.com/cancancommunity/cancancan/blob/develop/docs/split_ability.md A basic CanCanCan ability file defining permissions for users, books, and comments. ```ruby # app/models/ability.rb class Ability include CanCan::Ability def initialize(user) can :edit, User, id: user.id can :read, Book, published: true can :edit, Book, user_id: user.id can :manage, Comment, user_id: user.id end end ``` -------------------------------- ### Install Appraisals for Development Source: https://github.com/cancancommunity/cancancan/blob/develop/README.md Install necessary gems for testing CanCanCan against multiple Rails versions and model adapters. ```ruby bundle install bundle exec appraisal install ``` -------------------------------- ### Custom Resource Finding Source: https://github.com/cancancommunity/cancancan/blob/develop/docs/changing_defaults.md Use the `find_by` option with `load_resource` to fetch resources using attributes other than the default `id`. This example uses `permalink`. ```ruby load_resource find_by: :permalink # will use find_by!(permalink: params[:id]) authorize_resource ``` -------------------------------- ### Define Ability in Ruby Source: https://github.com/cancancommunity/cancancan/blob/develop/docs/internationalization.md Example of defining a 'create' ability for the 'Article' model in Ruby. This is a prerequisite for defining specific translations. ```ruby models/ability.rb ... can :create, Article ... ``` -------------------------------- ### Role Inheritance Setup in Ability.rb Source: https://github.com/cancancommunity/cancancan/wiki/Separate-Role-Model Implements role inheritance by calling role-specific methods within the Ability initializer. Assumes a User#roles method returning an array of roles. ```ruby class Ability include CanCan::Ability def initialize(user) @user = user || User.new # for guest @user.roles.each { |role| send(role.name.downcase) } if @user.roles.size == 0 can :read, :all #for guest without roles end end def manager can :manage, Employee end def admin manager can :manage, Bill end end ``` -------------------------------- ### Define Custom Adapter Dependencies with Appraisals Source: https://github.com/cancancommunity/cancancan/blob/develop/docs/model_adapter.md Use Appraisals to define gem dependencies for your custom model adapter, ensuring compatibility with specific versions. Run `bundle exec appraisal install` to install these dependencies. ```ruby appraise 'cancancan_custom_adapter' do gem 'activerecord', '~> 5.0.2', require: 'active_record' gemfile.platforms :jruby do gem 'jdbc-postgres' end gemfile.platforms :ruby, :mswin, :mingw do gem 'pg', '~> 0.21' end end ``` -------------------------------- ### Example of Scope Usage for Complex Conditions Source: https://github.com/cancancommunity/cancancan/wiki/Defining-Abilities-with-Blocks Illustrates the general structure for using scopes with block conditions, suitable for complex scenarios requiring joins. ```ruby can [:ability], Model, Model.scope_to_select_on_index_action do |model_instance| model_instance.condition_to_evaluate_for_new_create_edit_update_destroy end ``` -------------------------------- ### Setup Database Permissions from Controllers Source: https://github.com/cancancommunity/cancancan/blob/develop/docs/abilities_in_database.md This function populates the `Permission` table by inspecting controllers and their actions. It defines 'manage' permissions for controllers and specific actions like 'read', 'create', 'update', and 'delete'. Requires a `Permission` model with specific fields. ```ruby def setup_actions_controllers_db write_permission("all", "manage", "Everything", "All operations", true) controllers = Dir.new("#{Rails.root}/app/controllers").entries controllers.each do |controller| if controller =~ /_controller/ foo_bar = controller.camelize.gsub(".rb", "").constantize.new end end # You can change ApplicationController for a super-class used by your restricted controllers ApplicationController.subclasses.each do |controller| if controller.respond_to?(:permission) klass, description = controller.permission write_permission(klass, "manage", description, "All operations") controller.action_methods.each do |action| if action.to_s.index("_callback").nil? action_desc, cancan_action = eval_cancan_action(action) write_permission(klass, cancan_action, description, action_desc) end end end end end def eval_cancan_action(action) case action.to_s when "index", "show", "search" cancan_action = "read" action_desc = I18n.t :read when "create", "new" cancan_action = "create" action_desc = I18n.t :create when "edit", "update" cancan_action = "update" action_desc = I18n.t :edit when "delete", "destroy" cancan_action = "delete" action_desc = I18n.t :delete else cancan_action = action.to_s action_desc = "Other: " << cancan_action end return action_desc, cancan_action end def write_permission(class_name, cancan_action, name, description, force_id_1 = false) permission = Permission.find(:first, :conditions => ["subject_class = ? and action = ?", class_name, cancan_action]) if not permission permission = Permission.new permission.id = 1 if force_id_1 permission.subject_class = class_name permission.action = cancan_action permission.name = name permission.description = description permission.save else permission.name = name permission.description = description permission.save end end ``` -------------------------------- ### Run Specific Appraisal Set Source: https://github.com/cancancommunity/cancancan/blob/develop/README.md Execute a specific appraisal test set, for example, using SQLite. ```ruby DB='sqlite' bundle exec appraisal activerecord_5.2.2 rake ``` -------------------------------- ### Deny Specific Action with `cannot` Source: https://github.com/cancancommunity/cancancan/blob/develop/docs/cannot.md Use `cannot` after a general `can` call to restrict specific actions. This example allows all actions on `Project` except for `destroy`. ```ruby can :manage, Project cannot :destroy, Project ``` -------------------------------- ### Initialize Ability with IP Address Source: https://github.com/cancancommunity/cancancan/blob/develop/docs/accessing_request_data.md Modify the Ability initializer to accept and utilize request-specific data, such as an IP address, for permission checks. This example denies comment creation from a denylisted IP. ```ruby class Ability include CanCan::Ability def initialize(user, ip_address=nil) can :create, Comment unless DENYLIST_IPS.include? ip_address end end ``` -------------------------------- ### Checking Aliased Actions Source: https://github.com/cancancommunity/cancancan/blob/develop/docs/define_check_abilities.md Demonstrates how to check for aliased actions. For example, if 'can :read, Article' is defined, 'can? :show, @article' will also return true. ```ruby can? :show, @article ``` -------------------------------- ### Define User Abilities with CanCanCan Source: https://github.com/cancancommunity/cancancan/blob/develop/README.md Define user permissions within an Ability class. This example shows basic read permissions for posts, with additional rules for logged-in users and administrators. ```ruby class Ability include CanCan::Ability def initialize(user) can :read, Post, public: true return unless user.present? # additional permissions for logged in users (they can read their own posts) can :read, Post, user: user return unless user.admin? # additional permissions for administrators can :read, Post end end ``` -------------------------------- ### Define and Check Article Edit Ability Source: https://github.com/cancancommunity/cancancan/blob/develop/docs/define_check_abilities.md Example of defining a permission for a user to edit their own article and then checking that ability. Assumes an `Article` model with a `user` association. ```ruby class Article belongs_to :user end ``` ```ruby class Ability include CanCan::Ability def initialize(user) can :update, Article, user: user end end ``` ```ruby @article = Article.find(params[:id]) can? :update, @article # => true ``` -------------------------------- ### Define Custom Action in Ability Source: https://github.com/cancancommunity/cancancan/wiki/Custom-Actions Define a custom action like `:assign_roles` in your `ability.rb` file. This example restricts the action to users who are administrators. ```ruby can :assign_roles, User if user.admin? ``` -------------------------------- ### Define Custom Ability in Ruby Source: https://github.com/cancancommunity/cancancan/blob/develop/docs/internationalization.md Example of defining a custom 'vote' ability for the 'Article' model in Ruby. This demonstrates how to handle translations for non-standard abilities. ```ruby models/ability.rb ... can :vote, Article ... ``` -------------------------------- ### Conditionally Check Authorization with :if Source: https://github.com/cancancommunity/cancancan/wiki/Ensure-Authorization Use the `:if` option with `check_authorization` to perform the check only when a specific method returns true. This example ensures authorization only for the admin subdomain. ```ruby class ApplicationController < ActionController::Base check_authorization :if => :admin_subdomain? private def admin_subdomain? request.subdomain == "admin" end end ``` -------------------------------- ### Catch-all Rule Simplification Source: https://github.com/cancancommunity/cancancan/wiki/Rules-compression A catch-all rule (e.g., `can :read, Book`) eliminates all previous and subsequent rules of the same type. This example shows how a catch-all rule simplifies a more complex set of rules. ```ruby can :read, Book, author_id: user.id cannot :read, Book, private: true can :read, Book can :read, Book, id: 1 cannot :read, Book, private: true ``` ```ruby can :read, Book cannot :read, Book, private: true ``` -------------------------------- ### Define Abilities Incrementally by User Role Source: https://github.com/cancancommunity/cancancan/blob/develop/docs/define_abilities_best_practices.md Define abilities by starting with general permissions and incrementally adding more specific permissions based on user roles. This approach promotes readability and reduces the risk of granting incorrect permissions. ```ruby class Ability include CanCan::Ability def initialize(user) can :read, Post # start by defining rules for all users, also not logged ones return unless user.present? can :manage, Post, user_id: user.id # if the user is logged in can manage it's own posts can :create, Comment # logged in users can also create comments return unless user.manager? can :manage, Comment # like managing all comments in the website return unless user.admin? can :manage, :all # finally we give all remaining permissions only to the admins end end ``` -------------------------------- ### Traversing Associations in Hash of Conditions Source: https://github.com/cancancommunity/cancancan/blob/develop/docs/hash_of_conditions.md An example of traversing multiple associations ('service', 'account', 'user') to define a 'manage' ability on 'Part' based on deeply nested conditions. ```ruby can :manage, Part, service: { account: { user: user } } ``` -------------------------------- ### Left Join SQL Strategy Example Source: https://github.com/cancancommunity/cancancan/blob/develop/docs/sql_strategies.md This is the default strategy, which uses a DISTINCT clause that may impact performance. It joins tables to filter records based on conditions. ```sql SELECT DISTINCT "articles".* FROM "articles" LEFT OUTER JOIN "mentions" ON "mentions"."article_id" = "articles"."id" LEFT OUTER JOIN "users" ON "users"."id" = "mentions"."user_id" WHERE "users"."name" = 'pippo' ``` -------------------------------- ### Authorize Actions in Controllers Source: https://github.com/cancancommunity/cancancan/blob/develop/README.md Use the `authorize!` method in controllers to raise an exception if the user lacks permission for a specific action. This example demonstrates authorizing the 'read' action for a post. ```ruby def show @post = Post.find(params[:id]) authorize! :read, @post end ``` -------------------------------- ### Ability Class Role Permissions Source: https://github.com/cancancommunity/cancancan/wiki/Role-Based-Authorization Define permissions in the Ability class based on the user's role. This example grants different management capabilities based on moderator, admin, or superadmin roles. ```ruby class Ability include CanCan::Ability def initialize(user) if user.role? :moderator can :manage, Post end if user.role? :admin can :manage, ForumThread end if user.role? :superadmin can :manage, Forum end end end ``` -------------------------------- ### Test Model Adapter Class Identification Source: https://github.com/cancancommunity/cancancan/blob/develop/docs/model_adapter.md Write RSpec tests to ensure your model adapter correctly identifies its associated model class using the `for_class?` method. This example checks if the `MongoidAdapter` is for `MongoidProject` but not for a generic `Object`. ```ruby RSpec.describe CanCan::ModelAdapters::MongoidAdapter do it 'is for only Mongoid classes' do expect(CanCan::ModelAdapters::MongoidAdapter).not_to be_for_class(Object) expect(CanCan::ModelAdapters::MongoidAdapter).to be_for_class(MongoidProject) end it 'finds record' do project = MongoidProject.create expect(CanCan::ModelAdapters::MongoidAdapter.find(MongoidProject, project.id)).to eq(project) end it "should return the correct records based on the defined ability" do @ability.can :read, MongoidProject, :title => "Sir" sir = MongoidProject.create(:title => 'Sir') lord = MongoidProject.create(:title => 'Lord') MongoidProject.accessible_by(@ability, :read).entries.should == [sir] end end ``` -------------------------------- ### Combine Loading and Authorization with load_and_authorize_resource Source: https://github.com/cancancommunity/cancancan/blob/develop/docs/controller_helpers.md Use `load_and_authorize_resource` as a shortcut to both load the resource and authorize actions. This is the most concise way to integrate these features. ```ruby class ArticlesController < ApplicationController load_and_authorize_resource def edit; end end ``` -------------------------------- ### New and Create Actions Resource Initialization Source: https://github.com/cancancommunity/cancancan/wiki/Authorizing-controller-actions Builder actions like `new` and `create` initialize the resource with attributes from `can` definitions, ensuring authorization passes for the `new` action. User-provided params will override these initial attributes. ```ruby can :manage, Product, :discontinued => false ``` ```ruby @product = Product.new(:discontinued => false) ``` -------------------------------- ### Run All Appraisal Tests Source: https://github.com/cancancommunity/cancancan/blob/develop/README.md Execute all appraisal test suites, similar to the CI environment. ```ruby bundle exec appraisal rake ``` -------------------------------- ### Nest Resources Through a Method Source: https://github.com/cancancommunity/cancancan/wiki/Nested-Resources Load and authorize resources through a method, such as `current_user`. This is useful for resources associated directly with the current user. ```ruby class ProjectsController < ApplicationController load_and_authorize_resource through: :current_user end ``` -------------------------------- ### User Model with Has Many Through Association Source: https://github.com/cancancommunity/cancancan/blob/develop/docs/nested_resources.md Defines a User model with a has_many :through association to Group via GroupsUsers. This setup is common for many-to-many relationships. ```ruby class User < ActiveRecord::Base has_many :groups_users has_many :groups, through: :groups_users end ``` -------------------------------- ### SQL Query with OR Conditions Source: https://github.com/cancancommunity/cancancan/blob/develop/docs/define_abilities_best_practices.md Illustrates the resulting SQL query when combining hash conditions, showing how different criteria are OR'd together for flexibility. ```sql SELECT `articles`.* FROM `articles` WHERE `articles`.`is_published` = 1 OR ( `articles`.`author_id` = 97 AND `articles`.`is_published` = 0 ) ``` -------------------------------- ### Subquery SQL Strategy Example Source: https://github.com/cancancommunity/cancancan/blob/develop/docs/sql_strategies.md This strategy can help remove the DISTINCT clause, potentially improving performance. It uses a subquery to filter records. ```sql SELECT "articles".* FROM "articles" WHERE "articles"."id" IN (SELECT "articles"."id" FROM "articles" LEFT OUTER JOIN "mentions" ON "mentions"."article_id" = "articles"."id" LEFT OUTER JOIN "users" ON "users"."id" = "legacy_mentions"."user_id" WHERE "users"."name" = 'pippo') ``` -------------------------------- ### Show, Edit, Update, Destroy Actions Resource Loading Source: https://github.com/cancancommunity/cancancan/wiki/Authorizing-controller-actions These member actions automatically fetch the record directly using `Product.find(params[:id])`. ```ruby def show # @product automatically set to Product.find(params[:id]) end ``` -------------------------------- ### Get All Permitted Attributes Source: https://github.com/cancancommunity/cancancan/blob/develop/docs/accessible_attributes.md Retrieve a list of all attributes that are permitted for a specific action on an instance. This is useful for dynamic form generation or parameter filtering. ```ruby current_ability.permitted_attributes(:read, @user) ``` -------------------------------- ### Hash of Conditions for Negative Match (nil) Source: https://github.com/cancancommunity/cancancan/blob/develop/docs/hash_of_conditions.md Demonstrates how to use 'nil' in a hash of conditions for a negative match, allowing 'Project' read permission for projects without any members. ```ruby # Can read projects that don't have any members. can :read, Project, members: { id: nil } ``` -------------------------------- ### Define Ability Syntax Source: https://github.com/cancancommunity/cancancan/blob/develop/docs/define_check_abilities.md Use `can` to define who can perform certain actions on subjects with optional conditions. This is the core method for setting up permissions. ```ruby can actions, subjects, conditions ``` -------------------------------- ### Define User, Assignment, and Role Models Source: https://github.com/cancancommunity/cancancan/wiki/Separate-Role-Model Sets up the ActiveRecord models for User, Assignment, and Role to establish a many-to-many association. ```ruby class User < ActiveRecord::Base has_many :assignments has_many :roles, :through => :assignments end class Assignment < ActiveRecord::Base belongs_to :user belongs_to :role end class Role < ActiveRecord::Base has_many :assignments has_many :users, :through => :assignments end ``` -------------------------------- ### Specify Actions with :only and :except Source: https://github.com/cancancommunity/cancancan/wiki/Authorizing-controller-actions Use the `:only` and `:except` options to specify which controller actions `load_and_authorize_resource` should affect, similar to `before_action`. ```ruby load_and_authorize_resource :only => [:index, :show] ``` -------------------------------- ### Authorize Custom Action in Controller Source: https://github.com/cancancommunity/cancancan/wiki/Custom-Actions In your controller, use `authorize!` to enforce permissions for custom actions. This example authorizes the `:assign_roles` action on `@user` only if the `assign_roles` parameter is present. ```ruby authorize! :assign_roles, @user if params[:user][:assign_roles] ``` -------------------------------- ### Define Abilities with Database Permissions Source: https://github.com/cancancommunity/cancancan/blob/develop/docs/abilities_in_database.md Use this approach to check for the existence of matching permissions in the database for a given action, subject class, and optional subject ID. ```ruby class Ability include CanCan::Ability def initialize(user) can do |action, subject_class, subject| user.permissions.where(action: aliases_for_action(action)).any? do |permission| permission.subject_class == subject_class.to_s && (subject.nil? || permission.subject_id.nil? || permission.subject_id == subject.id) end end end end ``` -------------------------------- ### Inspect SQL for Index Action Source: https://github.com/cancancommunity/cancancan/wiki/Debugging-Abilities Examine the SQL generated by `accessible_by` to understand why records might not be fetched as expected. Helps in diagnosing complex filtering issues. ```ruby Project.accessible_by(ability).to_sql ``` -------------------------------- ### Determine User Role in Ability Class Source: https://github.com/cancancommunity/cancancan/wiki/Role-Based-Authorization Check the user's role directly in your Ability class to grant permissions. This example grants all abilities to users with the 'admin' role. ```ruby can :manage, :all if user.role == "admin" ``` -------------------------------- ### Separate Resource Loading and Authorization Source: https://github.com/cancancommunity/cancancan/wiki/Authorizing-controller-actions Alternatively, use `load_resource` and `authorize_resource` separately if you need distinct control over resource loading and authorization. ```ruby class ProductsController < ActionController::Base load_resource authorize_resource end ``` -------------------------------- ### Inverse Scope for `cannot` Declaration Source: https://github.com/cancancommunity/cancancan/wiki/Defining-Abilities-with-Blocks When using `cannot` with a scope, the scope must represent the inverse condition of what you are denying. For example, to deny reading discontinued products, the scope should fetch non-discontinued ones. ```ruby cannot :read, Product, Product.where(:discontinued => false) do |product| product.discontinued? end ``` -------------------------------- ### Derive Model Name from Controller Source: https://github.com/cancancommunity/cancancan/blob/develop/docs/abilities_in_database.md This helper method, intended for use within `ApplicationController`, derives the corresponding model name from the controller's name. For example, `UsersController` will return `User`. ```ruby class ApplicationController < ActionController::Base ... protected # Derive the model name from the controller. def self.permission return name = controller_name.classify.constantize end end ``` -------------------------------- ### Automatic Resource Loading and Authorization Source: https://github.com/cancancommunity/cancancan/wiki/Authorizing-controller-actions Employ `load_and_authorize_resource` in a controller to automatically load the resource into an instance variable and authorize it for all actions. ```ruby class ProductsController < ActionController::Base load_and_authorize_resource end ``` -------------------------------- ### Default Ability File Structure Source: https://github.com/cancancommunity/cancancan/blob/develop/docs/installation.md The generated abilities file provides a basic structure for defining user capabilities. ```ruby # /app/models/ability.rb class Ability include CanCan::Ability def initialize(user) end end ``` -------------------------------- ### Controller Actions with Explicit Authorization (Replaced by CanCan) Source: https://github.com/cancancommunity/cancancan/wiki/Controller-Authorization-Example These actions demonstrate the explicit authorization logic that `load_and_authorize_resource` handles automatically. This code is shown for illustrative purposes and is not needed when `load_and_authorize_resource` is used. ```ruby class ProjectsController < ApplicationController def index authorize! :index, Project @projects = Project.accessible_by(current_ability) end def show @project = Project.find(params[:id]) authorize! :show, @project end def new @project = Project.new current_ability.attributes_for(:new, Project).each do |key, value| @project.send("#{key}=", value) end @project.attributes = params[:project] authorize! :new, @project end def create @project = Project.new current_ability.attributes_for(:create, Project).each do |key, value| @project.send("#{key}=", value) end @project.attributes = params[:project] authorize! :create, @project end def edit @project = Project.find(params[:id]) authorize! :edit, @project end def update @project = Project.find(params[:id]) authorize! :update, @project end def destroy @project = Project.find(params[:id]) authorize! :destroy, @project end def some_other_action if params[:id] @project = Project.find(params[:id]) else @projects = Project.accessible_by(current_ability) end authorize!(:some_other_action, @project || Project) end end ``` -------------------------------- ### Hash of Conditions for Project Read Permission Source: https://github.com/cancancommunity/cancancan/blob/develop/docs/hash_of_conditions.md Demonstrates restricting the 'read' ability on a 'Project' to active projects owned by the current user, using direct attribute matching. ```ruby can :read, Project, active: true, user_id: user.id ``` -------------------------------- ### Request Test with Logged-in User Source: https://github.com/cancancommunity/cancancan/blob/develop/docs/testing.md Perform request-level authorization tests by logging in a user with appropriate permissions before making a GET request. This verifies that the application correctly handles authorized access. ```ruby user = User.create!(admin: true) article = Article.create! login user, as: :user # in devise get article_path(article) expect(response).to have_http_status(:ok) ``` -------------------------------- ### Configure CanCanCan for FriendlyId Source: https://github.com/cancancommunity/cancancan/blob/develop/docs/friendly_id.md Add this initializer to `config/initializers/cancancan.rb` to enable CanCanCan to use FriendlyId's `find` method when available. This avoids the need for manual `find_by :slug` calls. ```ruby if defined?(CanCanCan) class Object def metaclass class << self; self; end end end module CanCan module ModelAdapters class ActiveRecordAdapter < AbstractAdapter @@friendly_support = {} def self.find(model_class, id) klass = model_class.metaclass.ancestors.include?(ActiveRecord::Associations::CollectionProxy) ? model_class.klass : model_class @@friendly_support[klass]||=klass.metaclass.ancestors.include?(FriendlyId) @@friendly_support[klass] == true ? model_class.friendly.find(id) : model_class.find(id) end end end end end ``` -------------------------------- ### Removing Redundant Cannot Rule at Start Source: https://github.com/cancancommunity/cancancan/wiki/Rules-compression If the first rule is a 'cannot' rule that is later overridden by a 'can' rule of the same type, the initial 'cannot' rule can be removed. This simplifies the rule set by eliminating unnecessary restrictions. ```ruby cannot :read, Book can :read, Book, author_id: user.id ``` ```ruby can :read, Book, author_id: user.id ``` -------------------------------- ### Check Task Creation Permission Through Project Source: https://github.com/cancancommunity/cancancan/wiki/Nested-Resources Verify if a user has permission to create a new task by building the task through its associated project. This ensures the task is correctly linked to a project before authorization. ```ruby can? :create, @project.tasks.build ``` -------------------------------- ### Load and Authorize Nested Resources Source: https://github.com/cancancommunity/cancancan/wiki/Nested-Resources Configure the controller to load and authorize both the parent project and the nested task. The task is loaded through the project's association. ```ruby class TasksController < ApplicationController load_and_authorize_resource :project load_and_authorize_resource :task, through: :project end ``` -------------------------------- ### Check Abilities in ERB Views Source: https://github.com/cancancommunity/cancancan/blob/develop/README.md Use the `can?` helper method in ERB templates to conditionally render content based on user permissions. This example shows how to display a 'View' link only if the user can read the post. ```erb <% if can? :read, @post %> <%= link_to "View", @post %> <% end %> ``` -------------------------------- ### Adding Custom Action to Abilities Source: https://github.com/cancancommunity/cancancan/blob/develop/docs/define_check_abilities.md Illustrates how to add a custom action like ':translate' to existing abilities. This allows for more granular control over permissions as new features are added. ```ruby can :read, Article, published: true return unless user.present? can [:read, :update, :translate], Article, user: user return unless user.admin? can :manage, :all ``` -------------------------------- ### Conditionally Check Authorization with :unless Source: https://github.com/cancancommunity/cancancan/wiki/Ensure-Authorization Use the `:unless` option with `check_authorization` to skip the check based on a method's return value. This example skips the check for Devise controllers using the `devise_controller?` method. ```ruby class ApplicationController < ActionController::Base check_authorization :unless => :devise_controller? end ``` -------------------------------- ### Simplified Multiple Article Permissions Source: https://github.com/cancancommunity/cancancan/blob/develop/docs/define_check_abilities.md A more concise version of defining multiple article permissions, using array syntax for actions and combining conditions where possible. This simplifies the ability definition. ```ruby can :read, Article, published: true return unless user.present? can [:read, :update], Article, user: user return unless user.admin? can [:read, :update], Article ``` -------------------------------- ### Check Custom Action in View Source: https://github.com/cancancommunity/cancancan/wiki/Custom-Actions Use the `can?` helper in your ERB views to conditionally render elements based on the user's permission for a custom action. This example shows how to display role checkboxes only if the user can `:assign_roles` to the `@user`. ```erb <% if can? :assign_roles, @user %> <% end %> ``` -------------------------------- ### Ability Class Role Checks Source: https://github.com/cancancommunity/cancancan/blob/develop/docs/role_based_authorization.md Use the User model's role checking method within the Ability class to grant permissions based on roles. This example shows how a superadmin can manage all classes, while a moderator manages only one. ```ruby # in Ability#initialize if user.role? :moderator can :manage, Post end if user.role? :admin can :manage, ForumThread end if user.role? :superadmin can :manage, Forum end ``` -------------------------------- ### Define Multiple Article Permissions Source: https://github.com/cancancommunity/cancancan/blob/develop/docs/define_check_abilities.md Defines permissions for reading published articles, reading/updating own articles for logged-in users, and full access for administrators. This demonstrates increasing permissions. ```ruby can :read, Article, published: true return unless user.present? can :read, Article, user: user can :update, Article, user: user return unless user.admin? can :read, Article can :update, Article ``` -------------------------------- ### Inspect SQL for Index Action Filtering Source: https://github.com/cancancommunity/cancancan/blob/develop/docs/debugging.md Examine the SQL generated by `accessible_by` to understand why specific records might not be fetched correctly. This helps in diagnosing complex filtering issues. ```ruby Project.accessible_by(ability).to_sql # see what the generated SQL looks like to help determine why it's not fetching the records you want ``` -------------------------------- ### Authorize All Actions by Default Source: https://github.com/cancancommunity/cancancan/wiki/Authorizing-controller-actions By default, `load_and_authorize_resource` applies to all actions in a controller. The action name is passed during authorization. ```ruby class ProductsController < ActionController::Base load_and_authorize_resource def discontinue # Automatically does the following: # @product = Product.find(params[:id]) # authorize! :discontinue, @product end end ``` -------------------------------- ### Controller Test: Log in User and Test Access Source: https://github.com/cancancommunity/cancancan/wiki/Testing-Abilities Test controller authorization by logging in a user with specific permissions and then making a request. Verify that the expected template is rendered. ```ruby user = User.create!(admin: true) session[:user_id] = user.id # log in user however you like, alternatively stub `current_user` method get :index assert_template :index # render the template since they should have access ``` -------------------------------- ### Define Individual Abilities from Database Permissions Source: https://github.com/cancancommunity/cancancan/blob/develop/docs/abilities_in_database.md This alternative method iterates through each user permission and defines a specific `can` ability for it. It handles permissions with and without a specific subject ID. ```ruby def initialize(user) user.permissions.each do |permission| if permission.subject_id.nil? can permission.action.to_sym, permission.subject_class.constantize else can permission.action.to_sym, permission.subject_class.constantize, id: permission.subject_id end end end ``` -------------------------------- ### RSpec Describe Block for Abilities Source: https://github.com/cancancommunity/cancancan/blob/develop/docs/testing.md A structured RSpec approach for testing user abilities, using `describe` blocks and `subject` to define and test permissions for different user roles and contexts. ```ruby require "cancan/matchers" describe "User" do describe "abilities" do subject(:ability) { Ability.new(user) } let(:user) { nil } context "when is an account manager" do let(:user) { create(:account_manager) } it { is_expected.to be_able_to(:manage, Account.new) } end end end ``` -------------------------------- ### Using Custom Edit Link Helper in RHTML Source: https://github.com/cancancommunity/cancancan/wiki/Link-Helpers Demonstrates how to use a custom `edit_link` helper method to display an 'Edit' link, leveraging the permission check defined within the helper. ```rhtml <%= edit_link @project %> ``` -------------------------------- ### Generate Abilities File Source: https://github.com/cancancommunity/cancancan/blob/develop/docs/installation.md Use the Rails generator to create a template for your abilities file, where you will define user permissions. ```bash rails generate cancan:ability ``` -------------------------------- ### Override Resource Loading with a Before Action Source: https://github.com/cancancommunity/cancancan/wiki/Authorizing-controller-actions Manually override resource loading by defining a `before_action` that sets the instance variable before `load_and_authorize_resource` is called. Use `prepend_before_action` if `authorize_resource` is in `ApplicationController`. ```ruby class BooksController < ApplicationController before_action :find_published_book, :only => :show load_and_authorize_resource private def find_published_book @book = Book.released.find(params[:id]) end end ``` -------------------------------- ### Fetch Records Using Controller Action Source: https://github.com/cancancommunity/cancancan/wiki/Fetching-Records To use the current controller's action for permissions, pass `params[:action].to_sym` as the second argument to `accessible_by`. ```ruby @articles = Article.accessible_by(current_ability, params[:action].to_sym) ``` -------------------------------- ### Index Action Resource Loading Source: https://github.com/cancancommunity/cancancan/wiki/Authorizing-controller-actions The `index` action loads the collection resource using `accessible_by`. Custom find options can be applied as it is a scope. ```ruby def index # @products automatically set to Product.accessible_by(current_ability) end ``` ```ruby def index @products = @products.includes(:category).page(params[:page]) end ``` -------------------------------- ### Delegate `can?` Method from User Model Source: https://github.com/cancancommunity/cancancan/blob/develop/docs/define_check_abilities.md Add an `ability` method to your `User` model to delegate `can?` and `cannot?` calls. This allows for a more concise way to check abilities directly on a user object. ```ruby class User delegate :can?, :cannot?, to: :ability def ability @ability ||= Ability.new(self) end end ``` ```ruby some_user.can? :update, @article ``` -------------------------------- ### Specify Alternate Resource Class Source: https://github.com/cancancommunity/cancancan/blob/develop/docs/changing_defaults.md Use the `class` option with `load_and_authorize_resource` to map the controller to a different model class when they differ. ```ruby class ArticlesController < ApplicationController load_and_authorize_resource class: 'Post' def create @article.save end private def article_params params.require(:article).permit(:name) end end ``` -------------------------------- ### Basic Inherited Resources and CanCanCan Integration Source: https://github.com/cancancommunity/cancancan/wiki/Inherited-Resources Use `load_and_authorize_resource` in your controller to automatically load and authorize resources when using Inherited Resources. This ensures CanCanCan integrates seamlessly with Inherited Resources' lazy loading. ```ruby class ProjectsController < InheritedResources::Base load_and_authorize_resource end ``` -------------------------------- ### Update Action with Strong Parameters Source: https://github.com/cancancommunity/cancancan/blob/develop/docs/controller_helpers.md Use this pattern for the `:update` action to load, authorize, and update a resource with sanitized parameters. Ensure `article_params` is defined to permit specific attributes. ```ruby def update if @article.update(article_params) # hurray else render :edit end end ... def article_params params.require(:article).permit(:body) end ``` -------------------------------- ### Custom Resource Finding with :find_by Source: https://github.com/cancancommunity/cancancan/wiki/Authorizing-controller-actions Override the default resource loading mechanism to use a different attribute for finding records by specifying the `:find_by` option. ```ruby load_resource :find_by => :permalink # will use find_by_permalink!(params[:id]) authorize_resource ``` -------------------------------- ### Nested Hash of Conditions for Project Read Source: https://github.com/cancancommunity/cancancan/blob/develop/docs/hash_of_conditions.md Illustrates how to nest conditions through associations, allowing a 'Project' to be read only if its associated 'category' is visible. ```ruby can :read, Project, category: { visible: true } ``` -------------------------------- ### Generate and Migrate Roles Mask Source: https://github.com/cancancommunity/cancancan/wiki/Role-Based-Authorization Use these commands to add a roles_mask integer column to your users table and apply the migration. ```bash rails generate migration add_roles_mask_to_users roles_mask:integer rake db:migrate ``` -------------------------------- ### Checking 'Manage' Permissions Source: https://github.com/cancancommunity/cancancan/blob/develop/docs/define_check_abilities.md Verifies that an administrator can perform any action, such as 'edit' or 'destroy', on an article when 'can :manage, Article' is defined. ```ruby can? :edit, @article # => true can? :destroy, @article # => true ``` -------------------------------- ### Simplify Authorization with `authorize!` Source: https://github.com/cancancommunity/cancancan/blob/develop/docs/controller_helpers.md The `authorize!` helper automatically raises a `CanCan::AccessDenied` exception if the user lacks permission, simplifying access control checks in controllers. ```ruby def edit @article = Article.find(params[:id]) authorize! :edit, @article render :edit end ``` -------------------------------- ### Create Action with Strong Parameters Source: https://github.com/cancancommunity/cancancan/blob/develop/docs/controller_helpers.md This pattern is for the `:create` action. CanCanCan will attempt to initialize a new instance using parameters from `create_params`, `_params`, or `resource_params`. ```ruby def create if @article.save # hurray else render :new end end ``` -------------------------------- ### Protect Controller Actions with `can?` Source: https://github.com/cancancommunity/cancancan/blob/develop/docs/controller_helpers.md Use the `can?` helper to conditionally render content or deny access in controller actions. Ensure `current_user` is available. ```ruby class ArticlesController < ApplicationController def edit @article = Article.find(params[:id]) if can? :edit, @article render :edit else head :forbidden end end end ``` -------------------------------- ### Set Global SQL Strategy Source: https://github.com/cancancommunity/cancancan/blob/develop/docs/sql_strategies.md Configure the default SQL generation strategy for all CanCanCan queries globally. The default is :left_join. ```ruby CanCan.accessible_by_strategy = :subquery ``` -------------------------------- ### Defining Abilities with Conditions and Aliases Source: https://github.com/cancancommunity/cancancan/blob/develop/docs/define_check_abilities.md Sets abilities for different user roles. Admins have 'manage' permissions on all Articles, while logged-in users can 'read' and 'update' their own Articles. Unauthenticated users can only 'read' published Articles. ```ruby can :read, Article, published: true return unless user.present? can [:read, :update], Article, user: user return unless user.admin? can :manage, Article ``` -------------------------------- ### Attribute Initialization in New/Create Actions Source: https://github.com/cancancommunity/cancancan/wiki/Controller-Authorization-Example In `new` and `create` actions, CanCan can automatically set initial attribute values based on user permissions, simplifying attribute assignment. ```ruby @project = Project.new current_ability.attributes_for(:new, Project).each do |key, value| @project.send("#{key}=", value) end @project.attributes = params[:project] ``` -------------------------------- ### Defining 'Manage All' Permissions Source: https://github.com/cancancommunity/cancancan/blob/develop/docs/define_check_abilities.md Grants administrators full permissions on all subjects using the ':all' symbol. This includes permissions defined for specific subjects like Articles and abstract ones like :admin_dashboard. ```ruby can :read, Article, published: true return unless user.present? can [:read, :update], Article, user: user return unless user.admin? can :manage, :all ``` -------------------------------- ### Automatically Authorize Actions with authorize_resource Source: https://github.com/cancancommunity/cancancan/blob/develop/docs/controller_helpers.md Use `authorize_resource` to automatically call `authorize! action_name, @resource` for every action in the controller. Requires `before_action` to load the resource. ```ruby class ArticlesController < ApplicationController before_action :load_article authorize_resource def edit; end protected def load_article @article = Article.find(params[:id]) end end ``` -------------------------------- ### Singleton Resources with CanCanCan Source: https://github.com/cancancommunity/cancancan/wiki/Nested-Resources Configure singleton resources using the `singleton: true` option when a parent has a `has_one` association with the child. This changes the loading methods to use `@project.task` and `@project.build_task`. ```ruby class TasksController < ApplicationController load_and_authorize_resource :project load_and_authorize_resource :task, through: :project, singleton: true end ``` -------------------------------- ### Access Exception Action and Subject Source: https://github.com/cancancommunity/cancancan/wiki/Exception-Handling Retrieve the action and subject from the exception object within a rescue block to customize authorization failure responses. ```ruby exception.action # => :read exception.subject # => Article ``` -------------------------------- ### Run Specific Tests within an Appraisal Set Source: https://github.com/cancancommunity/cancancan/blob/develop/README.md Execute specific tests within a particular appraisal set by specifying a file or folder path. ```ruby DB='sqlite' SPEC=path/to/file/or/folder bundle exec appraisal activerecord_5.2.2 rake ``` -------------------------------- ### Split Ability Files per Model Source: https://github.com/cancancommunity/cancancan/blob/develop/docs/split_ability.md Organize abilities into separate files based on models for better maintainability. Each file includes CanCan::Ability and defines permissions for a specific model. ```ruby # app/abilities/user_ability.rb class UserAbility include CanCan::Ability def initialize(user) can :edit, User, id: user.id end end ``` ```ruby # app/abilities/comment_ability.rb class CommentAbility include CanCan::Ability def initialize(user) can :manage, Comment, user_id: user.id end end ``` ```ruby # app/abilities/book_ability.rb class BookAbility include CanCan::Ability def initialize(user) can :read, Book, published: true can :edit, Book, user_id: user.id end end ``` -------------------------------- ### Hash of Conditions using Association Name Source: https://github.com/cancancommunity/cancancan/blob/develop/docs/hash_of_conditions.md Shows how to use an association name ('owner') instead of a foreign key ('user_id') in a hash of conditions for a 'Project' read permission. ```ruby can :read, Project, active: true, owner: user ``` -------------------------------- ### Override Resource Loading with Before Filter Source: https://github.com/cancancommunity/cancancan/blob/develop/docs/changing_defaults.md The resource is only loaded if not already present. This allows overriding the loading mechanism in a separate `before_action`, such as finding a specific published book. ```ruby class BooksController < ApplicationController before_action :find_published_book, only: :show load_and_authorize_resource private def find_published_book @book = Book.released.find(params[:id]) end end ``` -------------------------------- ### Check Inability on Object Instance Source: https://github.com/cancancommunity/cancancan/wiki/Checking-Abilities Use `cannot?` for a convenient way to check if the current user is denied permission for a specific action on an object instance. ```ruby cannot? :destroy, @project ``` -------------------------------- ### Authorize Resource Callback Source: https://github.com/cancancommunity/cancancan/wiki/Authorizing-controller-actions Use this before_action callback to automatically authorize controller actions. It passes the resource instance variable or class name to the authorize! method. ```ruby authorize!(params[:action].to_sym, @product || Product) ``` -------------------------------- ### Checking Aliased Update Actions Source: https://github.com/cancancommunity/cancancan/blob/develop/docs/define_check_abilities.md Illustrates checking for aliased update actions. If 'can :update, Article' is defined, 'can? :edit, @article' will return true. ```ruby can? :edit, @article ``` -------------------------------- ### Defining Ability for a Symbol Subject Source: https://github.com/cancancommunity/cancancan/blob/develop/docs/define_check_abilities.md Shows how to define permissions for a symbolic subject, like an admin dashboard. This allows checking access to abstract resources. ```ruby can :read, :admin_dashboard ``` -------------------------------- ### Use Separate Ability Class for Namespaced Controller Source: https://github.com/cancancommunity/cancancan/wiki/Authorization-for-Namespaced-Controllers Instantiate a different `Ability` class (e.g., `AdminAbility`) for a specific namespaced controller. This approach is useful for complex authorization logic and aligns with best practices for organizing abilities. ```ruby class Admin::WidgetsController < ActionController::Base #... private def current_ability @current_ability ||= AdminAbility.new(current_user) end end ``` -------------------------------- ### User Model Role Management Source: https://github.com/cancancommunity/cancancan/wiki/Role-Based-Authorization Implement these methods in your User model to handle setting and retrieving roles using a bitmask. Ensure the ROLES constant is defined. ```ruby # in models/user.rb def roles=(roles) roles = [*roles].map { |r| r.to_sym } self.roles_mask = (roles & ROLES).map { |r| 2**ROLES.index(r) }.inject(0, :+) end def roles ROLES.reject do |r| ((roles_mask.to_i || 0) & 2**ROLES.index(r)).zero? end end ``` -------------------------------- ### Configure Cancancan for FriendlyId Source: https://github.com/cancancommunity/cancancan/wiki/FriendlyId-support Add this initializer to enable FriendlyId support in Cancancan. It modifies the ActiveRecord adapter to use `friendly.find` when FriendlyId is detected. ```ruby if defined?(CanCan) class Object def metaclass class << self; self; end end end module CanCan module ModelAdapters class ActiveRecord4Adapter < AbstractAdapter @@friendly_support = {} def self.find(model_class, id) klass = model_class.metaclass.ancestors.include?(ActiveRecord::Associations::CollectionProxy) ? model_class.klass : model_class @@friendly_support[klass]||=klass.metaclass.ancestors.include?(FriendlyId) @@friendly_support[klass] == true ? model_class.friendly.find(id) : model_class.find(id) end end end end end ``` -------------------------------- ### Check Ability on Class for Creation Source: https://github.com/cancancommunity/cancancan/wiki/Checking-Abilities Check if the user can create a new instance of a class. This is often used in views to conditionally render links. ```rhtml <% if can? :create, Project %> <%= link_to "New Project", new_project_path %> <% end %> ```