Are there are any authorization gems/examples for multi-tenancy applications?
I looked at CanCan and CanTango, but I wasn't able to find what I am looking for.
My app has Account, User, Relationship model. The relationship model has a relationship_type column which determines authorization level. Its value can be owner, moderator, editor, perhaps more in the future. Users can own/moderate many accounts, and an account can have many owners/moderators.
All the examples I found describe a single tenant application, whereas my app's authorization has to be scoped through the current account being viewed. A user can be a guest on one account and an owner of another, for example.
I'm starting to think my Relationship model is bad design and might have drawbacks, but I'm not sure what is a better alternative.
CanCan can indeed handle this because it lets you define arbitrary ruls. Your ability file might look like this:
def initialize(user)
can :read, BlogPost do |blog_post|
blog_post.account == user.account and user.relationship.in?([:owner, :moderator])
end
end
Try declarative_authorization, the authorization rules are defined in a single Ruby file using a DSL and you can define advanced rules based on objects' attributes and on the user role of course.
For example you can say something like this
role :moderator
has_permission_on :accounts do
to :manage
if_attribute :moderators contains {user}
end
end
declarative_authorization offer several methods you can use in models/controllers/views, for example in the account view you can use something like:
<% permitted_to? :update, #account do %>
<%= link_to 'Edit account', edit_account_path(#account) %>
<% end %>
You can have a look at the documentation and at the RailsCasts episode.
Related
I am working on a Ruby on Rails application that has two kinds of "Roles".
One will be where a user has multiple roles, such as "Admin", "Team Lead", etc. these will be defined in the seeds file and not generated by the user.
The other will be generated by a User and assigned to other users, such as "Chef", "Waiter", etc.
They are both similar in that they only have a name column. The former will be used with an authorization tool such as CanCanCan. I currently plan to allow users and roles to have many of the other using a has_many :through relationship.
class Role
has_many :user_roles
has_many :users, through: :user_roles
end
class User
has_many :user_roles
has_many :roles, through: :user_roles
end
class UserRole
belongs_to :user
belongs_to :role
end
My questions are:
Should the latter ("Chef", "Waiter", etc) be put in the same table? Separate tables?
Should I use some kind of inheritance?
What's a good practice for this situation?
I plan to use the term "Role" in the user interface for the latter, showing what "Roles" a user has. The former I guess is more about what privileges they have within the application.
If you go away from the technical side of roles and authentication, and try to describe the "things" from a more business oriented approach you make the distinction clearer for yourself.
What I understand is: You have a definition for a user of your application that is used to describe what authorization this user has, e.g. an "admin" has more rights than an "editor" or "community manager".
You also want these users of your application to be able to create names that are associated with (other?) users. Theses names have nothing to do with authorization, as I understood.
Maybe these names are more like tags, that people can assign?
I would keep both separated, as it shouldn't be able for a user to create a role, or modify existing roles, that could grant them access to admin features.
If you want to look at a tagging gem, I could recommend this one: https://github.com/mbleigh/acts-as-taggable-on I used this for several years and while it has its drawbacks, it's reliable.
I'd suggest having a look at rolify before rolling your own solution, as it will have solved some things that you'll have to reimplement / discover later (e.g. role queries, avoiding N+1 queries etc). It also integrates well with can?, and should work well for the authorisation part of your question.
Whilst it's not impossible to allow users to create new roles (by throwing an input form on top of Role.create), this starts to get messy, as you need to track which ones are for authorisation and which ones informative (and user created).
Since the two groups of things are for different purposes, I wholeheartedly agree with this other answer that it's cleaner to separate the user-generated entities, and look to implement them as tags. You may display all the "roles" together in certain views, but that doesn't mean that it makes sense to store them within a single table.
Side-note: if you do end up rolling your own solution, consider using HABTM here. The join table will still be created, but you won't have to manage the join table model. E.g.
has_and_belongs_to_many :users, join_table: :users_roles
Since you only have a limited number of roles, you could use a bitmask and store directly on the user model as a simple integer.
See this Railscasts for more information. That would be the most efficient, database and association wise, way to do this although perhaps not the simplest to understand. The only real restriction is that you can't alter the array of values you check against, only append to it.
Good luck!
I would create one more model, Permission, where you can create a list of all the permissions you want to manage under any given role.
Then have a many to many between Permissions and Roles.
From a UserRole instance then you will be able to list the permissions, and in the future you could add additional permissions to roles buy just running inserts in a couple of tables
Roles: Onwer, Chef, Cook, Waiter
Permission: can_cook, can_buy_wine, can_manage, can_charge_custome
Owner: can_manage, can_buy_wine, can_charge_customer
Chef: can_cook, can_manage
Waiter: can_charge_customer
Is would be a good start and you can evolve the role functionality to whatever your needs are without an external dependency.
Also, You can go just using Users table and adding role column as integer and give them a role code in default 0 integer.
#app/helpers/roles_helper.rb
module RolesHelper
#roles = {
'Default' => 0,
'Waiter' => 10,
'Chef' => 20,
'Superadmin' => 30
}
class << self
def list_roles
#roles.map{|k,v| [k,v] }
end
def code(str)
return #roles[str]
end
def value(id)
return #roles.key(id)
end
end
def require_default_users
unless current_user && current_user.role >= RolesHelper.code('Waiter')
redirect_to root_url(host: request.domain)
end
end
def require_superadmin_users
unless current_user && current_user.role >= RolesHelper.code('Superadmin')
redirect_to courses_path
end
end
end
access in controllers
sample:
class Admin::AdminController < ApplicationController
include RolesHelper
def sample_action_method
require_default_users #if non admin user redirect ...
puts "All Roles: #{RolesHelper.list_roles}"
puts "Value: #{RolesHelper.value(30)}"
puts "Code: #{RolesHelper.code('Superuser')}"
end
end
I am writing a rails application for an organization. Every user may have 1 or more roles and can only access certain controller actions depending on those roles.
For example, only admins can create, destroy and update certain fields of Users. Also, there are Teams which each have a team leader, and only the team leader can update certain information about the Team (like the member list, for example). However, Admins are the one who assign the team leader in the first place.
The specific details of my scenario are not important, I merely hope I described the situation where there are many different roles and permissions.
My question is: what gem to use? My first thought was CanCan, but the last commit was almost a year ago and there is no mention of Rails 4 compatibility. Is there a currently maintained alternative?
Your first guess was right, use cancancan and you'll be good with it.
EDIT Jul 24, 2015
I've been using cancancan for a long time now and it was always working great. I've recently started working on a project where Pundit is used for authorization.
It is awesome. It prompts you to define the policy for each resource and it feels more natural than one bloated Ability class.
For bigger projects, I would definitely recommend Pundit.
To control access to actions I'd recommend Action Access, it boils down to this:
class UsersController < ApplicationController
let :admin, :all
let :user, [:index, :show]
# ...
end
This will automatically lock the controller, allowing admins to access every action, users only to show or index users and anyone else will be rejected and redirected with an alert.
If you need more control, you can use not_authorized! inside actions to check and reject access.
It's completely independent of the authentication system and it can work without User models or predefined roles. All you need is to set the clearance level for the current request:
class ApplicationController < ActionController::Base
def current_clearance_level
session[:role] || :guest
end
end
You can return whatever you app needs here, like current_user.role for example.
Although it isn't required, it bundles a set of handy model additions that allow to do things like:
<% if current_user.can? :edit, :team %>
<%= link_to 'Edit team', edit_team_path(#team) %>
<% end %>
Here :team refers to TeamsController, so the link will only be displayed if the current user is authorized to access the edit action in TeamsController. It also supports namespaces.
You can lock controllers by default, customize the redirection path and the alert message, etc.
It's very straightforward and easy, I hope you find it useful.
Something that was suggested to me that we are now using is the petergate gem. Easy to use and very clean looking with a great rails feel.
Works well with devise.
Here is some examples from the readme.
If you're using devise you're in luck, otherwise you'll have to add following methods to your project:
user_signed_in?
current_user
after_sign_in_path_for(current_user)
authenticate_user!
This comes in your User.rb. Adding more roles is as easy as adding them to the array.
petergate(roles: [:admin, :editor], multiple: false)
Instance Methods
user.role => :editor
user.roles => [:editor, :user]
user.roles=(v) #sets roles
user.available_roles => [:admin, :editor]
user.has_roles?(:admin, :editors) # returns true if user is any of roles passed in as params.
Controller access syntax.
access all: [:show, :index], user: {except: [:destroy]}, company_admin: :all
Using a combination of a few tutorials and screencasts to set up Rails and CanCan to work with Devise etc. I've established User, Role, and the UserRole joining model for the has_many :through connection. And I only want some users to be able to assign certain roles, or create new accounts with those roles, etc.
Based on the documentation here, I feel like this should be pretty straightforward. What I have is essentially this:
if user
if user.role? 'admin'
can :manage, User
elsif user.role? 'submanager'
can :manage, User, roles: { name: ['basic', 'premium'] }
end
else
can :create, User, roles: { name: ['basic'] }
end
And then I'd simply test it in the form with:
- if can? :create, User, roles: { name: 'admin' }
= check_box 'user[role_ids]', 'admin'
Or, at least, that's the gist. And trust me I've tried every combination of singulars, plurals, role_ids, role, role: [ ]. So I don't think it's something as simple as that. No matter what I do, the can? check passes. Every time. Without fail.
I've found a lot of stuff out there on CanCan but I can't seem to find this specific thing. Is it because I'm trying to misuse CanCan, and I should be doing this some other way? Or … how do I do this?
I don't think this should be done in CanCan, as the control granularity of CanCan is on action. So if you really wanna to use CanCan to fulfill your intention, I think you should make the ”role change“ as a method call instead of a form submission.
the controller file
class UserController
def basic
user.change_role_to_basic
end
def premium
user.change_role_to_premium
end
end
the ability file
can :basic, User do
end
can :premium, User do
end
In my rails application, my Users have many Projects through Participants.
Currently I am using CanCan for authorizations. I have done some research and can't quite figure out how to do the following.
In my ability.rb file I want to provide :manage abilities to to Users on Projects. Currently that looks like:
can :manage, Project if user.has_role? :silver
My Participants table has user_id and project_id. How do I only give those manage capabilities to users that are associated with a project via the participants table?
I am currently using the following as a solution. I would prefer using a solution native to devise or cancan.
In the model.
def is_participant?(participant)
self.participants.exists?(:project_id => participant)
end
And in my view.
<% if current_user.is_participant?(#project.id) %>
I am trying to solve an issue I'm having with setting up admin on my site. Here is a simple version of my routes.
resources :users
resources :articles
resources :collaborators
end
resources :admin
Admin users are very similar to Collaborators, because they can edit and create Articles, but Admin has the ability to also c.r.u.d. Users, Collaborators, and Articles for every User. Collaborators can only c.r.u.d. Articles that are associated with the User they have been assigned to.
The part where this gets a little bit confusing is how to set up the controller and views that Admin uses for CRUD operations. As of now the way I am trying to implement it is to create separate CRUD views within admin/. The problem with this is that the functionality is so simimlar to a collaborator, I feel like it could be DRYer somehow.
Anybody know the most basic way to implement something like this? Thanks!
Update: I'd like to update this to say that it's super easy to google admin tools for rails. I'm more wondering how to implement this without an admin tool, or if that sounds like a bad idea, why?
The answer I came up with is to create Collaborators with an admin field true / false. I also set up CanCan and it's working pretty nicely.
Here is my CanCan ability.rb
def initialize(user)
if user.admin?
can :manage, :all
else
can :manage, Article, :id => user.article_id
cannot :index, Article
end
end
I just used "user" because it's more readable and shorter than collaborator and basically a user...
Try ActiveAdmin (http://activeadmin.info/). I think it is the most simplest way to build administrator backend for your application.