Devise ignore my custom strategy - ruby-on-rails

I want to create a custom auth strategy for accessing the API. I followed the example code at Devise ignoring custom strategy.
The problem is that the valid? method in my Api strategy is never run (based on trying to pry in it).
My code:
module Devise
module Strategies
class Api < Devise::Strategies::Base
def valid?
binding.pry
params[:request_source] == 'api'
end
def authenticate!
#do stuff here
if user
success!(user)
else
warden.custom_failure!
render :json=> {:success=>false, :message=>"Error with your login or password"}, :status=>401
end
end
end
Warden::Strategies.add(:api, Devise::Strategies::Api)
end
end
and in the devise initializer:
config.warden do |manager|
manager.default_strategies.unshift :api
end
What ever I do, it seems like Devise always use its default strategy. AFAIK, this should be enough...
-------EDIT--------
I require the strategy like this at the very top of my devise initializer:
require Rails.root.join('app/devise/strategies/api')
I know the strategy is loaded at boot time since if I put a pry call inside the class, it will start a pry session. But the Pry calls inside the methods are never run. :-S

Found the answer!
I had to use this:
config.warden do |manager|
manager.default_strategies(scope: :user).unshift :api
end
to make it work. Weird thing is, a lot of the sample code I saw on the net did not use it :-S

When are you requiring your custom strategy? Comparing to the example, you are adding the strategy there instead of in your initializer. You might try requiring the custom strategy at the top of your initializer to make sure it's loaded before you add it to default_strategies.
If that doesn't do the trick, don't be afraid to add some temporary puts statements right in devise itself where authenticate is called to check default_strategies. That is, if you're not already confortable using the debugger, which is what I would do.

Related

How do I pass a block to the Devise create method?

Im using devise token auth (which inherently just uses Devise) and I'm trying to modify the resource object before it gets saved upon user registration. The create method, as defined in the source and explained in user documentation has a yield resource if block_given? line, yet, the following code doesnt work as expected
class RegistrationsController < DeviseTokenAuth::RegistrationsController
def create
puts "this works"
super do |resource|
puts "this doesnt work"
end
end
end
Any idea why?
https://github.com/lynndylanhurley/devise_token_auth/blob/master/app/controllers/devise_token_auth/registrations_controller.rb
This base controller doesn't have block invocation.
Probably you meant https://github.com/plataformatec/devise/blob/master/app/controllers/devise/registrations_controller.rb
It has block invocation, but it'll not work, because DeviseTokenAuth::RegistrationsController will not pass block to it.
You need some other way to achieve what you want.
Probably paste code from DeviseTokenAuth::RegistrationsController to your custom controller, or fork DeviseTokenAuth gem and patch it.
Don't forget to make PR

testing custom devise and/or warden strategy with rspec

I see plenty that lead to custom authorization strategies for devise and warden, but what I'm sepcifically after is testing these solutions with rspec. Similar to this question: Filtering users who are able to sign in with Devise
What can I do to test this sort of implementation. Rails 3.1.1, Devise (most current), etc.
For those that may do this in the future, here is my solution:
This is the class that sets a new strategy for authentication through Devise (and it could also be used with Warden with a few small changes).
require 'devise/strategies/authenticatable'
module Devise
module Strategies
class AndroidAuthenticatable < Authenticatable
def valid?
# return true/false
return valid_params? && valid_headers?
end
def authenticate!
failure_message = "Authentication failed for device/user"
klass = mapping.to # if we're going to come here, we can mock this
begin
# code to determine whether or not to authenticate this request
# if yes, success!(instance of klass)
# if no, fail!(messsage)
rescue
fail!(failure_message) # always fail if not success
end
end
protected
def valid_params?
# params that show whether request should be here
end
def valid_headers?
# headers that determine if request should be here
end
end
end
end
The previous class is in my lib/.../strategies directory. I also have lib configured for auto-loading through the rails configuration.
From the rspec side, after I created the above class I write out a few stubs/mocks. Here is a basic rspec file to get you started.
# I do this in before block or right before test executes
#request = mock(:request)
#strategy = Devise::Strategies::AndroidAuthenticatable.new(nil)
#request.should_receive(:headers).and_return({#hash of the headers you are testing})
#strategy.should_receive(:params).at_least(:once).and_return({#hash of the params})
#strategy.should_receive(:request).and_return(#request)
# Break these up as needed to test failing and successful
# strategies for your application
lambda {
#strategy.should be_valid
#strategy.authenticate!.should eql :success
}.should_not raise_error
This isn't all inclusive, but I feel it should get us a good head start when adding strategies with Warden or Devise. I actually had to implement what I thought would work and then right tests to prove it after the fact. Now we can do it the other way around perhaps.

Removing a Warden strategy - how to ensure original devise_authenticable strategy is gone

I created my own Warden strategy for using with Devise. It's very similar to Devise::Strategies::DatabaseAuthenticatable and actually it inherits from it and re-implements authenticate!
My issue though is that I want to make sure the original devise_authenticable Warden strategy is gone. That is not in the list of strategies Warden will try because it's actually a security problem. Is that possible?
According to my manual inspection and tests, this in the devise.rb initializer achieves the goal:
config.warden do |manager|
strategies = manager.default_strategies(:scope => :user)
strategies[strategies.index(:database_authenticatable)] = :alternative_strategy
end
And the strategy is implemented this way (not part of this question, but I found conflicting information out there and this one is the one that worked for me using Rails 3.1, devise 1.4.7 and warden 1.0.5):
class AlternativeStrategy < Devise::Strategies::Authenticatable
def authenticate!
end
end
Warden::Strategies.add(:alternative_strategy, AlternativeStrategy)
I just implemented this as well. Devise will try each strategy in its list until one succeeds.
For me, rather than replace the :database_authenticatable strategy in place, I just added my strategy to the beginning of the list and popped :database_authenticatable off the end of the existing list.
config.warden do |manager|
# Exiles::Devise::Strategies::BySite implemented in lib/. It matches the stub in Pablo's answer
manager.strategies.add( :by_site_auth, Exiles::Devise::Strategies::BySite )
# add my strategy to the beginning of the list.
manager.default_strategies(:scope => :user).unshift :by_site_auth
# remove the default database_authenticatable strategy from the list
manager.default_strategies(:scope => :user).pop
end

Where should warden callbacks be placed in a rails app?

I'm relatively new to rails. I have Devise set up, and want to run some callback code after users sign in.
Looking at the Warden wiki page, I can use the "after_set_user" callback to perform this logic, for example:
Warden::Manager.after_set_user do |user, auth, opts|
unless user.active?
auth.logout
throw(:warden, :message => "User not active")
end
end
However, I'm not sure where I should be storing this stuff. My first thought is that I could put it in config/initializers/devise.rb. Is that correct? It doesn't feel right putting what is essentially controller code in the config directory.
Warden hooks need to be required when your application is booting, so inside Devise's initializer at config/initializers/devise.rb is a good candidate.
However, the behavior you want to achieve will be better accomplished by using this Devise feature:
https://github.com/plataformatec/devise/wiki/How-To:-Customize-user-account-status-validation-when-logging-in

How do I fake OpenID login in RSpec user story/Cucumber when using open_id_authentication plugin

I'm trying to write a Cucumber scenario that requires me to have a logged in user - that would normally be quite simple but I'm only using OpenID authentication (curtosy of the authentication plugin). However after digging through the open_id_authentication plugins guts I'm not sure how I could achieve this within Cucumber.
I've figured out a way, if you place this in your features/support/env.rb:
ActionController::Base.class_eval do
private
def begin_open_id_authentication(identity_url, options = {})
yield OpenIdAuthentication::Result.new(:successful), identity_url, nil
end
end
Then you can just do something like this in your appropriate step:
Given /^I am logged in as "(.*)"$/ do |name|
user = User.find_by_name(user)
post '/session', :openid_url => user.identity_url
# Some assertions just to make sure our hack in env.rb is still working
response.should redirect_to('/')
flash[:notice].should eql('Logged in successfully')
end
I'm just completely clobbering the open id auth for the cucumber features, obviously if I need instances where there is failed login I could do that based on the supplied identity_url.
If you want to be able to stub out responses do this:
In features/support/helpers.rb:
ActionController::Base.class_eval do
private
def fake_openid_response(identity_url)
[OpenIdAuthentication::Result.new(:successful), identity_url, nil]
end
def begin_open_id_authentication(identity_url, options = {})
yield fake_openid_response(identity_url)
end
end
By moving the response out to a separate method you can now stub the response in your steps if necessary. For example, if I wanted a :missing response and I had a controller GoogleLoginController I could do the following using Mocha:
GoogleLoginController.any_instance.stubs(:fake_openid_response)
.returns([OpenIdAuthentication::Result.new(:missing), identity_url, nil])
Bort, a rails skeleton app, has a full set of rspec tests and supports openID login so you may want to take a look and see what they do.
DEfusion's answer works except that I needed to normalize the identity_url like:
ActionController::Base.class_eval do
private
def begin_open_id_authentication(identity_url, options = {})
yield OpenIdAuthentication::Result.new(:successful), self.normalize_identifier(identity_url), nil
end
end
Thanks

Resources