Skip to content
This repository

Authenticated Route Constraints #1147

Merged
merged 4 commits into from almost 3 years ago

6 participants

Samuel Cochran José Valim Vijay Dev Deepak Kumar volkanunsal Cody Olsen
Samuel Cochran
sj26 commented June 18, 2011

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
sj26 commented June 18, 2011

@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
sj26 commented June 19, 2011

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
sj26 commented June 22, 2011

Oh man, okay, I'll get cracking.

(Opposite timezone fail.)

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

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

José Valim josevalim merged commit f43a7c4 into from June 23, 2011
José Valim josevalim closed this June 23, 2011
Deepak Kumar

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

volkanunsal

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
sj26 commented August 24, 2011

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

authenticated do
  root :to => :dashboard
end

# unauthenticated:
root :to => :home
Samuel Cochran
sj26 commented August 24, 2011

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.

volkanunsal

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 in KeithYJohnson/kwoter March 20, 2014
devise logins w/ rails 4. plataformatec/devise#1147 fb53c62
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Showing 4 unique commits by 1 author.

Jun 18, 2011
Samuel Cochran Add #authenticated and #not_authenticated route constraints 14fec4c
Jun 21, 2011
Samuel Cochran Oh hey, Warden has API for this. Should probably match the name, too. 471e4d6
Jun 23, 2011
Samuel Cochran Switch to Warden::Proxy#authenticate?
Warden::Proxy#authenticated? and Warden::Proxy#unauthenticated? don't try strategies first.
8012285
Samuel Cochran Tests. e75354b
This page is out of date. Refresh to see the latest.
50  lib/devise/rails/routes.rb
@@ -129,9 +129,9 @@ class Mapper
129 129
     #   end
130 130
     #
131 131
     # ==== Adding custom actions to override controllers
132  
-    # 
133  
-    # You can pass a block to devise_for that will add any routes defined in the block to Devise's 
134  
-    # list of known actions.  This is important if you add a custom action to a controller that 
  132
+    #
  133
+    # You can pass a block to devise_for that will add any routes defined in the block to Devise's
  134
+    # list of known actions.  This is important if you add a custom action to a controller that
135 135
     # overrides an out of the box Devise controller.
136 136
     # For example:
137 137
     #
@@ -209,6 +209,50 @@ def authenticate(scope)
209 209
       end
210 210
     end
211 211
 
  212
+    # Allow you to route based on whether a scope is authenticated. You
  213
+    # can optionally specify which scope.
  214
+    #
  215
+    #   authenticated :admin do
  216
+    #     root :to => 'admin/dashboard#show'
  217
+    #   end
  218
+    #
  219
+    #   authenticated do
  220
+    #     root :to => 'dashboard#show'
  221
+    #   end
  222
+    #
  223
+    #   root :to => 'landing#show'
  224
+    #
  225
+    def authenticated(scope=nil)
  226
+      constraint = lambda do |request|
  227
+        request.env["warden"].authenticate? :scope => scope
  228
+      end
  229
+
  230
+      constraints(constraint) do
  231
+        yield
  232
+      end
  233
+    end
  234
+
  235
+    # Allow you to route based on whether a scope is *not* authenticated.
  236
+    # You can optionally specify which scope.
  237
+    #
  238
+    #   unauthenticated do
  239
+    #     as :user do
  240
+    #       root :to => 'devise/registrations#new'
  241
+    #     end
  242
+    #   end
  243
+    #
  244
+    #   root :to => 'dashboard#show'
  245
+    #
  246
+    def unauthenticated(scope=nil)
  247
+      constraint = lambda do |request|
  248
+        not request.env["warden"].authenticate? :scope => scope
  249
+      end
  250
+
  251
+      constraints(constraint) do
  252
+        yield
  253
+      end
  254
+    end
  255
+
212 256
     # Sets the devise scope to be used in the controller. If you have custom routes,
213 257
     # you are required to call this method (also aliased as :as) in order to specify
214 258
     # to which controller it is targetted.
48  test/integration/authenticatable_test.rb
@@ -101,6 +101,54 @@ class AuthenticationSanityTest < ActionController::IntegrationTest
101 101
     assert_contain 'Private!'
102 102
   end
103 103
 
  104
+  test 'signed in as admin should get admin dashboard' do
  105
+    sign_in_as_admin
  106
+    assert warden.authenticated?(:admin)
  107
+    assert_not warden.authenticated?(:user)
  108
+
  109
+    get dashboard_path
  110
+
  111
+    assert_response :success
  112
+    assert_template 'home/admin'
  113
+    assert_contain 'Admin dashboard'
  114
+  end
  115
+
  116
+  test 'signed in as user should get user dashboard' do
  117
+    sign_in_as_user
  118
+    assert warden.authenticated?(:user)
  119
+    assert_not warden.authenticated?(:admin)
  120
+
  121
+    get dashboard_path
  122
+
  123
+    assert_response :success
  124
+    assert_template 'home/user'
  125
+    assert_contain 'User dashboard'
  126
+  end
  127
+
  128
+  test 'not signed in should get no dashboard' do
  129
+    assert_raises ActionController::RoutingError do
  130
+      get dashboard_path
  131
+    end
  132
+  end
  133
+
  134
+  test 'signed in user should not see join page' do
  135
+    sign_in_as_user
  136
+    assert warden.authenticated?(:user)
  137
+    assert_not warden.authenticated?(:admin)
  138
+
  139
+    assert_raises ActionController::RoutingError do
  140
+      get join_path
  141
+    end
  142
+  end
  143
+
  144
+  test 'not signed in should see join page' do
  145
+    get join_path
  146
+
  147
+    assert_response :success
  148
+    assert_template 'home/join'
  149
+    assert_contain 'Join'
  150
+  end
  151
+
104 152
   test 'signed in as user should not be able to access admins actions' do
105 153
     sign_in_as_user
106 154
     assert warden.authenticated?(:user)
9  test/rails_app/app/controllers/home_controller.rb
@@ -5,6 +5,15 @@ def index
5 5
   def private
6 6
   end
7 7
 
  8
+  def user_dashboard
  9
+  end
  10
+
  11
+  def admin_dashboard
  12
+  end
  13
+
  14
+  def join
  15
+  end
  16
+
8 17
   def set
9 18
     session["devise.foo_bar"] = "something"
10 19
     head :ok
1  test/rails_app/app/views/home/admin_dashboard.html.erb
... ...
@@ -0,0 +1 @@
  1
+Admin dashboard
1  test/rails_app/app/views/home/join.html.erb
... ...
@@ -0,0 +1 @@
  1
+Join
1  test/rails_app/app/views/home/user_dashboard.html.erb
... ...
@@ -0,0 +1 @@
  1
+User dashboard
14  test/rails_app/config/routes.rb
@@ -27,7 +27,19 @@
27 27
   authenticate(:admin) do
28 28
     match "/private", :to => "home#private", :as => :private
29 29
   end
30  
-  
  30
+
  31
+  authenticated :admin do
  32
+    match "/dashboard", :to => "home#admin_dashboard"
  33
+  end
  34
+
  35
+  authenticated do
  36
+    match "/dashboard", :to => "home#user_dashboard"
  37
+  end
  38
+
  39
+  unauthenticated do
  40
+    match "/join", :to => "home#join"
  41
+  end
  42
+
31 43
   # Routes for constraints testing
32 44
   devise_for :headquarters_admin, :class_name => "Admin", :path => "headquarters", :constraints => {:host => /192\.168\.1\.\d\d\d/}
33 45
   
Commit_comment_tip

Tip: You can add notes to lines in a file. Hover to the left of a line to make a note

Something went wrong with that request. Please try again.