Rails Cancan: Defining Default Role on Signup - ruby-on-rails

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

Related

Ruby on Rail - undefined method `admin?' for nil:NilClass

I am using gem 'devise' and 'cancancan' for defining roles for user. problem is when I run rails server its says: undefined method 'admin?' for nil:NilClass
ability.rb:
class Ability
include CanCan::Ability
def initialize(user)
if user.admin?
can :manage, :all
else
can :read, :all
end
end
end
User model:
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_many :projects
def admin?
self.admin
end
end
What could be the problem?
I'm guessing the line from where the error is originating is something like:
if can? :manage, #object
# ...
This is likely causing an error because your user is not logged in.
The method defining the abilities looks like this:
def initialize(user)
if user.admin?
can :manage, :all
else
can :read, :all
end
end
So, if you have no user, the line user.admin? will cause an error because user is nil and nil has no admin? method.
I would do something like this:
def initialize(user)
if user.try(:admin?)
can :manage, :all
else
can :read, :all
end
end
try in Ruby won't throw an error but will return nil if the method doesn't exist.
(I have not tested this and since you're not sharing the error, I'm taking some liberties in the response.)
I'd also like to point out that if the admin attribute on your User model is a boolean, you don't need to define this method—Rails does that automatically for you.
def admin?
self.admin
end
ability
class Ability
include CanCan::Ability
def initialize(user)
if user.has_role? :admin
can :manage, :all
else
can :read, :all
end
end
end

rails cancan allow users to edit *only* their data, but view everyones

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.

Where is user.admin? defined in rails-devise-pundit starter app?

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.

Omniauth callback processing after authorization

In my Rails app, I have user and authorization tables to handle users and auth data. I set up both Devise and Omniauth to use Twitter to sign up, it redirects to Twitter, but after returning to my app, it gives an error like:
NoMethodError at /users/auth/twitter/callback
undefined method `authorizations' for #<Class:0xbdc8100>
In which side, did I go wrong and how can I fix this issue?
Here are related parts: omniauth_callbacks_controller.rb:
class OmniauthCallbacksController < Devise::OmniauthCallbacksController
def all
user = User.authorizations.from_auth(auth_hash)
if user.persisted?
flash.notice = "Signed in!"
sign_in_and_redirect user
else
session["devise.user_attributes"] = user.attributes
redirect_to new_user_registration_url
end
end
alias_method :twitter, :all
protected
def auth_hash
request.env['omniauth.auth']
end
end
authorization.rb:
class Authorization < ActiveRecord::Base
attr_accessible :uid, :provider
belongs_to :user
def self.from_auth(auth)
where(auth.slice(:provider, :uid)).first_or_create do |user|
user.provider = auth.provider
user.uid = auth.uid
end
end
user.rb:
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :token_authenticatable, :confirmable,
# :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable, :omniauthable, :omniauth_providers => [:twitter, :facebook]
# Setup accessible (or protected) attributes for your model
attr_accessible :email, :password, :password_confirmation, :remember_me, :name
# attr_accessible :title, :body
has_many :authorizations, dependent: :destroy
end
Your issue is in this line...
user = User.authorizations.from_auth(auth_hash)
You call authorizations on the class User, but as an attribute it needs to be called on an instance of the User class, i.e. a specific user.

How to fix an error with undefined error with cancan and devise in rails?

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.

Resources