declarative authorisation user switching - ruby-on-rails-3

Has anyone tried to add user role switching using the authlogic/declarative authorisation gems?
My requirement is that a user can have many roles (e.g. Author, Teacher) and can switch role within the application.
I have a two ideas of how I might approach this:
Adding another boolean attribute (active), to the user_roles join
table
Copying the switched role_id into the users table and working
off that
I read the declarative authorisation readme and can't see anything that appears to be built in. Any ideas would be appreciated

Just looked back into this today and the solution is easy enough. I went with adding a boolean attribute to my many-to-many user_roles join to avoid duplication. The join now has the following attributes:
id | user_id | role_id | active
The role_symbols method in my user model, which is used to hook in the authorization_rules.rb DSL now looks like:
def role_symbols
user_roles.where(:active => true).map do |user_role|
user_role.role.name.underscore.to_sym
end
end
Now a users role sets itself to which ever role has active true in the user_roles table.
User switching is easy too (from the user model)
def self.set_active_role(user_id, role_id)
UserRole.where(:user_id => user_id).update_all(:active => false)
activate_role = UserRole.where(:user_id => user_id, :role_id => role_id).first
activate_role.update_attributes(:active => true)
end
Thought it might help someone in the future

Related

Get relation of a collection in rails

So simple but can't find how to do it in rails. I have a specific active record collection of users. Something like users = User.where(blabla). Given this collection is there a simple way to get all posts that those users have? Similar to User.posts, only for all users in that collection.
Post belongs_to User, User has_many posts.
Thanks!
Assuming that your Post model has a user_id with an association called "user", you can do something like this:
Post.where(user_id: User.where(blablah))
or
Post.joins(:user).where(users: {<user conditions>})
You'll need to be able to use the Hash form for the user conditions to use the second option. For example:
Post.joins(:user).where(users: {role: 'member'})
If your users query is more complex, you can create a scope for it:
class User < ApplicationRecord
scope :special, -> { where(< user conditions go here>) }
end
And then merge it with the Post query:
Post.joins(:user).merge(User.special)

Retrieving Records If Their Association Exists in Rails 3.2

I have a User model that has a has_one association with a UserInfo model. I'd like to be able to find all Users that have a UserInfo model. I've tried everything I can think of but nothing seems to work in Rails 3.2:
Users.joins(:user_infos).where("user_info IS NOT NULL")
Users.joins(:user_infos).where("user_infos.id IS NOT NULL")
In each case, I just get back all of the users even though, currently, only 1 of my 1000 users actually has a UserInfo associated with it.
Try this and let me know the response:
in your user.rb write this
def self.available
joins(:user_infos).uniq
end
Joining the user_infos association will remove Users without any user_infos ... SQL is doing your work here.
Now where You want those Users with having user_info, you can get them by using
User.available

Roles and Permissions model in database

I have a user model and lots of other models in my project, to create a RBAC system I implemented role and permission. User has_and_belongs_to_many roles and Role has_and_belongs_to_many permissions.
class Permission
include Mongoid::Document
field :ability, type: String
has_and_belongs_to_many :roles
belongs_to :permission_for, polymorphic: true, dependent: :destroy
index({ability: 1,permission_for_id: 1},unique: true)
end
class Role
include Mongoid::Document
field :name, type: String
has_and_belongs_to_many :permissions
has_and_belongs_to_many :users
belongs_to :role_for, polymorphic: true
index({name: 1,role_for_id: 1},unique: true)
end
and in User model I have :
Class User
include Mongoid::Document
.
.
.
def able?(scope,model,action)
# There must be something to load and check permissions
end
end
Role defined in a scope (role_for) and Permission defined in Models in Role's scope (project is scope and task is model in that scope) with permission_for.
In User model I need to get data from database and check if user is able to do that action, in large amount of data it take too long. able? function I've implemented is simple, it just load every user's role and every role's permission and then check if permission's ability is equal to action and then return true or false!
Is there any gem or code do something like that? and if there's not, could you give me advise on how to implement role and permission in such way, with much less database load?
Many many tahnks
Edit 1
Ok, I've created this relation and managed to use it in my models, performance in normal use is ok, but when I want to get huge amount data it's very slow. I need to cache roles and permissions in scope model, how can I do such thing? Is there any plugin for rails can do that for me?
Our product is near soft-launch and I implemented that solution but with minor tweaks :
How I've done RBAC :
Project <--- Role <---> Permissions ---> Task
^ (ability)
|
|
V
User (able?)
This schema is very simplified version of final implementation but the concept is same
User.able?(model,ability) returns true if union of all permissions of user's related roles witch are related to model has a permission with ability.
permission >> View Edit Delete
V Role --------------------------------
1 true false false
2 true true false
3 false true false
--------------------------------
result true true false
I case of user has role 1,2,3 then user can view,edit but can't delete
To solve Performance Issue and Database Hit used russian doll caching, for each role I cache Hash representation of permissions :
role.get_permissions
# returns {modelID => ['view'], model2ID => ['view','edit']}
And then merge this all this hashes for user and again cache that new hash.
In each call of able? method of User class I get that hash (from cache or if changed generate new from database) and Job Done :)
Our worst problem about this caching was cache expiry. So we decided to add new functionality to our ORM (MongoID)
Adding or removing permissions from role will update an attribute in role model (without updating it's timestamps)
For role-user on add/remove/edit role we do so and also for project-role relation.
But for task-permission we've done nothing, because permissions will never change (ability and ID is important).
For role-permission relation update won't trigger update_permission on role.
Hope this help for anybody reach this point.

Assign user roles with CanCan?

Ive setup a application with two basic user roles.
A admin who can manage everything and a normal user and can create things (jobs & clients) but can not delete.
I'm a bit stumped though as to how can assign roles to the users. I assume it would be a case of adding a role column to my database? As user's are only created by the admin user. I need to add some sort of collection select to my form?
Can anyone point me in the right direction?
Edit: I tried the One role per user instructions as detailed on the cancan wiki here but i'm recieving the error uninitialized constant User::ROLES
Found an answer :)
I inserted this code..
ROLES = %w[admin user]
def role_symbols
[role.to_sym]
end
Into my User model and put <%= f.collection_select :role, User::ROLES, :to_s, :humanize %> into my form :)

Howto redirect users based on their roles in rails 3?

I am trying to figure out how to redirect users on certain URL based on their role, after they log to the Ruby/Rails3 application.
So far, I have used authlogic gem for authentification and cancan gem for role setting.
Roles are just like this (defined in app/models/user.rb):
class User < ActiveRecord::Base
acts_as_authentic
ROLES = %w[admin customer demo]
end
Now there is app/controllers/user_session_controller.rb which is taking care of logins.
I would like to make something like this:
for r in User.role
if r == "admin"
redirect_to admins_url
else
redirect_to users_url
end
end
This is not working because of the following error:
"undefined method `role' for #<Class:0xb5bb6e88>"
Is there a simple or elegant way how to redirect users to the certain URLs according to their roles?
(Roles are defined in mysql column 'role' in the users table.)
The for r in User.role is confusing. Are you trying to access the array of ROLES defined on the class or are you trying to access the role value of the current user?
If you are trying to access the array of ROLES, then use User::ROLES.
Using authlogic, one typically defines the current_user in the application_controller. So the role of the current user can be found using current_user.role
So your code could look something like
if current_user.role == "admin"
redirect_to admins_url
else
redirect_to users_url
end
You should definitely check out CanCan. It is a pretty logical way to manage user roles and abilities.