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.
Related
It seems the most close method is to hook the devise model User to override after_database_authentication method.
But within that model method I was unable to send a flag to the controller which handles the root route. (Somehow current_user could not reach atr_accessor enabled parameter).
So wrapping up, what is the most rails way to differentiate devise authentication, if it is made by providing a password or if it is made by a cookie?
In the rails MVC model the model is not session aware. Your User model thus does and should not know if there is signed in user.
If you want to differentiate from when the user is redirected from the sign in vs other hits on the root path you can set a value in the session:
# routes.rb
devise_for :users, controllers: { sessions: 'sessions' }
# app/controllers/sessions_controller.rb
class SessionsController < Devise::SessionsController
def create
super do
session[:just_signed_in] = true
end
end
end
# this would be whatever controller you have that handles the route path
class HomeController
after_action :cleanup!
def index
if session[:just_signed_in]
# ...
else
# ...
end
end
private
def cleanup!
session.delete(:just_signed_in)
end
end
Another way to do this is by adding a query param to the redirect path after sign in.
currently I am using the gem delayed-web in my project. I have multiple user roles, and I don't wanna the users whose roles are sales can access to the page delayed web background interface. I already have a method to check for the authentication in my application controller. However, I don't know how to make it work in the route files. Any suggestion would be appreciated.
Updated: I am not using Devise gem. I roll my own authentication.
application_controller.rb:
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
before_action :authenticate
before_action :check_pricer_role, except: [:export_for_customer]
helper_method :check_pricer_role
def check_pricer_role
unless current_user && (current_user.pricer? || current_user.admin?)
redirect_to errors_not_found_path
end
end
end
routes.rb:
Rails.application.routes.draw do
# How to apply the defined authentication here?
mount Delayed::Web::Engine, at: '/jobs'
end
Ok, I've solved the problem. It turns out that I can find the current user based on his authentication token which is saved inside of the Request object(again, I don't use any gems for authentication, I roll my own one, but I don't think that is the problem here anyway).
This is the complete solution, in case somebody way run into the same difficulty:
class AuthConstraint
def matches?(request)
current_user ||= User.find_by_auth_token(request.cookie_jar['auth_token']) if request.cookie_jar['auth_token']
current_user.present? && !current_user.sales?
end
end
Rails.application.routes.draw do
mount Delayed::Web::Engine, at: '/jobs', :constraints => AuthConstraint.new
//Other resources ........
end
I want to protect my admins controller with a password. I added this:
before_filter :authenticate
protected
def authenticate
authenticate_or_request_with_http_basic do |username, password|
username == "user" && password == "pass!"
end
end
Into admins_controller.rb but when I visit any admins route like /admins or /admins/sign_in or admins/sign_up, no dialog shows up for the user to input the credentials.
I used this before for protecting the whole page by placing it at application_controller.rb; exactly the same way I use it now, and it worked fine before.
Any clue what might be wrong? (p.s. I use devise)
Try this:
protected
def authenticate
authenticate_or_request_with_http_basic_with name: "user", password: "pass!"
end
I've been googling this one and haven't turned up anything useful:
Assuming you use http basic auth in Rails is there a simple method to check if the user is authenticated? Ie. a way you can do something like this in a view:
- if http_basic_authenticated?
# show admin menu
Try this:
class ApplicationController < ..
before_filter :authenticate
def authenticate
authenticate_or_request_with_http_basic do |username, password|
#authenticated = username == "foo" && password == "bar"
end
end
def authenticated?
#authenticated
end
helper_method :authenticated?
end
You can now use authenticated in your view.
Please write tests!
Use a session parameter accessible through a method defined in your ApplicationController.
class ApplicationController < BaseController
...
def authorize
session[:authorized] = true
end
def http_basic_authenticated?
session[:authorized]
end
def end_session
session[:authorized] = nil
end
end
P.S. I'm not a security expert, so I can't comment on the suitability of using this in a production environment.
From the oficial code you can extract snipets to use something like this in any controller inherited by ApplicationController:
class ApplicationController < BaseController
...
protected # Only inherited controllers can call authorized?
def authorized?
request.authorization.present? && (request.authorization.split(' ', 2).first == 'Basic')
end
...
end
well, as I could know, there's no way to tell a view that the request is not authenticated, you could just tell the view that it is authenticated, but why?
let's see the process of a request:
Client Request
Controller
View
and in the 2nd step, the particular controller's method, which is before-filtered by the authentication method, that is, if you can go to the 3rd step -- the view, the request must be authenticated.
So, Here are the relevant routes
map.namespace "admin" do |admin|
admin.root :controller => :site_prefs, :action => :index
admin.resources :site_prefs
admin.resources :link_pages
admin.resources :menu_bars
admin.resources :services
admin.resources :users
end
And I have this for one controller:
before_filter :authenticate
protected
def authenticate
authenticate_or_request_with_http_basic do |username, password|
username == "1234" && password == "1234"
end
end
How do I set up my admin controllers to authenticate no matter what page within any of those controllers is navigated to, yet only have it authenticate once among all the admin controllers, and have the code all in one spot.
Right now, the only I can think of to authenticate is to copy the auth code into each controller, and I hate having duplicate code... so.... yeah
Create a "Admin::BaseController" that inherits from the ApplicationController. Put the before_filter in that controller that handels the basic auth. Then have all your other admin controllers inherit from this BaseController.
You could move the authentication code and the before_filter in the ApplicationController.
Your filter code could look at the request.fullpath to see if begins with /admin and if so, authenticate.
Here's a nice clean way to create an Admin::BaseController class in a Rails 3+ app:
Create an admin folder in your controllers folder (app/controllers/admin)
Create a new file in the admin folder: base.rb
Place your controller class in a module:
module Admin
class Base < ApplicationController
http_basic_authenticate_with :name => "name", :password => "password"
end
end
Then for all your other controllers in your admin folder, inherit from Admin::BaseController:
module Admin
class Users < BaseController
# method definitions
end
end
By doing this, you can keep all your admin-related files nice and tidy inside the admin directory, and they will all be authenticated because they inherit from Admin::BaseController.