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

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.

Related

Rails Cancan: Defining Default Role on Signup

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

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.

How to add an admin restriction feature through Devise to my rails app?

My rails app has a few cab operators and they have a few cabs, and they are related as follows:
class Operator < ActiveRecord::Base
has_many :cabs
end
I wish to add authentication system so as to create admins for each operator. I am using Devise. Since I need to create path as: operator/:operator_id/admins/sign_up, I generated the Admin model, as:
rails generate devise Admin
Then I modified my routes so as to obtain the above mentioned path:
scope "operators/:operator_id" do
devise_for :admins
end
After a few more modifications, I was able to add new admins and associate them to appropriate operators. However, I want to ensure that an admin only has access to the cabs of the operator to which the admin is associated. Adding the following to the cabs_controller doesn't work:
before_action :authenticate_admin!
as a signed_in admin has access to all other operator's cabs. I want to make sure that:
1. If there is no current_admin, my app asks to sign_in or sign_up
2. If there already is a current__admin signed_in, he/she has access to only the cabs associated to that operator to which the current_admin is assigned.
I am new to Devise. Please advise how I should proceed. Thanks!
Devise is for Authentication, your problem is an Authorization problem which Devise cannot help. You can look into Cancan https://github.com/ryanb/cancan :)
You don't need to scope your devise for admins. I'm not sure if it would solve any issue with access. It also makes your code harder to maintain.
I would advise you to focus on relationships instead
class Operator < ActiveRecord::Base
has_many :cabs
has_many :admins
end
class Operator < ActiveRecord::Base
has_many :cabs, through: operator
belongs_to :operator
end
Normally the way I handle such cases later is to write special in the cab model :
def is_admin?(admin)
admin.cabs.include?(#cab)
# you can put more logic here if you have different levels of access
end
and later in the controller when you want to restrict access:
if #cab.is_admin?(current_admin)
# do the stuff
else
# go away message
end
or for instance if you list cabs in index:
def index
current_admin.cabs.page(params[:page]) # this is assuming you're using pagination with something like Kaminari
end
current_admin from divise, you may want to also run :authenticate_admin! before filter to make sure you have you admin and it's not nil
Of course you can put it in price message and run it as before filter.
The most simple solution is to use STI and have Admin < User, then a couple of
devise_for :users
devise_for :admins
If you do not want to use STI, some custom overridings may do the trick, if you add a role method to your users table for exemple.
#routes
devise_for :users
namespace :admin do
devise_for :admins,
singular: :admin,
plural: :admins,
class_name: 'User',
only: [:sessions],
path: '/',
module: :devise
end
#lib/overrides/devise
require "devise/strategies/authenticatable"
require "devise/strategies/database_authenticatable"
module Devise
module Strategies
class DatabaseAuthenticatable
def authentication_hash
if mapping.name == :admin
#authentication_hash.update(role: :admin) #will add a where(role: :admin) clause
else
#authentication_hash
end
end
end
end
end
class Admin::ApplicationController < ApplicationController
skip_before_filter :authenticate_user!
before_filter :authenticate_admin!
end
You can use role base Authentication.
Generate User model
class User < ActiveRecord::Base
enum role: [:operator, :admin]
after_initialize :set_default_role, :if => :new_record?
def set_default_role
self.role ||= :operator
end
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
end
generate migration to add role
rails g AddRoleToUser role:integer
Please take a look ENUM

How to associate a Devise User with another existing model?

I am very new to Ruby on Rails and have setup Devise for authentication. I have an existing model that I created prior to adding Devise. That model is called Article. I believe I have done everything I need to do in order to use the association=(associate) method that "assigns an associated object to this object. Behind the scenes, this means extracting the primary key from the associate object and setting this object’s foreign key to the same value" which is exactly what I need to do.
Here is Devise's User model:
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
has_one :article
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
end
Here is my Article model:
class Article < ActiveRecord::Base
belongs_to :user
validates :name, presence: true, length: { minimum: 5 }
end
Here is my migration:
class AddUserRefToArticles < ActiveRecord::Migration
def change
add_reference :articles, :user, index: true
end
end
Here is my create method from my articles_controller.rb:
def create
#article.user = current_user
#article = Article.new(post_params)
if #article.save
redirect_to #article
else
render 'new'
end
end
And here is what happens when my controller runs:
NoMethodError in ArticlesController#create
undefined method `user=' for nil:NilClass
The highlighted code is #article.user = current_user. I was at least glad to know that I wrote that line of code similar to the popular answer in the Devise how to associate current user to post? question that I saw on here before posting this one.
I know I'm making a rookie mistake. What is it?
A new User instance needs to be assigned to #article before you can access any of the instance's attributes/associations. Try the following:
#article = Article.new(post_params) # Assign first
#article.user = current_user # Then access attributes/associations
The code posted in the question yields a nil:NilClassexception because the user association is being invoked on #article, which is nil because nothing has yet been assigned to it.

Admin user administration with Devise

I am trying out Devise for the first time. One of the things that I wanted to do is provide an interface for Admin users to create, find and edit users. Here's where I may have gone wrong.
I created a PeopleController class which inherits from ApplicationController that lists people and provides methods and views for creating and updating users. Everything works fine with one exception. When the admin user updates their own record, the session is cleared and they have to login again after saving it.
In this application I'm not using the registerable module. Only an admin user can create new users. What is the right way in devise to provide user management tools. Creating my own controller seems to have been the wrong path to take.
Thanks in advance for your help.
Thank you very much for the help. This is essentially exactly what I am doing. I discovered a clue that helped me solve the problem of the user's session being cleared when they edit their own record in this wiki:
https://github.com/plataformatec/devise/wiki/How-To:-Allow-users-to-edit-their-account-without-providing-a-password
This is the line I needed:
sign_in resource_name, resource, :bypass => true
This method is located in Devise::Controllers::Helpers so I did this in my controller.
class PeopleController < ApplicationController
include Devise::Controllers::Helpers
Then in my update method I call it only if the current_user.id equals the id that is being edited:
def update
#person = User.find(params[:id])
if #person.update_attributes(params[:user])
sign_in #person, :bypass => true if current_user.id == #person.id
redirect_to person_path(#person), :notice => "Successfully updated user."
else
render :action => 'edit'
end
end
Now if the current user edits their own record, the session is restored after it is saved.
Thanks again for your responses.
This is how I manage users in one of my apps. I have only one User class generated with
rails g devise User
to which I added a role column with this migration:
class AddRoleToUser < ActiveRecord::Migration
def change
add_column :users, :role, :string, :default => "client"
end
end
and my User model:
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :token_authenticatable, :confirmable, :lockable and :timeoutable
devise :database_authenticatable, :recoverable, :rememberable, :trackable, :validatable
# Setup accessible (or protected) attributes for your model
attr_accessible :email, :password, :password_confirmation, :remember_me
def admin?
self.role == "admin"
end
end
Then to create new users all you would have to do is provide a custom method in a controller (maybe even subclass Devise::RegistrationsController) like this:
# some_controller.rb
def custom_create_user
if current_user.admin?
User.create(:email => params[:email], password => params[:password])
redirect_to(some_path, :notice => 'sucessfully updated user.')
else
redirect_to(some_other_path, :notice => 'You are not authorized to do this.')
end
end

Resources