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.
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
I would like to reset session whenever user agent and/or user ip changes. Project uses Rails with Devise for authentication, CanCanCan for authorisation.
My approach is:
class ApplicationController < ActionController::Base
before_action :authorize_ip
def authorize_ip
if warden.authenticated?
warden.session['ip'] ||= request.ip
warden.session['user_agent'] ||= request.user_agent
warden.logout if warden.session['ip'] != 'request.ip' &&
warden.session['user_agent'] != request.user_agent
end
end
end
From my understanding, it should set warden.session['ip'] and user_agent once and then for following requests whenever request['ip'] or user_agent changes, session should be dropped and User should be logged out. However, when tested with different browsers, warden.session['user_agent'] changes according to what browser I use. I suppose I'm misunderstanding something. Also, if there is a better approach to this problem, please share.
Thanks!
I've solved the issue by adding this to initializers/devise.rb
Warden::Manager.after_authentication do |_user, auth, _opts|
auth.raw_session['warden.user.ip'] = auth.request.ip
auth.raw_session['warden.user.user_agent'] = auth.request.user_agent
end
and this to application controller:
class ApplicationController < ActionController::Base
before_action :authorize_ip_user_agent
protected
def authorize_ip_user_agent
return true unless session['warden.user.ip']
warden.logout if session['warden.user.ip'] != request.ip &&
session['warden.user.user_agent'] != request.user_agent
end
end
I have a simple website with password protected admin area. It works fine in development. When I upload to Heroku I get the following error:
NameError (uninitialized constant ApplicationController::ADMIN_USERNAME):
My application controller:
class ApplicationController < ActionController::Base
protected
def authenticate
authenticate_or_request_with_http_basic do |username, password|
username == ENV[ADMIN_USERNAME] && password == ENV[ADMIN_PASSWORD]
end
end
end
Admin/index_controller:
class Admin::IndexController < ApplicationController
before_filter :authenticate
def index
end
end
I have set heroku env varibale with:
heroku config:add ADMIN_USERNAME:'myusername'
I'm not able to find what to do next.
You need to use ENV['ADMIN_USERNAME'], otherwise the app thinks it's a constant name. Altough it's weird that this works in development.
What's wrong with this picture: I want to add more than one login to my app - really simple http auth...this is working locally but only lets me log in as user1/pass1 once I've uploaded it to Heroku...If I try user2/pass2 it wont let me log in.
Any ideas?
class ApplicationController < ActionController::Base
helper :all
protect_from_forgery
USER_NAME, PASSWORD = "user1", "pass1"
USER_NAME2, PASSWORD2 = "user2", "pass2"
before_filter :authenticate
private
def authenticate
authenticate_or_request_with_http_basic do |user_name, password|
(user_name == USER_NAME && password == PASSWORD) || (user_name == USER_NAME2 && password == PASSWORD2)
end
end
end
Thanks!
I have tried with the same code that you have. I made a heroku app and made same constants like you have made. But did't face any problem for using either of the password. Can you provide more detail about you app ?
I want the /admin route on my rails app to be protected by using .htaccess password files - is this possible?
Rails has a built-in helper for this, you could place this in your application controller:
protected
def authenticate
authenticate_or_request_with_http_basic do |username, password|
username == "admin" && password == "test"
end
end
Then use a before_filter on any controllers you want to protect (or just stick it in the application controller to block the whole site):
before_filter :authenticate
This method works on Nginx as well as Apache, which is an added bonus. It doesn't, however, work if you have full page caching enabled - as the visitor never hits the Rails stack; it won't kick in.
Edit
Just noticed that you specified the /admin route. All my admin controllers inherit from an AdminController. You could set yours up like so:
/app/controllers/admin/admin_controller.rb
class Admin::AdminController < ApplicationController
before_filter :authenticate
protected
def authenticate
authenticate_or_request_with_http_basic do |username, password|
username == "admin" && password == "test"
end
end
end
Then have all your controllers extend the admin controller, eg:
class Admin::ThingsController < Admin::AdminController
My routes are setup like so:
map.namespace :admin do |admin|
admin.resources :things
end
Hope that helps.