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.
Related
I have a pretty typical situation where I have a '/dashboard' which should render a different view for different user roles (i.e. client, admin, etc.).
I am open to more elegant suggestions but my thought was to have one route definition for dashboard like so:
routes.rb
resource :dashboard
and to have a dashboards_controller.rb like so:
class DashboardsController < ApplicationController
def show
if current_user.has_role?('sysadmin')
// show system admin dashboard
elsif
// show a different dashboard
// etc
end
end
end
Now I would like that each dashboard gets built in its role specific namespaced dashboard_controller, ex: controllers/admin/dashboard_controller.rb. This way, each dashboard can be appropriately built up in the right place.
The way I am trying to do this is to redirect from my main dashboards_controller to the admin/dashboard_controller like so:
redirect_to :controller => 'admin/dashboard_controller', :action => 'index'
But it is not working, presumably because I am not sure how to reference a namespaced controller from here.
How can I achieve this?
(If there is a more elegant solution I am open but I thought this was pretty good).
I am using devise and cancancan.
You can do per-role dashboard, for example:
routes.rb
scope '/:role' do
resources :dashboard
end
resources :dashboard
Then to redirect with the role, just simply:
redirect_to :controller => 'dashboard', :action => 'index', :role => 'admin'
The Better Way
If you want custom dispatch per controller, you should consider using filter. E.g. given admin/dashboard only accessible by admin and user/dashboard only accessible by default user, you may want to create files like this:
routes.rb
namespace 'admin' do
resources :dashboard
end
namespace 'user' do
resources :dashboard
end
Then you create these files:
app/controllers/admin/dashboards_controller.rb
app/controllers/admin/admin_base_controller.rb
app/controllers/user/dashboards_controller.rb
app/controllers/user/user_base_controller.rb
For each files:
# app/controllers/admin/dashboards_controller.rb
class Admin::DashboardsController < Admin::AdminBaseController; end
# app/controllers/admin/admin_base_controller.rb
class Admin::AdminBaseController < ApplicationController
before_action :ensure_admin!
def ensure_admin!
redirect_to controller: 'user/dashboards', action: index unless current_user.has_role?('sysadmin')
end
end
# Now you've got the idea. Similar things for the rest of the files:
# app/controllers/user/dashboards_controller.rb
# app/controllers/user/user_base_controller.rb
Then you can try visiting it at admin/dashboards and user/dashboards, it should be redirected to its role accordingly.
It's good to use named route helpers rather than providing controller and action explicitly.
Consider adding routes as follows:
namespace :admin do
resource :dashboard, controller: 'dashboard '
end
Then you can call :
redirect_to admin_dashboard_url
Remember, it's resource, not resources. So it is going to process dashboard_controller#show, not dashboard_controller#index
I'm using Devise with Ruby on Rails.
What is the recommended way to redirect unauthenticated users to the sessions#new page if they attempt to access a page that requires authentication?
Right now I get an error that says no route matches the one they attempt to access (leading to a 404 error in production).
Just simple add this method to application_controller.rb
protected
def authenticate_user!
if user_signed_in?
super
else
redirect_to login_path, :notice => 'if you want to add a notice'
## if you want render 404 page
## render :file => File.join(Rails.root, 'public/404'), :formats => [:html], :status => 404, :layout => false
end
end
And you can call this method on before_filter another controllers you want.
e.g :
class HomesController < ApplicationController
before_filter :authenticate_user!
## if you want spesific action for require authentication
## before_filter :authenticate_user!, :only => [:action1, :action2]
end
Don't forget add login_path into routes.rb
devise_scope :user do
match '/sign-in' => "devise/sessions#new", :as => :login
end
note : I always use this way when play with devise for my apps authentication.. (rails 3.2 and rails 4.0.1)
You can do just like GeekTol wrote, or just put
before_action :authenticate_user!
in your controller.
In this case, devise uses the default authenticate_user! method, that will redirect to the "user_session_path" and use the default flash message.
It's not necessary to rewrite authenticate_user! method, unless you want to customize it.
I thought you could just add:
before_action :authenticate_user!
to each controller that required the user to be logged in.
I'm a Rails beginner but I found this in my own searches and it works well in my application.
You should refer to Devise's own How To: How To: Redirect to a specific page when the user can not be authenticated.
Another alternative I can think of is creating a routing Constraint wrapping your protected routes. You'd better stick to Devise's way, but here is an example:
#On your routes.rb
constraints(Constraints::LoginRequired) do
get '/example'
end
#Somewhere like lib/constraints/login_required.rb
module Constraints
class LoginRequired
def self.matches?(request)
#some devise code that checks if the user is logged in
end
end
end
Add this code in your config/routes.rb devise_for :users and resources :users and you can generate devise in views.
I'm using the Sorcery gem for user signup/login.
One feature of this gem is the require_login before_filter on any controller you want to authenticate.
I have created a dashboard namespace for my app after they've logged in. For example /dashboard/reports or /dashboard/employees, etc.
Routes file:
# Dashboard
namespace :dashboard do
# Recent Activity
get '' => redirect('/dashboard/recent-activity')
get 'recent-activity' => 'activities#index', :as => 'root'
# Other dashboard controllers and actions
end
I extracted out the before_filter into it's own controller called:
"app/controllers/dashboard/base_controller.rb"
class Dashboard::BaseController < ApplicationController
before_filter :require_login
end
What I'd like to do is make 100% sure in some kind of test that ANY new controller I create within the dashboard folder (or dashboard namespace), inherits from Dashboard::BaseController
Such as my activities controller for example:
class Dashboard::ActivitiesController < Dashboard::BaseController
I dont want to go creating controllers in a few months and accidentally make it inherit from ApplicationController which would still would but wouldnt have login functionality.
I'm using RSpec
Can't quite believe my own eyes that I solved this on my own....
require 'spec_helper'
describe Dashboard::BaseController do
it "is the superclass of every dashboard namespaced controller" do
Rails.application.eager_load!
ApplicationController.descendants.each do |controller|
if controller.to_s.include?("Dashboard::") && controller.to_s != "Dashboard::BaseController"
expect(controller.superclass.to_s).to eq("Dashboard::BaseController")
end
end
end
end
I have started using devise for my rails app, however i need to make some changes to the controller logic that devise has, my problem is that i assign users roles, and i want to put in params[:user][:role_ids] ||= [] in the update action so if all the roles are unchecked then it actually works.
whats the best way to do this?
class UsersController < Devise::SessionsController
def create
super
end
def update
#edit here
end
end
and make sure you update your routes.rb file:
devise_for :users, :controllers => { :sessions => 'users/sessions' }
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.