this is my ability.rb
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new # guest user (not logged in)
if user.admin?
can :manage, :all
else
can :read, :all
end
end
end
and index
<% if can? :update, #post %>
<%= link_to t('.edit', :default => t("helpers.links.edit")),
edit_post_path(post), :class => 'btn btn-mini' %>
<% end %>
but I recive error
"NoMethodError in Posts#index undefined method `admin?' for #"
where should I define admin or other roles? And how can i choose admin or anoher role when sign in?
You have to create admin method in User model
def admin?
type == 'Admin'
end
This is in case you have Admin model:
class Admin < User
end
Related
Im trying to authorize each user so that they can create, read, update and destroy ONLY their own logg. I created the logg using a scaffold. I have used devise and admin as a boolean in users. I have a LoggsController, User model, logg model and ability.rb I tried following the rails cast video so that I can allow all users to do this. So far, admins can do everything. But users cant do what I want them to.
class Ability
include CanCan::Ability
def initialize(user)
user||= User.new
if user.admin?
can :manage, :all
else
can :read, :all
can :create, Logg
can :update, Logg do |logg|
logg.try(:user) == user
end
end
end
end
My models and controller
class User < ActiveRecord::Base
ROLES = %w[admin moderator author banned]
has_many :loggs
def role?(role)
roles.include? role.to_s
end
end
class Logg < ActiveRecord::Base
belongs_to :users
end
class LoggsController < ApplicationController
load_and_authorize_resource
before_action :set_logg, only: [:show, :edit, :update, :destroy]
respond_to :html
def index
#loggs = Logg.all
respond_with(#loggs)
end
def show
respond_with(#logg)
end
def new
respond_with(#logg)
end
def edit
end
def create
#logg.save
respond_with(#logg)
end
def update
#logg.update(logg_params)
respond_with(#logg)
end
def destroy
#logg.destroy
respond_with(#logg)
end
private
def set_logg
#logg = Logg.find(params[:id])
end
def logg_params
params.require(:logg).permit(:name, :date, :time, :whats_gone_well_this_week, :whats_not_gone_well_this_week, :learnt_anything_new, :what_would_you_like_to_improve, :anything_else)
end
end
View
<% if can? :show, #logg %>
<%= link_to 'Show', logg %>
<% end %>
<% if can? :update, #logg %>
| <%= link_to 'Edit', edit_logg_path(logg) %>
<% end %>
<% if can? :destroy, #logg %>
| <%= link_to 'Destroy', logg, method: :delete, data: { confirm: 'Are you sure?' } %></p>
<% end %>
It doesn't look like you're setting the Logg's owner (user) anywhere. In your create, you'll need something like:
def create
#logg = current_user.loggs.create(logg_params)
respond_with(#logg)
end
For some reason, for a non logged in user, this code displays the html but once delete is clicked CanCan does not allow the action.
<% if can? :destroy, #boat %>
<%= link_to "", boat, method: :delete, data: { confirm: "You sure?" } %>
<% end %>
How do I prevent the HTML from displaying???
def initialize(user)
user ||= User.new
if user.admin? || user.email = 'test#test.io'
can :manage, :all
elsif user.manager?
can :read, Boat
can [:create, :read, :update], User
else
can :read, Boat
can :create, User
end
end
Some how the app was still detecting my email, even while logged out and therefore giving me admin privileges!
Not sure how this is happening...
I am using devise and cancan in my app. My Product model has a belong_to :user and the User table has a column seller:boolean. So in my ability class I have this line product.try(:user).seller as shown in the Ability Class pasted in the question, that line throws NoMethodError in ProductsController#create, undefined method `seller' for nil:NilClass any time I try to create a new product. But in the controllers create action, the user object is not nil because when I inspect the log after the error, I see SELECT "users".* FROM "users" WHERE "users"."id" = 11.
Also, in the rails console if I do:
a = Product.find(4)
a.user.seller will return => true
My ability class
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new
can :manage, Product do | product |
product.try(:user_id) == user.id
product.try(:user).seller == true
end
end
end
The Productscontroller:
class ProductsController < ApplicationController
before_filter :authenticate_user!, except: [:index, :show]
load_and_authorize_resource only: [:create, :edit, :destroy]
respond_to :html, :json
def index
#products = Product.all
respond_with #products
end
def new
#product = Product.new
end
def create
#user = User.find_by(id: current_user.id)
#product = #user.products.build(product_params)
#product.save
end
end
To make CanCan work with rails 4, my application controller has
class ApplicationController < ActionController::Base
#temprary work-around for cancan gem to work with rails-4
#source https://github.com/ryanb/cancan/issues/835#issuecomment-18663815
before_filter do
resource = controller_path.singularize.gsub('/', '_').to_sym
method = "#{resource}_params"
params[resource] &&= send(method) if respond_to?(method, true)
end
end
To give clarity to the screenshots below, here is a shortened version of products/index.html.erb
<% #products.each do |product| %>
<% if user_signed_in? %>
<% if can? :update, product %>
<span class="bottomcentre"><%= link_to 'edit', edit_product_path(product), class: "btn btn-primary" %></span>
<% end %>
<% if can? :destroy, product %>
<span class="bottomright"><%= link_to "Delete", product, data: {confirm: 'Are u sure?'}, method: :delete, class: "btn btn-danger" %></span>
<% end %>
<% end %><!-- closes user_signed_in -->
<% end %>
<br/>
<% if user_signed_in? %>
<% if can? :create, Product %>
<%= link_to 'Create a new Product', new_product_path %>
<% end %>
<% end %>
An additional effect is that the edit and destroy links are shown to a seller for products that are not theirs unless I comment out the line raising the error, that is this product.try(:user).seller == true in the CanCan's Ability Class shown earlier on. So when commented out, I get this screenshot 1 with edit links hidden fro products not belongs to the seller and when uncommented you get screenshot 2, with all product edit link displayed to a seller, even if the products are not theirs.
screenshot 1 with product.try(:user).seller == true commented out in the CanCan Ability Class, and it shows the edit link for only the first two product which belongs to the signed_in seller
screenshot 2 with product.try(:user).seller == true left intact in the CanCan Ability Class. see edit links being displayed for the lower products that is shirt and cufflinks and they don't belong to the signed_in seller.
Your can :manage block definition is slightly wrong. It is the return value of the block that determines whether the user has the ability or not. In your block you have two statements of which only the second will have any bearing on whether the user has the ability. You need to join your statements with &&.
Also, your error seems to be when a product has no user as opposed to when the current user is nil. You need to use try on the return of product.try(:user) too as it will be nil if there is no product.
So, all told I think your block needs to be:
can :manage, Product do | product |
product.try(:user_id) == user.id &&
product.try(:user).try(:seller) == true
end
So I have setup rails_admin with devise and cancan, I have it so that only admins can access the /admin pages.
But when trying to only show certain code to admins using <% if user_admin? %> I get undefined methoduser_admin?' for`
ability.rb
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new # guest user (not logged in)
if user.admin?
can :manage, :all
can :access, :rails_admin #grant access to rails_admin
can :dashboard #gives access to the dashboard
else
can :read, :all
end
end
end
_header.html.erb
<% if user_admin? %>
<li><%= link_to 'Settings', edit_user_registration_path %></li>
<li><%= link_to 'Logout', destroy_user_session_path, method: :delete %></li>
<% else %>
<li><%= link_to "Create Account", new_user_registration_path %></li>
<li><%= link_to "Login", new_user_session_path %></li>
<% end %>
I needed to set the abilities up in the ability.rb
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new # guest user (not logged in)
if user.admin?
can :manage, :all
can :access, :rails_admin #grant access to rails_admin
can :dashboard #gives access to the dashboard
else
can :read, :all
end
end
end
Then was able to just call
<% if can? :access, :rails_admin %>
<li><%= link_to 'Admin', rails_admin_path %></li>
<% end %>
I can now do if can? on anything I want to authorize.
I am using cancan for my app
my ability.rb class is
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new # guest user
if user.role? :admin
can :manage, :all
elsif user.role? :operations
can :manage, :all
elsif user.role? :customer_support
can :read, :all
else
user.role? :marketing
can :read, :all
end
end
end
and i add method in user.rb
def role?(role)
self.roles.include? role.to_s
end
I also add
load_and_authorize_resource in my controller say products_controller which can authorise user and allow him to do certain action in this controller,
but my problem is when user gets logged in with admin as role he can't be able to add new product, it gives access denied error of cancan.
my view is
<% if can? :create, Product %>
<td class="action"><%= link_to 'Show', product %></td>
<td class="action"><%= link_to 'Edit', edit_product_path( product) %></td>
<td class="action"><%= link_to 'Destroy', product, :confirm => 'Are you sure?', :method => :delete %></td>
<% end %>
it also not showing this link to admin as there are all access to admin but still he can't access this action?
what else am I missing?
plz help?
Have you followed instructions in the cancan wiki? https://github.com/ryanb/cancan/wiki/Role-Based-Authorization.
Cancan default strategy for storing roles for each user is using a bitmask, but the wiki mentions about a different solution here: https://github.com/ryanb/cancan/wiki/Separate-Role-Model.