Can I use cancan with many resources? - ruby-on-rails-3

I use devise and created two models, Doctor and Patient.
When I write load_and_authorize_resource cancan gives an exception
undefined local variable or method `current_user
How can I use cancan for both of them?

There is a method called current_ability in the Application Controller, you can simply overwrite it.
Changing current_ability documentation
It will look something like this
def current_ability
#current_ability ||= Ability.new(current_user)
end
But you may change it to
def current_ability
if current_doctor
#current_ability ||= Ability.new(current_doctor)
else
#current_ability ||= Ability.new(current_patient)
end
end
That will allow the initialize method in the app/models/ability.rb to recieve either the current_patient or the current_doctor depending on the case.
You could customize it to look something like this
class Ability
include CanCan::Ability
def initialize(resource)
if resource.class == Patient
can, Read Something
can, Edit Other
.
.
.
else
can, Read Something
can, Edit Other
.
.
.
end
.
.
.
I hope this help, you can also see the cancan Full doc

You can find a complete configuration of Devise and Cancan here: Part 1 Part 2

Related

Devise + Declarative_authorization + role_model + different users model name : undefined method `current_user'

I do have this famous error : "undefined method `current_user'" with declarative authorization, though I set up this variable in the application_controller.rb :
class ApplicationController < ActionController::Base
protect_from_forgery
before_filter :set_current_user
protected
def set_current_user
Authorization.current_user = current_admin_utilisateur
end
end
I'm using a table called "admin_utilisateurs" instead of "users". Which was activated in Devise with : "rails generate devise admin_utilisateur"
Devise is working great.
For info, I customized my users table (admin_utilisateurs) with "roles_model" gem, So that I do have an attribut roles_mask that allows me to manage different roles while providing a role_symbols method for declarative authorization.
The problem is now that I got this strange error though the Authorization.current_user is set by the application_controller.rb.
This is the begning of one my resource controllers that procude the error :
class PubResponsablesController < ApplicationController
before_filter :authenticate_admin_utilisateur!
filter_resource_access
...
end
I search by google for this error, but none of the results provide a working solution.
Could anybody help me on this ?
Many Thanks
Ok this is the final answer.
I modified my app/controller/application_controller.rb because I don't use the #current_user instance variable in the views :
class ApplicationController < ActionController::Base
protect_from_forgery
# This is mandatory if you want to secure as well your app/models
before_filter :set_current_user
# This method is required by declarative_authorization on every controller
# that is using filter_resource_access (or any other declarative_auth.. mechanism)
def current_user
current_admin_utilisateur
end
protected
def set_current_user
Authorization.current_user = current_admin_utilisateur
end
end
As I said I'm using the following gem in collaboration :
gem devise for the authentication
The user-model-name is "admin_utilisateur" instead of "user", but it could have been : account, member, group or what you need.
gem role_model to provide a brillant role method "role_symbols" to my user model
*The method role_symbols was returning a "Set" subclass instead of an "Array" but after quick post on Github, the developer (martinrehfeld) fixed this compatibility issue in a lightning matter of minutes. Great !*
gem declarative_authorization to provide access management based on roles.
My will to use a different model name than "user" is confirmed to work by the following post.
The only thing that declarative_authorization needs is the current_user method on each controller. As I'm using a different model name with Devise (such as admin_utilisateur, account, member, ...) the helper created by devise have a different name. Instead of current_user, it is current_admin_utilisateur (or current_account, current_member). So I have to create my own current_user method.
The role_symbols method required by declarative_authorization is provided by role_model gem.
I hope this will help other developer cause I spent two days to sort out how all this fabric works together. Devise took me even more with routing issues.
My few cents to RoRrrr ;-)
Ok I managed to solve this error by modifying my app/controller/application_controller.rb :
class ApplicationController < ActionController::Base
protect_from_forgery
before_filter :set_current_user
def current_user
#current_user = current_admin_utilisateur
end
protected
def set_current_user
Authorization.current_user = current_admin_utilisateur
end
end
I just created my own current_user method that create an instance variable #current_user. This one is initialized with the value of current_admin_utilisateur which is based on Devise helpers and my customized user model called admin_utilisateur. (my model could as well be called acount, member, or whatever...)
I placed my current_user method in application_controller in order that it to be available in every controller of my application.
Now, I'm getting another error :
User.role_symbols doesn't return an Array of Symbols (#<RoleModel::Roles: {:developer, :admin, :coordinator, :manager, :assistant, :distributor, :exporter, :historian}>)
I don't understand because the roles_model gem provide an alias method 'role_symbols' to the admin_utilisateur model.

How to call the ApplicationController helper method in model? RoR

Guys I've a helper method in ApplicationController as follows
class ApplicationController < ActionController::Base
helper_method :current_user
end
And I want to call it in my model (say Project) as follows:
class Project < ActiveRecord::Base
after_save :update_other_tables
private
def update_other_tables
# if project saves, Create a new insteance into User Projects
#userproject=UserProject.new(
:user_id=>User.current_user.id,
:project_id=>self.id
)
#userproject.save
end
Here, I'm getting error like undefined method `current_user'
So, How to call the this method in model? please help me
I would do it in the following way:
class ApplicationController < ActionController::Base
# Assignment method
def current_user=(user)
#current_user = user
end
# Returns the current user, nil otherwise
def current_user
#current_user
end
end
Now when a user logs in you can set:
current_user = the_user
And from the point onwards you can do:
current_user.id
Hope this helps.
not a ruby guy but I am guessing the User here isnt referring to the User that is present in your controller namespace and that is why there is no method current_user on the object.. You probably need to pass the User object to your helper method or just pass the id..
The above theory is from my experience dealing with something similar in .Net MVC so please correct me if I am wrong and I will delete the answer..
P.S: was too long of a comment

Where to override current_user helper method of devise gem

How can i override current_user of devise gem.
Actually I need to add web services for mobile-app.
Currently devise is managing session and 'current_user' for web-application.
Now Mobile app will send user_id to the server. I need to override current user like this
def current_user
if params[:user_id].blank?
current_user
else
User.find(params[:user_id])
end
end
Should I need to modify devise gem as plugin ? or something else ?
Kindly explain in detail as I am new in rails.
Kind regards,
According to the module Devise::Controllers::Helpers, current_user (together with all other devise helpers) is added to ApplicationController, which means that you can override it in this way:
# in application_controller.rb
def devise_current_user
#devise_current_user ||= warden.authenticate(scope: :user)
end
def current_user
if params[:user_id].blank?
devise_current_user
else
User.find(params[:user_id])
end
end
The other answer suggesting aliasing the method is actually not the best solution. Doug English's comment is the best solution:
# In ApplicationHelper
def devise_current_user
#devise_current_user ||= warden.authenticate(:scope => :user)
end
Here's the reason:
Suppose you're including your ApplicationHelper in your controller. If you need a method in your ApplicationHelper that relies on devise_current_user, given the alias solution inside the controller, you're out of luck.
But with the explicit method definition above, you can move the definition to the helper and call it from other methods and you still get to include it in the controller.
This is useful, for example, when you're developing a user impersonation solution and you need to show two different users in the view, one for the real admin (devise_current_user) and the other, the impersonated user (current_user).
Limbo-Peng's answer is great, but can be improved a little to make sure only admins can do this:
You'll need to also define a is_admin? method or is_admin attribute on the User class.
You may also want to use a different key than user_id, so it will never conflict with your regular parameters.
# to impersonate another user, e.g. for customer support
# only admins can do this..
#
alias_method :devise_current_user, :current_user
def current_user
if ! params[:user_id].blank? \
&& devise_current_user && devise_current_user.is_admin?
User.find(params[:user_id])
else
devise_current_user
end
end
Assuming we can trust our session data (which relies on whether you put user input in there without proper authorization or not), this might work as a Concern:
module Concerns
module ControllerWithImpersonation
extend ActiveSupport::Concern
included do
helper_method :devise_current_user
end
def current_user
if session[:impersonated_user_id].blank?
devise_current_user
else
User.find(session[:impersonated_user_id])
end
end
def devise_current_user
#devise_current_user ||= warden.authenticate(:scope => :user)
end
end
end
I'm using this in a project for now.
A minor question (in the answer, sorry) ... should I be aware of any changes in Devise or Warden that make devise_current_user above outdated?

How to access devise current_user from model

Just start to develop with devise for my app authentication, however running into trouble with accessing current_user from a model. I have no problem accessing this from a controller.
For my scenario I need to get the current user id to keep track who has edit the blog post, which I save the user id with before_save filter.
Appreciate your help!
Thanks
You can only pass it down in a parameter.
class SomeModel
def my_nifty_method(user)
end
end
in controller:
class SomeModelsController < ApplicationController
def some_method
sm = SomeModel.new
sm.my_nifty_method(current_user)
end
end
You should be able to do this in your model. 'c' here being the model object and setting it's user_id attribute before create, or save in your case.
before_create {|c| c.user_id = Authorization.current_user.id}
I'm somewhat new to RoR so be warned.
Why are you trying to access to that method in the model?
You can access to the ID attributes via self.id (within the model)

before_filter not inheriting from parent controller correctly?

Sorry if this may be a stupid question but I'm unable get my filters to inherit the way the Rails 3 documentation is saying it should.
Specifically I have an Admin controller that was generated via:
rails generate controller admin
I added only a single action to the admin controller, the before filter & the private filter method
class AdminController < ApplicationController
before_filter require_admin_creds
def index
end
private
def require_admin_creds
unless current_user && current_user.admin?
flash[:error] = ...
redirect_to ....
end
end
end
I next then created my nested resources under the admin section with:
rails generate scaffold admin/model
While my admin index is indeed getting the filter, the admin/model index (or any other actions) are not. What is happening under the hood here that I must have excluded?
Thanks in advance.
Make require_admin_creds a protected method, not private.
Did you change:
Admin::ModelController < ApplicationController
to
Admin::ModelController < AdminController
?
This creates the inheritance, not placing the model controller into the admin namespace.
Double-check your syntax. You have:
before_filter require_admin_creds
but I think that should be:
before_filter :require_admin_creds
where you use a symbol, rather than a variable/method name.