Devise gem in Rails: generate user_root_path - ruby-on-rails

Trying to redirect users to their associated 'home' page after successful login w/out nil'ing out stored_location_for(resource_or_scope)...which is giving me some endless redirect loops (pretty sure I've set it up wrong).
Regardless, I'm looking for a better approach...
Devise's docs state: After
signing in a user, confirming the
account or updating the password,
Devise will look for a scoped root
path to redirect. Example: For a
:user resource, it will use
user_root_path if it exists,
otherwise default root_path will be
used. This means that you need to set
the root inside your routes: root :to => "home"
I'm sorta a newbie...how does one go about generating this home_root_path for each user?
rDocs also mention:
-- (Object) after_sign_in_path_for(resource_or_scope)
The default url to be used after
signing in. This is used by all Devise
controllers and you can overwrite it
in your ApplicationController to
provide a custom hook for a custom
resource.
By default, it first tries to find a resource_root_path, otherwise
it uses the root path. For a user
scope, you can define the default url
in the following way:
map.user_root '/users', :controller => 'users' # creates user_root_path
map.namespace :user do |user|
user.root :controller => 'users' # creates user_root_path
end
but these just gives me undefined local variable or methodmap' for #ActionDispatch::Routing::Mapper:…` errors.

If you would like to redirect using a route in answer to your question below:
how does one go about generating this home_root_path for each user?
This will work if you place it in your config/routes file. It will redirect a user to articles#index after (for example) a successful confirmation.
# Rails 4+
get 'user_root' => 'articles#index', as: :user_root
# Rails 3
match 'user_root' => 'articles#index', as: :user_root
See Devise: Controllers Filters and Helpers

You could try something like this:
application_controller.rb:
def after_sign_in_path_for(resource_or_scope)
# return home_page_path for user using current_user method
end

Dug around a bit to figure out the same thing. #polarblau's answer is correct,
def after_sign_in_path_for(resource_or_scope)
user_info_path(current_user)
end
where user_info_path is the path to the page you wish to display.
Also, I would allow this to fall back to super just in case, although I'm not entirely sure if that is necessary...
def after_sign_in_path_for(resource)
if resource.is_a(User)
user_info_path(resource)
else
super
end
end

I spent several hours trying to get the same functionality, and this is the code that ended up working for me:
def after_sign_in_path_for(resource)
current_user
end
If I ever tried current_user_path, I always got undefined local variable or method current_user_path errors.
Also, I'm using Rails 3.2.8 and Devise 2.1.2.
Hope that helps.

Based on #SnapShot answer, this worked for me. I'm using multiple devise models, trying to redirect back to the users profile edit page.
get 'user_root', to: redirect('/users/edit'), as: :user_root

ROR 7 answer
get '/users/home' => 'application#test', as: :user_root

Related

Undefined local variable or method 'current_user' for Namespaced Controller

I'm using Devise along with CanCan/Rolify for authentication for my rails app and I'm having a hard time understanding how to pass Devise methods to namespaced controllers.
I'm using a standard users model and controller for the main part of my app, everything on that side works well and good. However, I have set it up that if an account with a specific role (which is determined after registration) logs in using the main login page, it will redirect them to namespaced views and controllers.
Lets say that user_id: 3 logs in and is redirected to 0.0.0.0/external/dashboard, I can call Devise's current_user method on the dashboard page after redirection and returns the correct information. However, when I set a link_to for that user to be able to edit his profile at 0.0.0.0/external/users/3/edit I receive the following error:
Started GET "/external/users/3/edit" for 127.0.0.1 at 2017-08-31 21:40:59 -0400
ActionController::RoutingError (undefined local variable or method `current_user' for External::UsersController:Class)
routes
devise_for :users, controllers: { confirmations: 'confirmations', registrations: 'registrations' }
# External routes
namespace :external do
resources :properties, :clients, :orders, :deeds, :mortgages, :users, :owners, :dashboard
end
external/dashboard view
<%= user_name_helper(current_user) %>
user_name_helper (produces user edit link)
def user_name_helper(user)
if user.name.blank?
link_to "Update your profile here.", edit_external_user_path(user)
else
"Hello, #{user.name}"
end
end
Error happens when I click the link produced by this helper.
But <%= link_to fa_icon("sign-out", text: "Logout"), destroy_user_session_path, method: :delete %> works fine from 0.0.0.0/external/dashboard.
So I am confused as to why I can call current_user within the view for the External::DashboardController but can't call it within the External::UsersController. All namespaced controllers inherit directly from ApplicationController and I want to keep only using a single users model. I've searched through a bunch of different articles but can't seem to find an answer with a configuration similar to mine, so can anyone explain to me what I might be doing wrong and how to get this to work as well as allowing me to have the ability to use any other Devise methods in my namespaced views? Thanks in advance.
Welp, I figured it out. The issue was that I was calling a conditional before_filter in my external/ controllers as follows:
before_filter :client_only unless current_user.has_role? :admin
Once I removed that everything worked as intended. I still have the following before_filter in place which works.
before_filter :client_only
...
private
def client_only
redirect_to root_path unless current_user.is_client?
end
Not entirely sure why the conditional filter wouldn't work the way I originally had it so if anyone has any insight and feels like sharing then I'm all ears, thanks.

Password reset reaching Controller but not method with devise

I am using a devise scope for my password reset route, and it is hitting the controller but not the actual method inside the controller. Mind boggling... Any help is appreciated.
devise_scope :dashboard_users do
post 'forgot_password', to: 'sessions#forgot_password'
put 'reset_password', to: 'sessions#reset_password'
end
That's in my routes.rb and then this is my sessions_controller
class Api::Dashboard::V1::SessionsController < Devise::SessionsController
p "hello"
def forgot_password
...
end
end
It will print the "hello" but wont print or execute any code inside the method.
hello
Processing by Api::Dashboard::V1::SessionsController#forgot_password as */*
Parameters: {"email"=>"jovannyadams#rmlabs.org"}
[Devise] Could not find devise mapping for path "/api/dashboard/v1/forgot_password".
This may happen for two reasons:
1) You forgot to wrap your route inside the scope block. For example:
devise_scope :user do
get "/some/route" => "some_devise_controller"
end
2) You are testing a Devise controller bypassing the router.
If so, you can explicitly tell Devise which mapping to use:
#request.env["devise.mapping"] = Devise.mappings[:user]
I don't know the exact answer to your question, but I have some ideas. First, the p "hello" is getting executed on file load, not on the request. It's probably confusing because Rails will automatically reload your controller on requests, printing the "hello" message. You can test this in an IRB console:
class Test
p "hello"
end
Second, I would take a look at your rake routes output. You may be sending requests to the wrong place. My hunch is that you need to adjust your to settings for the routes to something like:
post 'forgot_password', to: 'api/dashboard/v1/sessions#forgot_password'
put 'reset_password', to: 'api/dashboard/v1/sessions#reset_password'
That change will point the requests to your properly namespaced controller. The previous routes may have sent requests to a different sessions controller that was defined on the global namespace by either you or Devise.
Also, take a look at these posts from devise wiki pages:
https://github.com/plataformatec/devise/wiki/How-To:-Change-the-default-sign_in-and-sign_out-routes
https://github.com/plataformatec/devise/wiki/How-To:-Customize-routes-to-user-registration-pages

rails: how to check admin user for setting up a route?

I need to set up a route that is only valid if the logged in user is admin (user.admin?) using devise. I'm doing this for sidekiq, but the question is valid for any similar use.
Here is my route code:
class AdminConstraint
def matches?(request)
!request.session['warden.user.user.key'].nil? and request.session['warden'].user.admin?
end
end
require 'sidekiq/web'
mount Sidekiq::Web => '/sidekiq', :constraints => AdminConstraint.new
I got this code from the sidekiq wiki: https://github.com/mperham/sidekiq/wiki/Monitoring
I tried the code they posted, which didn't work so I made some modifications as I just posted. The code above doesn't work as user evaluates to nil.
What is the correct secure way to deal with this?
thanks for any help!
If you are using devise to authenticate users, what you need to do is as follows:
Inside your routes.rb file, replace this line:
mount Sidekiq::Web => '/sidekiq', :constraints => AdminConstraint.new
with this block:
authenticate :user, lambda { |u| u.has_role? :admin } do
mount Sidekiq::Web => '/sidekiq'
end
What the block does, is telling to your devise mechanism to check first if the user is authenticated (the authenticate method is a devise method), and also that the user has the admin role (which the lambda block checks), before defining the route to the sidekiq UI.
This way, only users with an admin role will be able to access this route. Other users will get routing error in development and 404 error in production.
One more thing, the has_role? method is a rolify gem which in my case I use, but you can replace it with any other method/query that checks if the user is admin.
You're not really trying to change the route depending on who the user is (I'm not even sure if it's possible in rails without a lot of hacking), you're trying to authorize a user's action.
There are several good authorization libraries. Personally I like CanCan, but there are many others.
if you are doing any type of filtering in the controller for devise you can use the helpers
class ###Controller < ApplicationController
before_filter: :authenticate_admin!
def index
end
or whichever devise model you are trying to allow access for i.e
before_filter: :authenticate_user!
before_filter: :authenticate_author!
like bdares said. you can use the CanCan gem for authorization if the helper methods in devise isn't enough
hope it helps!

How do you set a session variable to tell Devise where to redirect after Sign In or Sign Up?

Does devise have a method built in that you can pass a variable which contains a URL to redirect the user to after they sign in or sign up?
Thanks
If your model is called User, then define a user_root route in your config/routes.rb:
match '/profile', :to => "user#profile", :as => "user_root"
Devise will then automatically redirect the user to this path.
There's a page in the Wiki that explains this: https://github.com/plataformatec/devise/wiki/How-To:-Redirect-to-a-specific-page-on-successful-sign-in-out.
application_controller.rb
...
def after_sign_in_path_for(resource_or_scope)
example_path
end
def after_sign_up_path_for(resource_or_scope)
example_path
end
...

Ruby on Rails Devise, how to change signin path when authentication fails?

I have recently implemented Devise into my new app, in conjunction with Omniauth, however I am unsure how to change the default signin path in Devise so that when I call:
user_authenticated!
It will redirect to the authentication controller page.
Any ideas how to do this?
EDIT: To better explain my problem ->
What I want is when a user tries to access a page that requires you to be logged in and then gets sent to users/sign_in by the user_authenticated before filter, however I want them to be redirected to /auth via user_authenticated! not users/sign_in.
In your controller:
before_filter do
authenticate_user! rescue redirect_to auth_url
end
I don't think I really understood your problem, but to redirect to a particular page this should be the implementation
class ApplicationController < ActionController::Base
private
def after_sign_in_path_for(resource_or_scope)
root_path
end
end
if you want to have another url for the sign_in process, in your routes.rb
devise_scope :user do
get "sign_in", :to => "devise/sessions#new"
end
and everything else you need at
https://github.com/plataformatec/devise/wiki
hope it's useful

Resources