Skip to content

Lesson use hydra access controls and cancan to decide whether to render a page

Michael Bond edited this page Sep 23, 2015 · 2 revisions

This Tutorial is known to work with hydra-head version 6.4.0.
Please update this wiki to reflect any other versions that have been tested.

Goals

  • Use Hydra Access Controls and its built-in CanCan integration to decide whether to render a page

Explanation

All of the methods described in this lesson are actually provided by a gem called CanCan. Hydra simply implements the ability for those CanCan methods to consult Hydra rightsMetadata to make decisions about access controls. The actual methods come from CanCan, so for more information you should consult the CanCan documentation.

Steps

Step: Make sure you have generated the Rails Scaffolding for Books

Before starting this lesson, you need to have Controller, Views and Routes set up for Books. The easiest way to do this is by using the rails scaffold generator. See Lesson: Generate Rails Scaffolding for Creating and Editing Books in the Dive into Hydra tutorial for step-by-step instructions.

Step:

Rails 3

If you're using Rails 3, in app/controllers/books_controller.rb find the lines that define the show method and add authorize! :show, params[:id] as the first thing the method does.

  # GET /books/1
  # GET /books/1.json
  def show
    authorize! :show, params[:id]
    @book = Book.find(params[:id])

    respond_to do |format|
      format.html # show.html.erb
      format.json { render json: @book }
    end
  end

Rails 4

Note to Rails 4 Users

Rails 4 generates slightly different code for the show action:

class BooksController < ApplicationController
  before_action :set_book, only: [:show, :edit, :update, :destroy]

  [...]

  # GET /books/1
  # GET /books/1.json
  def show
  end
  
  [...]

  private
  # Use callbacks to share common setup or constraints between actions.
  def set_book
    @book = Book.find(params[:id])
  end

  [...]

Rewrite the show action using the exact same code that is given above in the Rails 3 example, but edit the before_action to look like this:

  before_action :set_book, only: [:edit, :update, :destroy]

Here we've changed the before_action so that it will not fire before our show requests.

Test the Results

Now that we've added CanCan's abilities to books_controller.rb, we want to see if that controller will enforce the restrictions. To test this, we use the show method on BooksController which has the url of:

  http://localhost:3000/books/changeme:1

The actual pid changeme:1 may differ in your application.

You'll note that this is different than the catalog controller path of /catalog/changeme:1 which you would get if you visited your book's show page when coming from a search result in Blacklight. This is because we've only attached CanCan's abilities to app/controllers/books_controller.rb and not app/controllers/catalog_controller.rb.

Now when you try to view a book's show page using the BooksController in the browser, you will get an error page (assuming you don't have read access to the book you're using).

CanCan::AccessDenied in BooksController#show

You are not authorized to access this page.

If you go into the rails console and give yourself read access, then the page renders normally.

Likewise, you can add authorize! :edit, params[:id] to the BooksController#edit, BooksController#update and BooksController#destroy methods. Then save the file and see what happens in the browser. When you try to access the edit page for an object that you don't have edit access to, you get an error, but if you go into the rails console and give yourself edit access, then the page renders normally.

Step: Alternatively use load_and_authorize_resource

Instead of calling authorize! in each of your methods, there's a one-line option you can use. At the top of app/controllers/books_controller.rb add a line that simply says load_and_authorize_resource

class BooksController < ApplicationController

  load_and_authorize_resource

  # GET /books
  # GET /books.json
  def index
    @books = Book.all

    respond_to do |format|
      format.html # index.html.erb
      format.json { render json: @books }
    end
  end

  [...]

end

This load_and_authorize_resource method does two things for you. It loads a copy of the Book from Fedora and it runs authorize! for you. So, if you're using this method you can also delete the lines that create @book for you, for example:

@book = Book.find(params[:id])

If you're using load_and_authorize_resource, it does that loading for you. This allows you to have less lines of code in your Controller.

Note: A drawback of using load_and_authorize_resource is that it always loads a copy of the object from Fedora on every request. By contrast, calling authorize! only reads the permissions metadata out of Solr, which is much faster than loading an object from Fedora. Either method works equally well. There is just the tradeoff: one method lets your application run faster while the other lets you have less lines of code in your Controller.

Next Step

Go on to Lesson - Using Hydra Access Controls and CanCan to conditionally render part of a page or return to the Access Controls with Hydra tutorial.

Clone this wiki locally