I am using devise. In a view partial, I have:
<%= simple_form_for resource, :as => :user, ...
In my application helper:
# #see http://stackoverflow.com/questions/4541075/what-is-the-devise-mapping-variable-and-how-can-i-include-it
def resource_class
devise_mapping.to
end
# #see http://stackoverflow.com/questions/4081744/devise-form-within-a-different-controller
def resource_name
:user
end
def resource
# with some controllers (homepage, static, this method is called)
# with other controllers (Item, it's not called: I don't see the abort call)
abort('should stop here')
#resource ||= User.new
end
def devise_mapping
#devise_mapping ||= Devise.mappings[:user]
end
On some controllers, my form works fine. On others, I get this error:
Couldn't find Item without an ID
The problem is that the method I have defined is not called. With some controllers, ApplicationHelper.resrouce is called, on others, it's an other method resource (Item.resource ?), and I don't know where it's defined.
How the method resource can be defined elsewhere ? Why is the method resource of my application helper not called on certain pages ?
Note: I am using the gem inherited_resources that defines the method resource. Maybe this create the conflict. But I don't know how the method is inherited
After seeing http://railscasts.com/episodes/230-inherited-resources, it all became clear.
I defined resource in ItemsController:
class ItemsController < InheritedResources::Base
# #see http://stackoverflow.com/questions/16831095/devise-the-method-resource-is-not-called-properly
def resource
# this solves my problem. It looks super wrong tough
#resource ||= User.new
#view_context.resource # tried that, but doesn't work
end
# ...
And now it works. The method resource was being re-defined by the gem for that controller...
Related
I'm trying to integrate pundit with my active admin and devise configuration. But the app works weirdly. It takes in model/record as current_user.
my policy file:
class AdminUserPolicy
attr_reader :current_user, :model
def initialize(current_user, model)
Rails.logger.info '--------------- initialize called-------------------'
Rails.logger.info current_user
Rails.logger.info model
#current_user = current_user
#record = model
end
def index?
#current_user.admin?
end
end
controller:
controller do
include Pundit
protect_from_forgery
rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized
before_action :authenticate_admin_user!
def index
authorize current_admin_user
super
end
private
def user_not_authorized
flash[:alert]="Access denied"
redirect_to (request.referrer || admin_root_path)
end
end
The log is as follows:
--------------- initialize called----------------------------------------
#<AdminUser:0x007f27733f8a80>
Completed 500 Internal Server Error in 364ms (ActiveRecord: 312.8ms)
NoMethodError (undefined method `admin?' for nil:NilClass):
app/policies/admin_user_policy.rb:13:in `index?'
app/admin/dashboard.rb:19:in `index'
Rendering /usr/local/rvm/gems/ruby-2.3.4/gems/actionpack-5.0.6/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb within rescues/layout
Rendering /usr/local/rvm/gems/ruby-2.3.4/gems/actionpack-5.0.6/lib/action_dispatch/middleware/templates/rescues/_source.html.erb
Rendered /usr/local/rvm/gems/ruby-2.3.4/gems/actionpack-5.0.6/lib/action_dispatch/middleware/templates/rescues/_source.html.erb (4.8ms)
Rendering /usr/local/rvm/gems/ruby-2.3.4/gems/actionpack-5.0.6/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb
Rendered /usr/local/rvm/gems/ruby-2.3.4/gems/actionpack-5.0.6/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb (2.5ms)
Rendering /usr/local/rvm/gems/ruby-2.3.4/gems/actionpack-5.0.6/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb
Rendered /usr/local/rvm/gems/ruby-2.3.4/gems/actionpack-5.0.6/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb (1.1ms)
Rendered /usr/local/rvm/gems/ruby-2.3.4/gems/actionpack-5.0.6/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb within rescues/layout (39.5ms)
The weird part is, according to logs, current_user=nil and model=#<AdminUser:0x007f27733f8a80>
I swapped current_user with model my policy file as
def index?
#record.admin?
end
And it works!
I don't understand this strange behaviour.
Pundit policy doc says that it calls the current_user method to retrieve what to send into the first argument of the initialize method inside the Policy class. If you have configured ActiveAdmin to retrieve the current logged in user by using current_admin_user, then you have to override the pundit default method in your ApplicationController class like so: Ref
class ApplicationController < ActionController::Base
// ...
def pundit_user
current_admin_user // or whatever based on ActiveAdmin initializer config
end
end
In order to make the defined policy working, you have to invoke authorize inside the controller action with the instance of the corresponding policy model. So if you have a PostPolicy and you want to authorize the update action, you have to do the following:
controller do
def update
#post = Post.find(params[:id])
authorize #post // the current user will be automatically sent to the PostPolicy
super
end
end
The authorize method automatically infers that Post will have a matching PostPolicy class, and instantiates this class, handing in the current user and the given record. It then infers from the action name, that it should call update? on this instance of the policy. In this case, you can imagine that authorize would have done something like this:
unless PostPolicy.new(current_user, #post).update?
raise Pundit::NotAuthorizedError, "not allowed to update? this #{#post.inspect}"
end
Having all these, in your case, if you want that a user should be authorized before viewing the list of all users, you can define the AdminUserPolicy like you have done already. Then in the index action of your AdminUserController,
controller do
def index
#users = AdminUser.all
authorize #users // NOT `authorize current_admin_user`
super
end
end
You can pass a second argument to authorize if the name of the permission you want to check doesn't match the action name. For example:
def publish
#post = Post.find(params[:id])
authorize #post, :update?
#post.publish!
redirect_to #post
end
I've got the following initializer:
app/config/initializers/store_location.rb
module StoreLocation
def self.skip_store_location
[
Devise::SessionsController,
Devise::RegistrationsController,
Devise::PasswordsController
].each do |controller|
controller.skip_before_filter :store_location
end
end
self.skip_store_location
end
Relevant parts of my ApplicationController:
class ApplicationController < ActionController::Base
protect_from_forgery
before_filter :convert_legacy_cookies
before_filter :store_location
alias_method :devise_current_user, :current_user
def current_user
# do something
end
private
def store_location
# store location
end
Plus this in
config/environments/development.rb
Foo::Application.configure do
# normal rails stuff
config.to_prepare do
StoreLocation.skip_store_location
end
end
If I let RSpec/Rails run the self.skip_store_location I'm getting the following error:
/foo/app/controllers/application_controller.rb:7:in `alias_method': undefined method `current_user' for class `ApplicationController' (NameError)
If I remove the call, everything is back to normal (except the filter is run, as expected). I'm guessing that I mess up dependency loading somehow?
The problem is that you use alias_method before the method is defined in ApplicationController. To fix the problem, move the line
alias_method :devise_current_user, :current_user
below
def current_user
# do something
end
It's a bit misleading that the error appears when running skip_store_location. I assume that happens because skip_store_location loads several controllers, and one of them is a subclass of ApplicationController.
I have authlogic set up and want to use declarative_authorization. In Ryan Bates's railcast #188 he uses
before_filter {|c| Authorization.current_user = c.current_user}
If I do the same I get an error:
`private method 'current_user' called for #<UsersController:0xa6025e8>`
The current_user method is in application controller and is private (as in the Authlogic setup example) and if I make it public, this works. It also works if I just say current_user (current_user method is also a helper) instead of c.current_user in the before filter block.
So would it actually be ok if I would just do before_filter {Authorization.current_user = current_user}?
Try this:
application_controller.rb
Add before_filter :set_current_user
Put this method into protected
def set_current_user
Authorization.current_user = current_user
end
How can I restrict access to ALL devise controllers by IP address'? I am trying to allow only users from a specific IP address to view the admin interface / pages.
I found this approach. Which is to include a restrict_access method in the before filter. However, its a bit repetitive if I have to copy this method on all the Devise controllers that I currently use.
Is there a better approach?
class Admin::SessionsController < Devise::SessionsController
before_filter :restrict_access
# Needed to restrict access to a set of IP's only. We don't want random users trying to access the admin interface
def restrict_access
if Rails.env == 'development' or Rails.env == 'test'
whitelist = ['59.120.201.20', '59.120.201.21'].freeze
else
whitelist = ['59.120.201.20', '59.120.201.21'].freeze
end
unless whitelist.include? request.remote_ip
redirect_to root_path, :notice => 'Access denied!'
end
end
...
Build a class like the following and place it in RAILS_ROOT/lib/blacklist_constraint.rb.
class BlacklistConstraint
def initialize
if Rails.env == 'development' or Rails.env == 'test'
#whitelist = ['59.120.201.20', '59.120.201.21'].freeze
else
#whitelist = ['59.120.201.20', '59.120.201.21'].freeze
end
end
def matches?(request)
!#whitelist.include?(request.remote_ip)
end
end
... and in your routes.rb file...
match "*", :constraints => BlacklistConstraint.new, :controller => "blacklist", :action => "my_access_denied_action"
You may need to load the class in an initializer, or modify your config.autoload_paths += %W(#{Rails.root}/lib) in config/application.rb (Rails3.x).
I believe all of the Devise Controller extend your Application controller, so you could put the method in the ApplicationController as a protected method then you only need to call the
before_filter :restrict_access
on each devise controller.
I want to override Devise's RegistrationsContollers' create action so that when a user signs up, I can associate a UserProfile model with that user.
So, following the guidelines in the Devise Readme, I override the action:
#File app/controllers/registrations_controller.rb:
class Users::RegistrationsController < Devise::RegistrationsController
def create
# some code here..
self.user_profiles.build #Error (no method `user_profiles`)
current_user.user_profiles.build #Error (current_user is nil)
some other way???
end
end
#File routes.rb:
devise_for :users, :controllers => { :registrations => 'users/registrations' }
Devise is creating a record in the users table, but how do I associate a UserProfile with that record?
I've tried googling but I simply can't get this to work! Any help is much appreciated.
(I'm now using Devise 1.1.5 on Rails 3.0.3)
SOLVED:
Adding solution for benefit of others:
#File app/controllers/registrations_controller.rb:
class Users::RegistrationsController < Devise::RegistrationsController
def create
super
#user.build_user_profile
#user.user_profile.some_data = 'abcd'
#user.save!
end
end
self refers to the contoller not the model in this context.
Also, does the user model have many UserProfiles? Otherwise if they don't (ie they only have one), then you should use #user.build_user_profile, not #user.user_profiles.build
I'd also recommend doing this at the model level, not the controller level, using a callback such as before_create or after_create, ie:
class User < AR
has_one :user_profile
after_create :build_profile
def build_profile
self.build_user_profile
...
end
end