Ruby on Rails: can I use http basic auth in constraints? - ruby-on-rails

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

Related

rails 4 multisite http_basic_authenticate_with

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.

RSpec - Check sessions with Basic Authentication

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

Before Filter on a rack ( sinatra ) based app in RoR applications

I have a sinatra application, mounted in my rails app.
match "/my_sinatra_app" => MyApp, :anchor => false
Is there a way to execute a Rails before filter on rails on all /my_sinatra_app urls. ?
This is an old question but I ran into a similar problem with Sidekiq Web.
To get around this we can use route constraints.
In your routes file constrain the route to the mounted application:
require "sidekiq/web"
mount Sidekiq::Web, at: "/sidekiq", constraints: AdminConstraint.new
Then in the AdminConstraint, you need to place the calls that are in your before_filter that are applicable for your application's authentication. So for example if I had the following in my ApplicationController:
class ApplicationController < ActionController::Base
before_filter :authenticate
def authenticate
user = User.find_by(email: params[:email])
if user && user.authenticate(params[:password])
session[:user_id] = user.id
end
end
end
I would put the following in the constraint:
class AdminConstraint
def matches?(request)
return false unless request.session[:user_id]
user = User.find(request.session[:user_id])
# Only show if user is a super admin
user.present? && user.super_admin?
end
end

ActiveAdmin: How to setup HTTP basic authentication?

I want to set basic authentication for ActiveAdmin, which internal devise solution doesn't apply to my case. For that I would like to be able to add middleware to the ActiveAdmin Engine before this is bundled into my app. What I managed to do was:
ActiveAdmin::Engine.configure do |config|
config.middleware.use Rack::Auth::Basic do |username, password|
username == 'admin' && password == 'root'
end
end
But apparently this doesn't make it work, since my active admin routes are still unprotected. How can I effectively do this? And no, I don't want to protect my whole site with basic authentication.
Here's a few ideas:
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
# ...
http_basic_authenticate_with :name => "frodo", :password => "thering", :if => :admin_controller?
def admin_controller?
self.class < ActiveAdmin::BaseController
end
Or, the monkeypatching version
# config/initializers/active_admin.rb
# somewhere outside the setup block
class ActiveAdmin::BaseController
http_basic_authenticate_with :name => "frodo", :password => "thering"
end
If you only want to protect specific resources, you can use the controller block:
# app/admin/users.rb
ActiveAdmin.register Users do
controller do
http_basic_authenticate_with :name => "frodo", :password => "thering"
end
# ...
end
I was hoping that I would be able to extend the controller in this way in config/initializers/active_admin.rb in the setup block, but this didn't work for me:
# app/admin/users.rb
ActiveAdmin.setup do |config|
config.controller do
http_basic_authenticate_with :name => "frodo", :password => "thering"
end
# ...
end
You might try it though, as it could be an ActiveAdmin version thing (I could have sworn that I saw that documented somewhere...)
Good luck, I hope this helps.
UPDATE: A couple more options:
I hadn't realized before that :before_filter in activeadmin config takes a block.
# config/initializers/active_admin.rb
ActiveAdmin.setup do |config|
# ...
config.before_filter do
authenticate_or_request_with_http_basic("Whatever") do |name, password|
name == "frodo" && password == "thering"
end
end
end
And... just one more idea. It sounds like you are not keen on adding anything to application_controller, but this version is not conditional like the first above:
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
def authenticate_admin
authenticate_or_request_with_http_basic("Whatever") do |name, password|
name == "frodo" && password == "thering"
end
end
end
# config/initializers/active_admin.rb
ActiveAdmin.setup do |config|
# ...
config.authentication_method = :authenticate_admin
end
If you just want to protect the admin area of ActiveAdmin, then you should try this:
# app/admin/dashboard.rb
controller do
http_basic_authenticate_with :name => "mega-admin", :password => "supersecret"
end
that works like a charm ;-)
have fun
just another solution for you would be:
# app/controllers/application_controller.rb
protected
def authenticate
authenticate_or_request_with_http_basic do |username, password|
username == "admin" && password == "superpassword"
end
end
# config/initializers/active_admin.rb
config.before_filter :authenticate
the big plus for this solution ist, that you can call
before_filter :authenticate
in every area you want to protet.

HTTP Basic Custom Error Possible in Rails?

I am building an application in Rails 2.3.14 using Ruby 1.8.7.
My client has requested a very simple authentication on a webinars page.
I thought using http_auth would be very fitting, as it just needs a very basic username and password.
Now, she has requested that if they hit cancel or use the wrong information, they get redirected to a page that basically says "if you forget login information, contact us."
How do I make it so that when we get the "HTTP Basic: Access denied." error, I can instead redirect to a page? Or, instead, just customize this page with our custom styles/content?
Thanks in advance.
Here is the code from my webinars controller:
class WebinarsController < ApplicationController
before_filter :authenticate, :only => [:bpr]
def bpr
render :action => :bpr
end
protected
def authenticate
authenticate_or_request_with_http_basic do |username, password|
username == "abc" && password == "123"
end
end
end
If you look at the authenticate_or_request_with_http_basic source, you'll see this:
def authenticate_or_request_with_http_basic(realm = "Application", &login_procedure)
authenticate_with_http_basic(&login_procedure) || request_http_basic_authentication(realm)
end
def authenticate_with_http_basic(&login_procedure)
HttpAuthentication::Basic.authenticate(request, &login_procedure)
end
#...
def authenticate(request, &login_procedure)
unless request.authorization.blank?
login_procedure.call(*user_name_and_password(request))
end
end
So your login_procedure block can do pretty much anything it wants as long as it returns true for a successful login. In particular, it can call redirect_to:
def authenticate
authenticate_or_request_with_http_basic do |username, password|
if(username == "abc" && password == "123")
true
else
redirect_to '/somewhere/else/with/instructions'
end
end
end

Resources