Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Authenticated Route Constraints #1147

Merged
merged 4 commits into from

6 participants

Samuel Cochran José Valim Vijay Dev Deepak Kumar Volkan Unsal Cody Olsen
Samuel Cochran

Allows routing based on authentication state, optionally by scope.

The example included in the comments for #authenticated:

authenticated :admin do
  root :to => 'admin/dashboard#show'
end

authenticated do
  root :to => 'dashboard#show'
end

root :to => 'landing#show'
José Valim
Owner

Thanks for the pull request, but why would I use authenticated instead of authenticated?

Vijay Dev

@josevalim what's the question again? :)

José Valim
Owner

Hahaha, LOL, sorry.

The question is: why would I use authenticated instead of the existing authenticate?

Samuel Cochran

@josevalim: because authenticate forces authentication, authenticated only checks for it.

With authenticated I can provide the same path twice but route differently based on authentication state. A classic example is github itself. Unauthenticated users see a landing page extorting github's virtue, authenticated users see a dashboard of recent activity and repositories, both at the root URL.

José Valim
Owner

Oh, that's great. I like it. Could you please provide tests then?

Samuel Cochran

Awesome, yeah, I wanted to get feedback before doing so. I'll chuck some together now, cheers!

José Valim
Owner

Hey mate, any news? I am planning to release Devise 1.4 in the next 24 hours. So if you can add tests, we can get it in!

Samuel Cochran

Oh man, okay, I'll get cracking.

(Opposite timezone fail.)

sj26 added some commits
Samuel Cochran sj26 Switch to Warden::Proxy#authenticate?
Warden::Proxy#authenticated? and Warden::Proxy#unauthenticated? don't try strategies first.
8012285
Samuel Cochran sj26 Tests. e75354b
Samuel Cochran

Fully tested. Caught a problem the last commit, too. -.-

José Valim josevalim merged commit f43a7c4 into from
Deepak Kumar

This is a great addition to devise. Thanks sj26 for contributing and josevalim for merging!

Volkan Unsal

This feature would be even better if we could specify some pages that be shown only to unauthenticated users. Like the registration and login pages, for instance. Then anyone requesting those pages can be redirected to the landing page for that model.

José Valim
Owner

unauthenticated was added to Devise later with exactly this behavior.

Samuel Cochran

... or you could just have later routes which, implicitly, are unauthenticated:

authenticated do
  root :to => :dashboard
end

# unauthenticated:
root :to => :home
Samuel Cochran

Oh, nevermind, I get you might want to have routes only accessible to unauthenticated users unmasked by authenticated routes.

Also, useful for skipping a whole section of unauthenticated routes as an efficiency gain.

Volkan Unsal

Isn't unauthenticated the same as the default root path? What I had in mind was more like a way of making sure authenticated users never get to see "new registration" and "new session" pages. It would be a way of doing the same thing as what this line from my registrations_controller.rb is doing right now:

redirect_to stored_location_for(current_user) if signed_in?

My proposed syntax for it would be:

authenticated, :force =>[:registrations,:sessions] do 
    as :user do
      root :to      => "pages#index"
    end
end

(Unless there is already a way of doing the same from the controller that I am not aware of.)

Cody Olsen

@sj26, @josevalim: Thank you!

Keith Johnson KeithYJohnson referenced this pull request from a commit
Commit has since been removed from the repository and is no longer available.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Jun 18, 2011
  1. Samuel Cochran
Commits on Jun 21, 2011
  1. Samuel Cochran
Commits on Jun 23, 2011
  1. Samuel Cochran

    Switch to Warden::Proxy#authenticate?

    sj26 authored
    Warden::Proxy#authenticated? and Warden::Proxy#unauthenticated? don't try strategies first.
  2. Samuel Cochran

    Tests.

    sj26 authored
This page is out of date. Refresh to see the latest.
50 lib/devise/rails/routes.rb
View
@@ -129,9 +129,9 @@ class Mapper
# end
#
# ==== Adding custom actions to override controllers
- #
- # You can pass a block to devise_for that will add any routes defined in the block to Devise's
- # list of known actions. This is important if you add a custom action to a controller that
+ #
+ # You can pass a block to devise_for that will add any routes defined in the block to Devise's
+ # list of known actions. This is important if you add a custom action to a controller that
# overrides an out of the box Devise controller.
# For example:
#
@@ -209,6 +209,50 @@ def authenticate(scope)
end
end
+ # Allow you to route based on whether a scope is authenticated. You
+ # can optionally specify which scope.
+ #
+ # authenticated :admin do
+ # root :to => 'admin/dashboard#show'
+ # end
+ #
+ # authenticated do
+ # root :to => 'dashboard#show'
+ # end
+ #
+ # root :to => 'landing#show'
+ #
+ def authenticated(scope=nil)
+ constraint = lambda do |request|
+ request.env["warden"].authenticate? :scope => scope
+ end
+
+ constraints(constraint) do
+ yield
+ end
+ end
+
+ # Allow you to route based on whether a scope is *not* authenticated.
+ # You can optionally specify which scope.
+ #
+ # unauthenticated do
+ # as :user do
+ # root :to => 'devise/registrations#new'
+ # end
+ # end
+ #
+ # root :to => 'dashboard#show'
+ #
+ def unauthenticated(scope=nil)
+ constraint = lambda do |request|
+ not request.env["warden"].authenticate? :scope => scope
+ end
+
+ constraints(constraint) do
+ yield
+ end
+ end
+
# Sets the devise scope to be used in the controller. If you have custom routes,
# you are required to call this method (also aliased as :as) in order to specify
# to which controller it is targetted.
48 test/integration/authenticatable_test.rb
View
@@ -101,6 +101,54 @@ class AuthenticationSanityTest < ActionController::IntegrationTest
assert_contain 'Private!'
end
+ test 'signed in as admin should get admin dashboard' do
+ sign_in_as_admin
+ assert warden.authenticated?(:admin)
+ assert_not warden.authenticated?(:user)
+
+ get dashboard_path
+
+ assert_response :success
+ assert_template 'home/admin'
+ assert_contain 'Admin dashboard'
+ end
+
+ test 'signed in as user should get user dashboard' do
+ sign_in_as_user
+ assert warden.authenticated?(:user)
+ assert_not warden.authenticated?(:admin)
+
+ get dashboard_path
+
+ assert_response :success
+ assert_template 'home/user'
+ assert_contain 'User dashboard'
+ end
+
+ test 'not signed in should get no dashboard' do
+ assert_raises ActionController::RoutingError do
+ get dashboard_path
+ end
+ end
+
+ test 'signed in user should not see join page' do
+ sign_in_as_user
+ assert warden.authenticated?(:user)
+ assert_not warden.authenticated?(:admin)
+
+ assert_raises ActionController::RoutingError do
+ get join_path
+ end
+ end
+
+ test 'not signed in should see join page' do
+ get join_path
+
+ assert_response :success
+ assert_template 'home/join'
+ assert_contain 'Join'
+ end
+
test 'signed in as user should not be able to access admins actions' do
sign_in_as_user
assert warden.authenticated?(:user)
9 test/rails_app/app/controllers/home_controller.rb
View
@@ -5,6 +5,15 @@ def index
def private
end
+ def user_dashboard
+ end
+
+ def admin_dashboard
+ end
+
+ def join
+ end
+
def set
session["devise.foo_bar"] = "something"
head :ok
1  test/rails_app/app/views/home/admin_dashboard.html.erb
View
@@ -0,0 +1 @@
+Admin dashboard
1  test/rails_app/app/views/home/join.html.erb
View
@@ -0,0 +1 @@
+Join
1  test/rails_app/app/views/home/user_dashboard.html.erb
View
@@ -0,0 +1 @@
+User dashboard
14 test/rails_app/config/routes.rb
View
@@ -27,7 +27,19 @@
authenticate(:admin) do
match "/private", :to => "home#private", :as => :private
end
-
+
+ authenticated :admin do
+ match "/dashboard", :to => "home#admin_dashboard"
+ end
+
+ authenticated do
+ match "/dashboard", :to => "home#user_dashboard"
+ end
+
+ unauthenticated do
+ match "/join", :to => "home#join"
+ end
+
# Routes for constraints testing
devise_for :headquarters_admin, :class_name => "Admin", :path => "headquarters", :constraints => {:host => /192\.168\.1\.\d\d\d/}
Something went wrong with that request. Please try again.