We're using cancan to authorize access to ~130 controllers, as things currently stand, this process takes ~100ms per authorization .. that is super slow (it literally takes around a second to just render the menu bar)
Is there techniques for breaking up cancan to simplify the work it needs to do per authorization, or in general speeding it up?
Does anyone have any cancan experiences to share?
Let me try to explain a bit better then .. We have cancan combined with a custom role system. Each role has a number of permissions, and based on if the user is allowed to do certain things, the cancan rules get dynamically built allowing it. In the end there is just one ability.rb file with a few hundred lines of rules which are dynamic based on the currently logged in user's role's permissions. (the roles are dynamic too).
The DB queries are already optimized, everything is eager loaded ahead of time and there is no wasteful queries done to fetch any of that stuff, but somewhere in there 100ms pass per check.
Related
If I want to build a Rails app that has two different types of users, let's say one type is called players and the other one is owners, what is the best and most efficient approach to modeling the app?
Things to take into account:
There should only be one Login, but different Registration forms that Owners/Players can use.
Owners can have access to a control panel but Players cannot.
Owners cannot share any of Players capabilities, but both need to be able to perform Login/Registration.
I am not using Devise, so please do not suggest it.
Different Approaches I've considered:
Using cancancan gem, but it does not really seem to meet my needs in the sense that I am not looking to create a user/admin hierarchical approach but rather a if you're a Player, then you can see these pages and perform these actions but Owners cannot and vice versa. Almost like splitting the app in two. cancancan seems that it would treat Owners as "Players with extra privileges", not different privileges entirely.
Creating separate models with separate login and registration forms, which seems like a disaster waiting to happen. One small mixup between a Players table and the Owners table, especially with the primary keys, and that will be a world of trouble where people could end up logging in to the wrong accounts.
Creating a polymorphic or has_one relation toward an Account model, which so far, seems like the best way to probably go about it. If I created a polymorphic Account model, I can store different types of Players/Owners, but how could I compare login credentials against all types?
I had been trying to find something on this matter regarding how to map this out and was surprised to not find an information on how to do this without using Devise. If anyone has any good links they can point me to that also address this matter (without Devise), please leave them in your answer! Thanks.
I'd suggest one User class with a type attribute that determines whether the user is a Player or an Owner (single table inheritance). This way you keep the registration logic in one place but can customize the forms depending on the user's class.
There must be alternatives to cancancan that help with what you want to do, or you can implement helpers yourself:
def can_access_control_panel?
current_user.is_a?(Owner)
end
You have to have a way to separate one user from another. One way is to add an attribute to the User table so you can call current_user.role and it will return "owner" or return "player".
I have used Pundit gem in the past. It lets you define which controller actions the current user is allowed to access. So as you create resources for your application, you can add a policy that specifies who is allowed to that given resource. This is the repo to the application.
This answer might help you.
I would like to use CanCan on top of a Mongoid based Rails 3 application. I would like to introduce general models for user, role and privilege. Essentially the authorization system shall authorize at a per action base. Therefore we want to store action x roles privilege objects.
Now when it comes to the ability DSL we could generate the abilities dynamically as an after_save hook in the proivilege model. But this results in a problem in production mode, cause these runtime changes only affect the server process where the privilege changes were made.
On the other hand one could reevaluate all (the users) abilities as before_filter in every controller. But that slowed down every request.
Just now, we are undecided how to solve this problem. I am thankful for every suggestion.
Regards
Felix
CanCan uses a simple authorization system based on a role column on the User model.
Here are some good links:
Abilities
Role Based Authorization
Why do you need to dynamically set up privileges? Unless you have a compelling reason to do so you are just introducing unnecessary complexity. Just define the roles you need to with the correct abilities (you can do this on a controller/action basis with cancan) and then assign those roles upon creation/update.
An application has users which can have one of three different account types. Let's call them Small, Medium, Large
I'm trying to figure out the best way to set limits depending on the account type. I looked into cancan but it seems to just have basic authentication options like read, manage, etc. whereas I am mostly going to be doing things like:
if user_has_hit_upload_limit?
# display a message
else
# display the upload form
What's the best way to do this? Am I correct in thinking cancan isn't ideal for this?
I started making a model class that sets all the limits and does all the checks, then added methods to ApplicationHelper to call that class, which is in turn called by the views.
Does this seem reasonable or not good? Is there a better way?
CanCan is for authorization based on abilities defined for users. It's probably not what you want for a quota type system like you described. Assuming you kept track of current usage and the user's quota both in the user model you could write a helper like this:
def user_has_hit_upload_limit?
current_user && current_user.upload_count < current_user.upload_limit
end
The hard part is actually tracking the usage and that will depend on what you are trying to do within your application.
You could still use CanCan define an ability that had a block condition but IMHO that's more complexity then it really needed.
I'm slowly but surely putting together my first rails app (first web-app of any kind in fact - I'm not really a programmer) and it's time to set up a user registration/login system. The nature of my app is such that each user will be completely separated from each other user (except for admin roles). When users log in they will have their own unique index page looking at only their data which they and no-one else can ever see or edit. However, I may later want to add a role for a user to be able to view and edit several other user's data (e.g. a group of users may want to allow their secretary to access and edit their data but their secretary would not need any data of their own).
My plan is to use authlogic to create the login system and declarative authorization to control permissions but before I embark on this fairly major and crucial task I thought I would canvas a few opinions as to whether this combo was appropriate for the tasks I envisage or whether there would be a better/simpler/faster/cheaper/awesomer option.
What about cancan by Ryan Bates?
Here you can get a complete visual guided implementation
Take a look at this, it might help:
Basic Rails 3 engine utilizing Authlogic, CanCan and Easy Roles
What about Devise? Take a look at the railscasts.com site.
I think the answer is an admin login and then check if the user has an admin flag, but I also thought of some other related questions.
Is it better to have an admin flag (attr_protected) in the same user table as non admins? or should i have an admin users table?
Should I create a separate rails application for the admin users? This might be overkill since they will both have to access the same datbase (not to mention it might be a huge pain to set up).
Any other suggestions? Right now I just need to secure a page or two so I even looked into HTTP basic or digest authentication as a temporary measure (the protected content is actually not THAT private/important). But... I don't know how to implement HTTP auth for specific actions, I have only seen how to implement it to prevent directory access.
Any direction and discussion would be great. I am sure other Stack Overflow users will benefit from this discussion.
Thanks!
Ryan Bates has a great three part series of Railscasts on this topic which should give you some food for thought:
Part 1: Where Administration Goes
Part 2: Restricting Access
Part 3: Super Simple Authentication
There are also three Railscasts on different authentication techniques:
RESTful Authentication
HTTP Basic Authentication
Authlogic
I'm using restful_authentication plugin for this purpose. And it is very simple to restrict access to any controller or any method. On example in controller add this function:
private
def authorized?
user.admin?
end
or
private
def authorized?
user.admin? if update? || create?
end
I defined admin? method in my User model. I also created update? and create? methods that check which action was called. In restful_authentication authorized? method is always run when accessing controller.
I would put everything in one application and in one table (don't create users and admin table). You can secure admin flag in your users controller by allowing to set this value only for existing admin users.
I think it depends on the type of administration.
If the view your administrators will have of the site is the same as a normal user's, but with additional privileges, I would go with an admin flag. (Or, as your needs expand, a full-fledged roles table.) This is a situation where everybody sees the same stuff, but administrators have access to various actions (delete? edit? ban? etc.) that normal users do not.
If the view your administrators need is wildly different than the normal site, I would recommend a completely separate Rails app that accesses the same database. For example, if your "administrators" are really help desk employees that are going to answer phone calls or deal with billing questions, they may have completely different views of the database (and perhaps ways to edit the data) that aren't available in the regular application.
The disadvantage to having multiple sites is that it is possible to have models (validations, associations, etc.) get out of sync. The disadvantage to having a single site is that you may end up inserting all sorts of ugly "if-admin" code in previously easy-to-understand portions of your site. Which problem is easier to handle depends on your requirements.