I have a rails app that uses Devise and cancan. I am wanting to allow only users to edit their own data, but still be able to view everyone elses.
I have:
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new # guest user (not logged in)
if user.role == "member"
their own only
can :read, User
can :manage, User, user_id: user.id
elsif user.role == "guest"
can :read, User
end
I also have:
class User < ActiveRecord::Base
#attr_accessible :name , :email # Include default devise modules. Others available are:
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
ROLES = %w[member guest]
def is?( requested_role )
self.role == requested_role.to_s
end
end
But I can still edit and delete other users comments. When I shouldn't be able to. Why? What am I doing wrong?
Rails 4
Devise 2.5.2
cancan 1.6
Thanks
For giving the permission of user to edit their own data, change
From
can :manage, User, user_id: user.id
To
can :update, User
You're specifying user_id as a column name for your User model:
can :manage, User, user_id: user.id
I'm willing to bet that User has no user_id column -- it only has an id column. What you want is this:
can :manage, User, id: user.id
Double check this by looking at your schema.rb file and making sure that there is (or is not) a user_id column. The actual ID column is called id and is not listed in schema.rb but is actually present in the database table.
Also, be sure that you really want to allow :manage permissions. The :manage symbol is very powerful and allows the user to do anything with the object in question, including deleting it.
Related
I've recently added roles to my rails application with CanCanCan (by enumeration)- but now I want to add a default role on signup. How do I go about doing this? Does it go in the controller or model?
My User model:
class User < ActiveRecord::Base
#Defining different roles
enum role: [:Admin, :User, :Guest]
#Users can only create one scholarship application
has_one :applications
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
end
My Ability Model - there's just three roles, an administrator which I will create through seeding the database with a user with a role of 1- and then everyone else should be 2 on signup.:
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new # guest user (not logged in)
if user.role = 1
can :manage, :all
elsif user.role = 2
can :manage, Application
can :manage, User
else
can :read, Static_Page
end
end
end
you can add callback to your model
before_create :set_default_role
private
def set_default_role
self.role ||= Role.find_by_name('your_role')
end
devise article
or in your case you can do
before_create :set_default_role
def set_default_role
self.update_attribute(:role,'your_role')
end
I am in a serious issue with the school based application, I want to set up like the roles based system for Admin, Student and the Teacher. I have used devise for authentication and cancan for the authorization. The thing is I don't no where to start. I have added the devise user model and added an field roles. I followed this for the role based authorization. It works, but when I try to login with the Student credential by selecting a Teacher role in the drop down it never validate the roles(never says access denied for you something like that). It login as a student. I want to validate the selected roles in the login form.
By now I am using User model as the devise model. and
has Student Model , Teacher Model , and the Admin Model. and the all Student, Teacher and the admin belongs to the User.
In User model
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_many :admins
has_many :students
has_many :teachers
ROLES = %i[admin teacher student]
end
In my devise registration view
<%= f.collection_select(:role, User::ROLES, :to_s, lambda{|i| i.to_s.humanize}) %>
Or else do I want to use devise model for all Student, Teacher and Admin? if yes how do I complete that. Any help or suggestions are helpful. Thanks in advance.
If you want to validate a specific role user to login with the role selected in the form dropdown then you have to customize the login method SessionsController#Create for the role selected in the login form with the user credentials coming from the form and send an error warning and avoid user to get logged in.
A sample sessions#create method will be :
def create
self.resource = warden.authenticate!(auth_options)
if resource.role == params[:role]
set_flash_message!(:notice, :signed_in)
sign_in(resource_name, resource)
yield resource if block_given?
respond_with resource, location: after_sign_in_path_for(resource)
else
set_flash_message!(:error, "UnAuthorized Access!")
redirect_to root_path
end
end
I have been trying to get rolify gem to work FOREVER. I have cancancan and devise as well, i basically have a signup page with email, password, password_confirmation, and a dropdown box selecting role. and when i click sign up it gives me an error to the segment of code creating the DDbox. Can anyone help me get this working ? my ability.rb is this
`class Ability
include CanCan::Ability
def initialize(user)
user ||=User.new
if user.has_role? :admin
can :manage, :all
elsif user.has_role? :regularUser
can :read, Menu
elsif user.has_role? :institution
can :read, Menu
elsif user.has_role? :franchiseOwner
can :read, Menu
end
# Define abilities for the passed in user here. For example:
#
# user ||= User.new # guest user (not logged in)
# if user.admin?
# can :manage, :all
# else
# can :read, :all
# end
#
# The first argument to `can` is the action you are giving the user
# permission to do.
# If you pass :manage it will apply to every action. Other common actions
# here are :read, :create, :update and :destroy.
#
# The second argument is the resource the user can perform the action on.
# If you pass :all it will apply to every resource. Otherwise pass a Ruby
# class of the resource.
#
# The third argument is an optional hash of conditions to further filter the
# objects.
# For example, here the user can only update published articles.
#
# can :update, Article, :published => true
#
# See the wiki for details:
# https://github.com/CanCanCommunity/cancancan/wiki/Defining-Abilities
end
end `
and this the the dropdown box code snippet from my view/devise/registation/new.html.erb..
<%= user_form.select :role, options_from_collection_for_select(Role.all,"Institution","Regular User", "Franchise Owner")%>
this is my User.rb model
`class User < ActiveRecord::Base
rolify :before_add => :before_add_method
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
def before_add_method(role)
end
end`
and my users_controller
`class UserController < ApplicationController
def index
#users = User.all
end
def new
#user = User.new
end
# POST /userss
# POST /users.json
def create
#user = User.new(user_params)
#user.add_role params[:user][:role]
end
private
def user_params
params.require(:user).permit(:email, :password, :password_confirmation, :role)
end
end`
User.new does not save a record and as such will not have an id.
Try saving the user either with user.save or by using User.create instead so it has an id before adding the role. Otherwise the role wont have a user_id to use for it's association.
user = User.create(:email => 'foo#bar.com', :password => 'somestring', :password_confirmation => 'somestring')
user.add_role :rolename
I used RailsApps rails-composer to create a rails-devise-pundit starter application. I am still a little new to ruby on rails and newer to devise, pundit and rails 4.
I was looking at the code to learn how it works. There are many places in controllers and in policy classes where user.admin? is called. But I can't find the admin? method. I would expect it to be in the User model but it isn't there. Here's the user class:
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable, :confirmable,
:recoverable, :rememberable, :trackable, :validatable
enum role: [:user, :vip, :admin]
after_initialize :set_default_role, :if => :new_record?
def set_default_role
self.role ||= :user
end
end
Used in part of users_controller.rb:
def show
#user = User.find(params[:id])
unless current_user.admin?
unless #user == current_user
redirect_to root_path, :alert => "Access denied."
end
end
end
Does pundit or devise create this method somehow? I've seen it used in the pundit documentation but it just uses it as an example. It doesn't say the method needs to be created or that it handles it. Is it somehow using the role enum which has :admin as a possible value? If anyone can explain how this works, I'd appreciate it.
I'm planning to add in use of the rolify gem soon to handle roles instead of the role enum in the user class. Maybe I'll want to make role names that are completely different for some reason. I want to make sure I understand how to keep everything working. Thanks.
Roles are defined in the app/models/User.rb file (the User model).
class User < ActiveRecord::Base
.
.
.
enum role: [:user, :vip, :admin]
after_initialize :set_default_role, :if => :new_record?
def set_default_role
self.role ||= :user
end
end
The application uses the ActiveRecord enum method to manage roles. ActiveRecord provides convenient methods to query the role attribute:
user.admin! # sets the role to "admin"
user.admin? # => true
user.role # => "admin"
See documentation for ActiveRecord::Enum for details. The ActiveRecord enum method is new in Rails 4.1.
I've updated the README for the rails-devise-pundit application to include this information. It's also covered in my Rails Pundit Tutorial.
It is an attribute of the User model. Same as first_name or last_name, there is a field called admin which is a boolean.
I am trying to create permissions in my Rails app with Devise and Cancan.
ability.rb:
class Ability
include CanCan::Ability
def initialize(user)
if user.role == 'admin'
can :manage, :all
else
can :read, :all
end
end
end
user.rb
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :token_authenticatable, :encryptable, :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
# Setup accessible (or protected) attributes for your model
attr_accessible :email, :password, :password_confirmation, :remember_me, :role
# attr_accessible :title, :body
def role(role)
roles.include? role.to_s
end
end
view
<% if can? :update, review %>
<p><p><i><a href = '/products/<%= #product.id %>/reviews/<%= review.id %>/edit'>Edit</a>
<% end %>
I get an error saying undefined method `role' for nil:NilClass for this line in the view. Any advice on how to fix this?
I've been trying to use https://github.com/ryanb/cancan/wiki/Role-Based-Authorization as a guide
try adding
user ||= User.new # guest user (not logged in)
to see if that fixes your role error.
class Ability
include CanCan::Ability
user ||= User.new # guest user (not logged in)
def initialize(user)
if user.role == 'admin'
can :manage, :all
else
can :read, :all
end
end
end
that way you can rule out user being nil.
Check out this screencast by the creator of the cancan gem. It explains what you are trying to do in depth a little better than the link you are following.