stephenmurdoch
stephenmurdoch

Reputation: 34603

CanCan: limiting a user's ability to set certain model attributes based on their role

I have a Post model with a :published attribute (boolean) and a User model with a role attribute (string). There are three roles: ROLES = %w[admin publisher author]

I don't want users whose role is author to be capable of setting, or editing, the :published field on the Post model.

I'm using CanCan (and RailsAdmin gem) and my simplified Ability.rb file looks like this:

class Ability
  include CanCan::Ability
  def initialize(user)
    user ||= User.new

    if user.role? :admin
      can :manage, :all
    elsif user.role? :publisher
      can :manage, Post
    elsif user.role? :author
      # I want to prevent these guys from setting the :published attribute
    end

  end
end

Anyone got any tips for doing this sort of thing?

Upvotes: 20

Views: 8148

Answers (4)

ideaoforder
ideaoforder

Reputation: 1023

Until CanCan 2.0 comes out, I've solved this by creating a subclass of the model with restricted accessibility, something like:

class AuthorPost < Post
  attr_protected :published
end

And then give authors access to AuthorPosts: can :manage => AuthorPost

Then in your controller, you can set the resource you want in a before_filter:

before_filter :set_resource
...
  private
    def set_resource
      if current_user and current_user.author?
        @resource = AuthorPost
      else
        @resource = Post
      end
      params[:post] ||= params[:author_post]
    end

One last caveat: you won't be able to use load_and_authorize_resource in that controller. You'll have to do that manually, as detailed here: https://github.com/ryanb/cancan/wiki/Controller-Authorization-Example

You'll need to replace Project with @resource.

I'm on the fence as to whether this is more or less effective than the method described in the railscast. For my purposes, it left the original model totally intact, so my other code wasn't affected--and just allowed me to give some users fewer editable fields.

Upvotes: 3

John Goodman
John Goodman

Reputation: 1098

Check out this post: How do I use CanCan with rails admin to check for ownership

It shows how to make a field not visible based off a users role.

UPDATE I was able to set options in rails admin with this code:

config.model User do
  edit do
    configure :organization do
      visible do
        bindings[:view]._current_user.max_role_name != 'admin' ? false : true
      end
    end

    configure :organization_id, :hidden do
      visible do
        true if bindings[:view]._current_user.max_role_name != 'admin'
      end
      default_value do
        bindings[:view]._current_user.organization_id if bindings[:view]._current_user.max_role_name != 'admin'
      end
    end

    include_all_fields
  end
end

This configuration will hide the organization field if the logged in user is not an admin. It will then show an organization_id field ( set to type='hidden' ) and set the default value.

Hope this helps someone.

Upvotes: 3

Shannon
Shannon

Reputation: 2744

There is a way, I did something like this in my project. But CanCan is not entirely the answer. What you need to do is make attr_accessible in your model dynamic based on user role, so if you're an admin, then you're allowed to update the published field. If not, then giving the field a new value simply won't take when the model saves.

Railscasts comes to the rescue once again: http://railscasts.com/episodes/237-dynamic-attr-accessible

Following getting the backend part of that implemented, then you can do something about the frontend form by wrapping the publish field in the View with a roles check or something to show or hide the field based on the user. Rough example of my implementation...

<% if current_user.roles.where(:name => ['Administrator','Editor']).present? %>
    <%= f.label :display_name %>
    <%= f.text_field :display_name %>
<% end %>

Upvotes: 0

Piotrek Okoński
Piotrek Okoński

Reputation: 597

So far it is not possible. But according to this: https://github.com/ryanb/cancan/issues/326 this feature should be in cancan 2.0.

Update: you can see this on CanCan 2.0 branch here: https://github.com/ryanb/cancan/tree/2.0 in section "Resource Attributes"

Upvotes: 13

Related Questions