Devise with CanCan(can) - ruby-on-rails

I can't seem to find a good tutorial for this and I've hit a bit of a wall.
I'm using rails 4.2.0 with a basic CRUD app. For auth I'm using devise and for roles I'm using Cancancan as these have fairly understandable documentation.
I have two types of Users:
Users and Admins.
Admins can interact with all models. Add-Edit-Delete etc.
Users can only interact with certain models. Which will be a booking system of sorts.
I'm not quite sure of the process I need to go through to set this up. Do I need to do a full rails generation for each user type or can I just use the Devise generation? Adding onto that how can I choose the user type? So far I have two login links which works.
The main issue I'm having is defining roles in cancancan.
Any help/questions on the subject would be appreciated.

For simplicity, you could add an admin boolean column on the users table. You would check for an admin user with user.admin?.
Here is what the migration will look like.
> rails g migration add_admin_to_users
In your migration file, I would set a default value to false prior to running it.
class AddAdminToUsers < ActiveRecord::Migration
def up
add_column :users, :admin, :boolean, null: false, default: false
end
def down
remove_column :users, :admin
end
end
By default, your users won't be admins. However, you can easily make a user an admin with user.update_column(:admin, true).
With this, you should be able to follow the CanCanCan docs, as they are pretty extensive I believe.
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new
if user.admin?
# admin abilities
else
# non-admin abilities
end
end
end
http://github.com/CanCanCommunity/cancancan/wiki/Defining-Abilities

Related

Who handle the roles of a User in Rails?

I'm making a Rails 6 application where I'm using Devise for authentication, Pundit for authorization and I added Active-Admin because I need a dashboard where admin users manage the content of the app.
Other than admin, I have a couple of more roles president, manager, guest. An admin can be president or manager.
I'm little confuse on what to use to implement the roles, with devise? pundit? I do it by hand?
Is it better to unite the User and the AdminUser model active-admin created? Because this way UserAdmin users can't log in to the application, only to the dashboard and that is not what I want.
I have seen tutorials where people add an admin:boolean column to the users, should I do something like that?
Is it better to unite the User and the AdminUser model active-admin created? Because this way UserAdmin users can't log in to the application, only to the dashboard and that is not what I want.
That depends more on your business logic. It may be a good idea to keep your users and admin_users tables separated; The users table will probably need to have a lot of associations with other tables, that will not be necessarily needed by admin_users, right?
I'm little confuse on what to use to implement the roles, with devise? pundit? I do it by hand?
You may define a role column in your admin_users table, and use that column in pundit policies, for example:
class ResourcePolicy
# ...
# ...
# ...
def update?
user.admin? || user.president?
end
end
in AdminUser, you can do the following:
class AdminUser < ActiveRecord::Base
def admin?
role == 'admin'
end
def president?
role == 'president'
end
end
There are many other ways to implement that, and they all depend on what you need to achieve.

Accessing an external table from a Rails application

Rails 3.2
I am using the API gem. What the client wants, is to keep the table where he wants to whitelist the email addresses that can be used to access the API, in a seprate table, that he only can access through phpmyadmin.
This would be a single table:
api_users
With a single column: email (in addition to id, created_at, updated_at)
The email addresses that would go in this table, also exist in the users table for the rails application.
If I create a model: models/api_user.rb:
class ApiUser< ActiveRecord::Base
attr_accessible :email
And, in my models/api_ability.rb, I add the following:
class ApiAbility
include CanCan::Ability
def initialize(user, params = {})
user ||= User.new
if ApiUser.find_by_email(user.email)
can :manage, :api
end
end
end
Will this work?
That sounds absolutely doable. You might want to add something like
def readonly?
true
end
to the ApiUser class to make sure no one will try to create instances of it from within Rails. But apart from that I don't see any reason not to do it that way given the clients requirements.

Workflow for admin/user accounts creation in Rails

I'm building an event registration system in Ruby on Rails. I'll need admin users as well as normal users. Is there any best practice for creating the admin users manually and not letting any random person "Sign Up" as an admin? Also, is there any way to prevent signups in general?
(I'm thinking about using the Devise gem)
Option #1
The simplest way I've found, for most scenarios, is to simply add an admin (boolean) attribute to the user model (I usually use Devise, but this applies to any User model):
# db migration
class AddAdminToUsers < ActiveRecord::Migration
def change
add_column :users, :admin, :boolean, default: false
end
end
Then, thanks to ActiveRecord, you'll automagically have an admin? method on the User model:
<% if current_user.admin? %>
<%= # super-secret admin-only option %>
<% end %>
Then, your signup pages will be the same as they were, and new users will silently default to admin = false. Then, you can implement "promoting" Users to admin = true however you would like...such as in an Admin::UsersController#edit view in an admin area of your app.
Option #2
If, however, you need the admin users to have their own views/routes/logic/etc, it might be worth separating them into their own Admin model (devise supports this as well). This would give you two separate models, each with their own routes, views, and controllers. One downside is that promoting a User to an Admin would mean actually moving the record from one table to the other. This option has a lot of overhead, but can end up being much cleaner if you need separate logic.
Which pattern you use will ultimately depend on your own scenario. If you need separate views/routes/logic for managing admin users, then choose #2. If you can get away with just checking for an admin attribute, then #1 is much simpler.
Devise illustrates both patterns in more detail here.

manage roles in rails

I want create roles in my project. Each user can be: admin, registered or demo. Each role see different things.
How can I do that? What is the best gem to do roles?
This is a example in 'bad programming" of what I want:
def index
if current_user.role[:name] == 'admin'
#installations = Installation.all
elsif current_user.role[:name] == 'registered'
#installations = current_user.installations
elsif current_user.role[:name] == 'demo'
#installations = current_user.installations.first
else
end
end
Some gems that might be interesting for you :
rolify
role_model
If you decide to implement it yourself, then within some page you might want to change the content, for that you might want to do something like this :
Add a role to the user model using a migration :
class AddRoleToUsers < ActiveRecord::Migration
def change
add_column :users, :role, :string, default: :demo
end
end
Then in your app you can use it as follows:
def index
case current_user.role
when :admin
#installations = Installation.all
when :registered
#installations = current_user.installations
else
#installations = current_user.installations.first
end
end
You can also simply create a boolean admin for instance.
What you might want to do also is create some methods in your model so that you can call current_user.admin? or current_user.registered? . You can do that by doing (if you chose to use a string to store the role):
class User < ActiveRecord::Base
def admin?
self.role == "admin"
end
def registered?
self.role == "registered"
end
end
One advantage I see of having a role stored in a string is that if you have 5 roles for instance then you do not have 4 booleans (as when you store admin in a boolean) but only one string. On the long run you might want to store actually a role_id instead of a string and have a separate role model.
An excellent alternative pointed out by Jorge de Los Santos (another answer) is to use enum :
class User < ActiveRecord::Base
enum role: [:demo, :admin, :registered]
end
It is an excellent alternative because it will automagically add the methods described above such as current_user.admin? without hard coding them.
With your roles, you might want to do some authorization (admins can have access to specific pages, demo users are restricted to only a subset of pages, etc.). For this, you can use the gem called cancancan. You can look at this railscast to learn more about it. Also, you can have some infos here : How to use cancancan? .
There are plenty of solutions available to you.
Starting by gems:
https://github.com/RolifyCommunity/rolify
https://github.com/martinrehfeld/role_model
By using Devise architecture (in case you use it):
https://github.com/plataformatec/devise/wiki/How-To:-Add-a-default-role-to-a-User
By using enums in rails 4:
class AddRolesToUser < ActiveRecord::Migration
#add_column 'role', :integer, default: 0 to the users table
end
class User < ActiveRecord::Base
enum role: [:demo, :admin, :registered]
end
That will enable role methods.
user = User.find(1)
user.role #:demo
user.admin? #false
user.registered? #false
And consequently:
if user.admin?
#somethig
elsif user.registered?
#another something
else
#another another something.
And last but not least, what you are searching is not the manage roles solution, is the manage permissions solutions:
https://github.com/ryanb/cancan
Add a boolean, :admin to your User model.
class AddAdminToUsers < ActiveRecord::Migration
def change
add_column :users, :admin, :boolean, deafult: false
end
end
Create a method for a registered user to separate them from demo users, such as verifying their email, providing a home address and phone number, filling out a profile, etc. This is up to you though, first you need to decide how a registered and demo user should be different.
The CanCan gem adds authorization to your project, and is especially useful if you want to implement multiple roles with differing abilities. When used with an authentication system like devise, you get a full suite of capability for your site.
You're in full control of what roles you want to define and what abilities they have. CanCan manages tracking, assignment, and querying of roles, and then gets out of your way to let you build what you need.
You can find the CanCan gem in Github: https://github.com/ryanb/cancan
It's simple to use, and the documentation is straightforward and easy to follow.

How to maintain both admin and user with devise gem?(Rails 4)

I am using rails 4 with devise gem and following this article -"https://github.com/plataformatec/devise/wiki/How-To:-Add-an-Admin-Role", I created a model for admin also. Now I have both admin and user model. How can I get the current_admin as in application_helper, these are present:-
def resource_name
:user
end
def resource
#resource ||= User.new
end
def devise_mapping
#devise_mapping ||= Devise.mappings[:user]
end
Please help to main a admin session. Thanks in advance
Devise's current_X helper methods are defined like this:
def current_#{mapping}
#current_#{mapping} ||= warden.authenticate(scope: :#{mapping})
end
where mapping is the role type. *
As such, you should be able to access the current admin using:
current_admin
If this doesn't work, I would recommend checking to make sure that devise has been properly instanciated on the Admin model, as follows:
class Admin < ActiveRecord::Base
devise :database_authenticatable # additional attributes, i.e. :trackable, :lockable
end
*Source: Line 106 of https://github.com/plataformatec/devise/blob/master/lib/devise/controllers/helpers.rb
If you continue to experience issues with this, it might be worth reconsidering whether you need to separate administrators and users using different models, instead of simplifying both the model and the implementation by adding an administrative attribute to the users model.
Edit:
To give you an idea as to how much of a negligible performance difference there would be querying a single users table for administrative users vs having two tables, I seeded a MySQL database (running on XAMPP for OSX, so it is by no means heavily optimized) with 1000 user records, with only 2 of the users having 'isAdmin' set to true.
Query using SQL (Selecting Admins from a pool of 1000 Users):
SELECT * FROM `users` WHERE isAdmin = 1
Result:
Showing rows 0 - 1 (2 total, Query took 0.0004 seconds.)
Query using SQL (Selecting Admins from an Admin table):
SELECT * FROM `administrators`
Result:
Showing rows 0 - 16 (17 total, Query took 0.0003 seconds.)
This is by no means an incredibly thorough example - it was just to provide an idea as to the minimal difference when used like this. Active Record may be slightly quicker/slower, but I can't imagine it would be too different to the results above.
Personally, I find it easier to add an 'isAdmin' (or similar) attribute to my users model, like this:
Active Record migration (db\migrate\TIMESTAMP_create_users.rb)
class CreateUsers < ActiveRecord::Migration
def change
create_table :users do |t|
t.string :username
t.boolean :isAdmin, default: false
...
end
end
end
and then filter between Users and Administrators like so:
Users, except Admins
users = User.where(:isAdmin => false)
All users
allUsers = User.all
Admins only
admins = User.where(:isAdmin => true)

Resources