ForbiddenAttributesError, only in tests, after minor version upgrade - ruby-on-rails

I'm trying to upgrade from Rails 4.1.11 to 4.1.16. Several of my SessionsController specs fail after this upgrade. But when I try to log in in the development environment, everything seems to work fine.
I have the following code:
# sessions_controller.rb
def create
#identity = Identity.find_or_create(auth)
...
end
protected
def auth
request.env["omniauth.auth"]
end
# identity.rb
class Identity < ActiveRecord::Base
...
def self.find_or_create(auth)
where(auth.slice(:uid, :provider)).first_or_create
end
end
In my specs, I use a fixture in spec_helper.rb to supply the OmniAuth hash:
# spec_helper.rb
OmniAuth.config.mock_auth[:facebook] = OmniAuth::AuthHash.new({
:provider => 'facebook',
:uid => '1234567',
:info => {
:email => 'joe#bloggs.com',
...
}
}
When I try to use the OmniAuth attributes to find a user's identity, I get a ForbiddenAttributesError:
1) SessionsController GET :create not yet signed in sets the user ID in the session hash
Failure/Error: get :create
ActiveModel::ForbiddenAttributesError:
ActiveModel::ForbiddenAttributesError
# /Users/pat/.rvm/gems/ruby-2.1.2/gems/activemodel-4.1.6/lib/active_model/forbidden_attributes_protection.rb:21:in `sanitize_for_mass_assignment'
I understand the idea of strong parameters and whitelisting attributes in the params hash. But I can't use require on the OmniAuth hash (at least not directly), and I haven't had to in the past.
This is the example provided on OmniAuth's GitHub page:
class SessionsController < ApplicationController
def create
#user = User.find_or_create_from_auth_hash(auth_hash)
self.current_user = #user
redirect_to '/'
end
protected
def auth_hash
request.env['omniauth.auth']
end
end
Presumably, it works with recent versions of Rails.
How can I get my tests to pass? Here is the rest of the code if you want to take a look.

Have you tried whitelisting the parameters like this?
def auth
ActionController::Parameters.new(request.env["omniauth.auth"]).permit(...)
end

Try changing the rspec version in your gemfile to this:
gem "rspec-rails", '~> 2.14.0.rc1'

Related

cancan not working with activeadmin 0.4.4

I am not beeing able to make this work.
I have a project with activeadmin 0.4.4 and devise working, and I need to include role-based permissions to it, so I thought on CanCan. The roles could be 'administrator' or 'publisher', administrator can manage all and publisher only a post section.
I followed https://github.com/gregbell/active_admin/wiki/How-to-work-with-cancan-with-activeadmin, the AdminUser crud it is working fine, and the config.authentication_method = :authenticate_admin_user! was alredy uncomment.
In my ApplicationController I added the following:
# https://github.com/ryanb/cancan/wiki/exception-handling
rescue_from CanCan::AccessDenied do |exception|
respond_to do |format|
format.html do
redirect_to admin_root_path, :alert => exception.message
end
end
end
# https://github.com/ryanb/cancan/wiki/changing-defaults
def current_ability
#current_ability ||= Ability.new(current_admin_user)
end
And here is my Ability class:
class Ability
include CanCan::Ability
def initialize(user)
user ||= AdminUser.new
if user.role?('administrator')
can :manage, :all
else
can :read, :all
end
end
end
The Ability initialize method is not executing. I tried abort, -as the comments suggest- puts or p, and even sintax error and I have nothing. What am I mising?
I'm stuck here, I also tried http://www.activeadmin.info/docs/13-authorization-adapter.html#using_the_cancan_adapter but it is rising undefined method "authorization adapter", I am not sure if this is working with my activeadmin version. And adding a require active_admin/cancan_adapter rise a LoadError.
Thanks.
Finally I did the trick thanks to http://makandracards.com/jan0sch/13877-rails-activeadmin-and-cancan
Basically I need to add a controller block in the activeadmin register page for loading it:
ActiveAdmin.register Whatever do
#...
controller do
load_and_authorize_resource :except => :index
def scoped_collection
end_of_association_chain.accessible_by(current_ability)
end
end
#...
end

Rails4 + Authlogic + rspec

Using Rails 4, I am having issues getting Authlogic to see my faked UserSession.
I have set up pages#whoami to render the current user's email address, as a simplistic test.
class PagesController < ApplicationController
# before_filter :require_user
def whoami
render :text => current_user.try(:email) || 'anonymous'
end
end
in spec/spec_helper.rb:
require "authlogic/test_case"
include Authlogic::TestCase
and my rspec test:
require 'spec_helper'
describe '/whoami' do
setup :activate_authlogic
it "should tell me who I am" do
user = FactoryGirl.create(:user)
user.should be_valid
session = UserSession.create(user)
session.should be_valid
get '/whoami'
response.body.should == user.email
end
end
I updated my application controller to show the current session:
def require_user
unless current_user
raise "Current User Session is: #{ current_user_session.inspect}"
store_location
flash[:notice] = "You must be logged in to access this page"
redirect_to new_user_session_url
return false
end
end
With before_filter :require_user commented, I correctly get "anonymous". When I uncomment it, I see that my user session is nil. I tried looking through the authlogic code but got lost in Authlogic::Session:Persistence::InstanceMethods#persisting?
I'm trying to debug. Here's where I am so far.
Here, we try to set Authlogic::Session::Base.controller to the test's mock controller:
https://github.com/binarylogic/authlogic/blob/master/lib/authlogic/test_case.rb#L109
in my spec, I see that #controller is a Authlogic::TestCase::MockController
and in my spec, I see that Authlogic::Session::Base.controller is set to that Mock Controller.
However, I then check this:
class ApplicationController < ActionController::Base
...
def current_user_session
raise Authlogic::Session::Base.controller.inspect
...
end
end
and I see Authlogic::ControllerAdapters::RailsAdapter ... so somehow the controller is being set but isn't persisting. I'm wondering whether this has to do with the switch from Rails3 to Rails4?
Any insight into this would be appreciated.
Gem versions for those who are interested:
gem rspec-core (2.14.5)
gem authlogic (3.3.0)
gem rails (4.0.0)
Per https://stackoverflow.com/a/5803121, a request spec is just a thin wrapper around ActionDispatch::IntegrationTest. As such, there is no direct access to the session, unlike a normal controller spec.
Due to this, it isn't directly possible to log a user in directly with AuthLogic, which does rely on the session and cookies:
It first authenticates, then it sets up the proper session values and cookies to persist the session.
For request/integration/api/feature specs, a request directly to the login path will be necessary to set the proper session / cookies behind the scenes. The integration session will then be sent back (just like a normal web request) with the proper values.
To make life easier you can add a helper method, which you can include for request/integration/api/feature specs:
# spec/support/auth_logic_helpers.rb
module Authlogic
module TestHelper
# You can call this anything you want, I chose this name as it was similar
# to how AuthLogic calls it's objects and methods
def create_user_session(user)
# Assuming you have this defined in your routes, otherwise just use:
# '/your_login_path'
post user_session_path, login: user.login, password: user.password
end
end
end
# Make this available to just the request and feature specs
RSpec.configure do |config|
config.include Authlogic::TestHelper, type: :request
config.include Authlogic::TestHelper, type: :feature
end

Get access to current_user in Doorkeeper overriden layout

Since Doorkeeper is an isolated Engine we have no access to whatever you did in ApplicationController. But what if you need a current_user? What could be a workaround here?
The first idea is to monkey-patch ActionController::Base. Any better thoughts?
My doorkeeper implementation was inside of the base app so this wont help if you are using a separate engine but will if you use the same rails app so I will share here:
class ApplicationController < ActionController::Base
protect_from_forgery
private
def current_user
if doorkeeper_token
return current_resource_owner
end
# fallback to auth with warden if no doorkeeper token
warden.authenticate(:scope => :user)
end
# Needed for doorkeeper find the user that owns the access token
def current_resource_owner
User.find(doorkeeper_token.resource_owner_id) if doorkeeper_token
end
end
Unless there are no answers, may be my own dirty monkey patch will be useful to someone.
in initializers/action_controller_patch.rb:
module ActionController
Base.class_eval do
def current_user
#current_user ||= User.find(session[:user_id]) if session[:user_id]
end
helper_method :current_user
end
end
I think you can find it on this page.
https://github.com/applicake/doorkeeper/wiki/Using-Resource-Owner-Password-Credentials-flow
I am using credential auth pattern, so in my case this works.
Doorkeeper.configure do
resource_owner_from_credentials do |routes|
request.params[:user] = {:email => request.params[:username], :password => request.params[:password]}
request.env["devise.allow_params_authentication"] = true
request.env["warden"].authenticate!(:scope => :user)
end
end

How to rescue OmniAuth::Strategies::OAuth2::CallbackError?

I am building a Rails application with Omniauth for log in service.To authenticate Google I am using OmniAuth Google OAuth2 Strategy.
When user clicks 'allow access' button everything works fine.But when user clicks 'no thanks' button the below error is raised.
OmniAuth::Strategies::OAuth2::CallbackError
I have tried adding the below rescue code in application controller.
class ApplicationController < ActionController::Base
rescue_from OmniAuth::Strategies::OAuth2::CallbackError, :with =>
:omniauth_callback_error_handler
protected
def omniauth_callback_error_handler
redirect_to init_sign_in_users_path
end
end
But no luck. Any idea?
You can set the on_failure proc in the omniauth initializer in an even cleaner fashion:
OmniAuth.config.on_failure = UsersController.action(:oauth_failure)
This happens because the authentication happens in a middleware so your controller is not involved in it. This is where the exception is raised and the called code is this
I think you can handle this kind of error by defining a callback in OmniAuth initializer with this kind of code
OmniAuth.config do |config|
config.on_failure do
# your handling code invoked in the context of a rack app
end
end
Otherwise there is a commit of three months ago which introduce this behavior
def redirect_to_failure
message_key = env['omniauth.error.type']
new_path = "#{env['SCRIPT_NAME']}#{OmniAuth.config.path_prefix}/failure?message=#{message_key}"
Rack::Response.new(["302 Moved"], 302, 'Location' => new_path).finish
end
which states that on errors your user is redirected to /auth/failure with an error message, so you should be able to define a route for that path and handle it in your app. Keep in mind that this won't happen in development mode so you need to try it in other envs. If this doesn't happen in production try to upgrade your omniauth gem to version 1.1.0
I have solved this problem with the Fabio's first suggestion.
OmniAuth.config.on_failure = Proc.new do |env|
UsersController.action(:omniauth_failure).call(env)
#this will invoke the omniauth_failure action in UsersController.
end
In my UsersController
class UsersController < ActionController::Base
def omniauth_failure
redirect_to init_sign_in_users_path
#redirect wherever you want.
end
end
There's a configuration to use /auth/failure instead of raising an error.
I use OmniAuth 1.2.2 and when I checking the FailureEndpoint I found the code is like this:
def call
raise_out! if OmniAuth.config.failure_raise_out_environments.include?(ENV['RACK_ENV'].to_s)
redirect_to_failure
end
And the failure_raise_out_environments is defined here:
def self.defaults
#defaults ||= {
# other configurations
:failure_raise_out_environments => ['development']
}
end
The environment can be configured so the solution is easy. I use Rails so I put below code in an initializer file:
OmniAuth.configure do |config|
# Always use /auth/failure in any environment
config.failure_raise_out_environments = []
end

Rails 3 override Devise sessions controller

I need to override Devise sessions controller during the login process (Rails 3.0.9, Ruby 1.9.2, Devise 1.3.4), I tried this without any effect
class SessionsController < Devise::SessionsController
# GET /resource/sign_in
def new
resource = build_resource
clean_up_passwords(resource)
respond_with_navigational(resource, stub_options(resource)){ render_with_scope :new }
end
end
Ideas?
EDIT
As indicated in the answer, I also need to change the route. In addition, I also need to copy the views. It's better explained here
http://presentations.royvandewater.com/authentication-with-devise.html#8
My custom strategy:
devise.rb
config.warden do |manager|
manager.strategies.add(:custom_strategy) do
def authenticate!
... authenticate against 3rd party API...
if res.body =~ /success/
u = User.find_or_initialize_by_email(params[:user][:email])
if u.new_record?
u.save
end
success!(u)
end
end
end
Have you altered your route to use your new controller?
/config/routes.rb
devise_for :users, :controllers => {:sessions => "sessions"}

Resources