I have some strange issue : if I run
class ApplicationController < ActionController::Base
http_basic_authenticate_with name: "test", password: "test"
it works fine. But if I put this
before_filter authenticate_incoming
def authenticate_incoming
http_basic_authenticate_with name: "hi", password: "ho"
end
I get undefined method http_basic_authenticate_with. Where am I joint wrong ?
the source code of http_basic_authenticate_with is
def http_basic_authenticate_with(options = {})
before_filter(options.except(:name, :password, :realm)) do
authenticate_or_request_with_http_basic(options[:realm] || "Application") do |name, password|
name == options[:name] && password == options[:password]
end
end
end
So you see that it is implementing before_filer. So what you should do is using something like (you can also store some session login data ;))
def authenticate_incoming
authenticate_or_request_with_http_basic do |name, password|
if name == 'hi' && password == 'ho'
session[:logged_in] = true
return true
else
return false
end
end
end
http_basic_authenticate_with is a class method and authenticate_incoming is an instance method. You could do self.class.http_basic_authenticate_with but that doesn't make a whole lot of sense to do in a before_filter. What's your goal? Maybe I can help you think through how to acomplish it.
Related
I use Rails 5 API mode
routes.rb:
Rails.application.routes.draw do
constraints Basic do
mount Rswag::Ui::Engine => '/api-docs'
mount Rswag::Api::Engine => '/api-docs'
mount Sidekiq::Web => '/sidekiq'
# etc
end
end
constraints/basic.rb:
class Basic
def self.matches?(request)
authenticate_or_request_with_http_basic do |email, password|
email == 'foo' && password = 'bar'
end
end
end
But I'm getting an error:
NoMethodError (undefined method `authenticate_or_request_with_http_basic' for Basic:Class)
Can I use http basic auth in constraints?
I dont think you can use authenticate_or_request_with_http_basic method out of controllers scope. You can set up before_filter with auth check in general controller. Here is an example taken from docs comments:
class AdminController < ApplicationController
before_filter :authenticate
def authenticate
authenticate_or_request_with_http_basic('Administration') do |email, password|
email == 'foo' && password == 'bar'
end
end
end
Also here is what I found Rails Authentication Routing Constraints Considered Harmful.
That being said I think there is a way:
class Basic
def self.matches?(request)
if ActionController::HttpAuthentication::Basic.has_basic_credentials?(request)
credentials = ActionController::HttpAuthentication::Basic.decode_credentials(request)
email, password = credentials.split(':')
email == 'foo' && password == 'bar'
end
end
end
Here is docs on HTTP Basic authentication with examples
have been trying to solve this for 6 hours now. Hope someone can nail this. I have one codebase running multiple apps on heroku. Some apps already have their own domain. I am already using the host to set locale for each app which is working fine. See below. But authenticating (hide non-ready apps from public) per host doesn't work.
Setting the locale in application controller - working nicely:
before_filter :extract_locale_from_domain
def extract_locale_from_domain
domain = request.host
if domain == 'www.domain.hu'
I18n.locale = :'hu'
elsif domain == 'www.domain.com'
I18n.locale = :'en-US'
else
I18n.locale = :'en-US'
end
end
Now my home page is 'static_pages#home' so first I thought I put the method in the static_pages_controller but that didn't work so I even tried in the application_controller. Even tried to set default URLs per environment (in application_controller) but no luck with that neither (more here). Oh yes, and I tried to restrict per environment with no luck. So I tried several versions this is the one in application_controller (giving nomethod error):
before_filter :authenticate
def authenticate
domain = request.host
if domain == 'www.domain.hu'
http_basic_authenticate_with name: "stuff", password: "boda"
elsif domain == 'www.domain.com'
http_basic_authenticate_with name: "stuff", password: "boda"
else
end
end
This gives the error:
NoMethodError (undefined method `http_basic_authenticate_with' for #
< StaticPagesController:0x000000090d8de8 >):
app/controllers/application_controller.rb:49:in `authenticate'
TIA!
This is what worked:
In applicaiton controller:
before_filter :authenticate
....
protected
def authenticate
domain = request.host
if domain == 'www.domain.hu'
authenticate_or_request_with_http_basic do |username, password| username == 'stuff' && password == 'boda'
end
elsif domain == 'www.domain.com'
authenticate_or_request_with_http_basic do |username, password| username == 'stuff' && password == 'boda'
end
end
end
NoMethodError (undefined method `http_basic_authenticate_with' for #
< StaticPagesController:0x000000090d8de8 >)
This means it can't find the http_basic_authenticate_with method on the StaticPagesController instance.
Try including ActionController::HttpAuthentication::Basic and ActionController::HttpAuthentication::Basic::ControllerMethods modules in your controller and then try to use the http_basic_authenticate_with method again.
I'm trying to check if my sessions works well with basic authentication. Here is my controller :
class ClientsController < ApplicationController
skip_before_filter :verify_authenticity_token
before_action :authenticate
def create
#client = Client.create!({
:user_id => #current_user.id
})
session[:client_id] = #client.id
render(:xml => { :status => 'OK' })
end
private
def authenticate
authenticate_or_request_with_http_basic do |username, password|
# User checking...
#current_user = checked_user
end
end
end
end
It's a very basic controller. But when I try to see if session[:client_id] is correctly set, it's just returning nil.
I didn't write the initialization of #user.
it "should create session" do
request.env['HTTP_AUTHORIZATION'] = ActionController::HttpAuthentication::Basic.encode_credentials(#user.login, #password)
post :create
response.should be_success # not fail
Hash.from_xml(response.body)['hash']['status'].should == 'OK' # not fail
Client.last.user.should == #user # not fail
assigns(session[:client_id]).should == Client.last.id # Fail !
end
The error is that assigns(session[:client_id]) is nil... I'm totally sure the #client is initialized and the render is OK, but session seems not to be saved.
It's the first time I use rspec with session. Is it the correct writing of this test ?
Regards
So the issue is the line:
assigns(session[:client_id]).should == Client.last.id # Fail !
assigns is a method that is going to point to the equivalent instance method, so assigns(session[:client_id]) is going to check for #session[:client_id], which it won't find.
Also, the session hash is available in rspec so you can call it like you would in your controller, which is what you need to do here:
session[:client_id].should == Client.last.id # pass
how to get session in helper file?
UserHelper.rb
module UsersHelper
def self.auth login, password
user = Users.where("firstname = :firstname AND password = :password", {:firstname => login, :password => password})
if user != []
return true
else
return false
end
end
def self.is_auth? level
puts #session
user = Users.where("firstname = :firstname AND password = :password", {:firstname => #session[:firstname], :password => #session[:password]})
if user != []
return true
else
return false
end
end
end
Admin_controller.rb
class AdminController < ApplicationController
include Rails.application.routes.url_helpers
def initialization
#session = session
end
def index
#session = session
if UsersHelper.is_auth?(2)
render :text => "ssssss"
end
end
def auth
if params[:send] != nil
if UsersHelper.auth params[:firstname], params[:password]
session[:firstname] = params[:firstname]
session[:password] = params[:password]
redirect_to :action => "index"
else
#error = 1
end
end
end
def exit
session.delete(:firstname)
session.delete(:password)
render :json => session
end
end
Error
undefined method `[]' for nil:NilClass
app/helpers/users_helper.rb:13:in `is_auth?'
app/controllers/admin_controller.rb:8:in `index'
Only Controller can access session.
So, in a nutshell, if you are going to use this method in Controllers only like what is you case, you can define it as ApplicationController's method. Or define it a module and include it in AppplicationController.
class ApplicationController < ActionController::Base
def auth
end
def is_auth?
end
end
If you want to use the method in both controller and view, just declare them as helper_method
class ApplicationController < ActionController::Base
helper_method :auth, :is_auth?
def auth
end
def is_auth?
end
end
Ref: http://apidock.com/rails/ActionController/Helpers/ClassMethods/helper_method
Another note: In my opinion it's really not worth the time to build auth system from scratch by yourself. The functionalities are not easy but quite general. There are well baked gems such as Devise, Authlogic. Better to use them.
I want to implement HTTP basic authentication on my staging server, but only for those outside the local network. I have a Rails 3.1 app. In application.rb, I have the following:
class ApplicationController << ActionController::Base
http_basic_authenticate_with :realm => "Staging", :name => "user", :password => "password" if :need_authentication?
private
def need_authentication?
Rails.env == "staging" && request.remote_addr !~ /^192.168.0.\d{1,3}$/
end
end
Here's the rub: even when the need_authentication? method explicitly returns false, the app still asks me to authenticate, as if it's completely ignoring the if clause at the end.
So, is there any way to only require authentication under certain conditions?
In Rails 4, the :if condition works. For example,
class ApplicationController < ApplicationController::Base
http_basic_authenticate_with name: "user", password: "password" if Rails.env == 'staging'
end
or if you want a helper method to set the condition,
class ApplicationController < ApplicationController::Base
http_basic_authenticate_with name: "user", password: "password", if: :need_authentication?
private
def need_authentication?
Rails.env == 'staging'
end
end
This is what worked:
class ApplicationController < ActionController::Base
before_filter :authenticate_if_staging
private
def authenticate_if_staging
if Rails.env == 'staging' && request.remote_addr !~ /^192.168.0.\d{1,3}$/
authenticate_or_request_with_http_basic 'Staging' do |name, password|
name == 'username' && password == 'secret'
end
end
end
end
'Staging' is the name of the realm. This is not required, but can be used for clarification.