before_filter not inheriting from parent controller correctly? - ruby-on-rails-3

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.

Related

How to apply abilities to a non-restful controller in cancan

I'm new to rails and for the life of me I don't "get" cancan.
I've read this tutorial but can't figure out how to apply instructions to my situation.
In the cancan wiki there is:
an AdminController
a roll_logs action
In the ability class is says:
can :roll, :logs if user.admin?
I don't get what the :roll and :logs symbols have to do with the controller and the action?
All I want to do is say, if a user is an admin, give them access to the AdminController actions, otherwise don't, is this possible?
Yes this is possible.
The statement
can :roll, :logs if user.admmin?
means that when calling authorize! :roll, :logs an unauthorized exception gets thrown if the user isn't an admin.
So it doesn't have anything to do with a controller or an action, untill you make it so.
If you have a logs_controller for example with an action roll you could do something like this.
class LogsController < ApplicationController
def roll
authorize! :roll, :logs
# Rest of the roll functionality.
end
So in your example, you want to give users who are admin permission to access all admin controller actions.
You can achieve this like this.
ability.rb
class Ability
include CanCan::Ability
def initialize(user)
can(:manage, :admin) if user.admin?
end
end
admin_controller.rb
class AdminController < ApplicationController
authorize_resource :class => false
def foo
end
def bar
end
end
This will make sure that only admins can access the foo and bar actions of the admin_controller.
The :class => false statement means that you are not authorizing a resource, which is what we want since you are not for example authorizing a certain post or comment. You are just authorizing actions on a controller.

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?

Overriding Public folder based on namespace Rails 3

After searching for hours, I am posting my first question on Stack Overflow.
Given: I have the following route.rb:
resource: :non_namespaced_resource
namespace :namespaced_resource do # an example could be :admin
resources :one_nested_resource
resources :another_nested_resource
end
Desired Outcome: Is to have namespaced resources use its own assets and non-namespaced resources use the default assets, as shown below:
# non-namespaced
/public
/public/images
/public/javascripts
/public/stylesheets
# namespaced
/admin
/admin/images
/admin/javascripts
/admin/stylesheets
I have seen information on changing config/environments/*.rb or config/application.rb and use something like the following, but can not find any examples to do this based on the namespace.
Keep in mind there will ONLY be two namespaces ADMIN and PUBLIC.
config.action_controller.asset_path
config.action_controller.asset_dir
config.action_controller.javascripts_dir
config.action_controller.stylesheets_dir
config.action_controller.images_dir
Question: It seems this should be possible. So my question(s) is, is this possible? If so, how? Thanks in advance.
This is going on a bit more of a limb than I'm used to, but I hope it helps.
The first step would be to identify some code that is run only for your admin namespace. I would probably create another application controller, perhaps admin_application_controller.rb, that extended from your base application controller, and then extend from that controller for all your admin controllers. For example.
# your basic applications controller
class ApplicationController < ActionController::Base
protect_from_forgery
# etc
end
# your public controllers subclass it
class UsersController < ApplicationController
# stuff
end
# now your "specialized" admin controller
class AdminApplicationController < ApplicationController
before_filter :setup_asset_paths
def setup_asset_paths
Rails.application.config.action_controller.assets_dir = File.expand_path(File.join(Rails.root, 'admin'))
Rails.application.config.action_controller.javascripts_dir = File.expand_path(File.join(Rails.root, 'admin', 'javascripts'))
Rails.application.config.action_controller.stylesheets_dir = File.expand_path(File.join(Rails.root, 'admin', 'stylesheets'))
Rails.application.config.action_controller.page_cache_directory = File.expand_path(File.join(Rails.root, 'admin'))
end
end
# and your admin controllers extend from THAT instead
class AdminUsersController < AdminApplicationController
# more admin-y stuff
end
I'm quite interested to hear if this works for you, and if not, what problems you run into and what you find, so let us know! Good luck!!
[Edit] I've udpated the code above to reflect the members available:
pp Rails.application.config.action_controller
{:perform_caching=>false,
:assets_dir=>"/Users/BinaryMuse/src/postecho/public",
:javascripts_dir=>"/Users/BinaryMuse/src/postecho/public/javascripts",
:stylesheets_dir=>"/Users/BinaryMuse/src/postecho/public/stylesheets",
:page_cache_directory=>"/Users/BinaryMuse/src/postecho/public",
:helpers_path=>["/Users/BinaryMuse/src/postecho/app/helpers"]}