In my Rails app I want to forward my users to their personal sign in page which is stored in a cookie :user_subdomain. So when a user goes to www.app.com/sign_in, s/he should be automatically forwarded to www.app.com/sign_in/mycompany.
How can this be achieved?
I would like to keep my sign_in_path helper method because it's sprinkled all over my app.
If I simply redirect the new action using redirect_to sign_in_path(:name => cookies[:user_subdomain]) I end up in a loop.
Thanks for any pointers.
# routes.rb:
get 'sign_in', :to => 'sessions#new'
# sessions_controller.rb:
class SessionsController < ApplicationController
def new
params[:name] ||= cookies[:user_subdomain]
end
...
end
Then the solution is easy. You do not need to redirect, you just need optional parameter for your routes.
bound parameters
# routes.rb:
get 'sign_in(/:company_name)', :to => 'sessions#new'
# This will allow
# /sign_in
# and
# /sign_in/mycompany
# Both will lead to same action and you can keep your helper
# sessions_controller.rb:
class SessionsController < ApplicationController
def new
params[:company_name] ||= cookies[:user_subdomain]
# You logic here
end
...
end
Related
I am trying make a project in which /username gets redirected to that username's profile. How can I make this happen?
The route would be: "get /:username", to: "users#profile"
You would change users#profile to whatever your controller action is called.
You need to make sure to put this at the end of your routes. Otherwise it will intercept all your routes.
For example, don't do the following:
get "/:username", to: "users#profile"
get "/foo", to: "pages#bar"
Because you will never be able to reach the pages#bar endpoint.
Problem with the previous answer is that anything that doesn't match in routes would be routed to users#profile.
Alternatively, and to solve that issue, you can create a dynamic router, like this:
class DynamicRouter
def self.load
Rails.application.routes.draw do
User.all.each do |user|
puts "Routing #{user.name}"
get "/#{user.name}", :to => "users#profile", defaults: { id: user.id }
end
end
end
def self.reload
Rails.application.routes_reloader.reload!
end
end
Then on the UsersController:
class UsersController < ApplicationController
def profile
#user = User.find(params[:id])
redirect_to not_found_path unless #user
end
end
And to actually generate the routes at server start:
Rails.application.routes.draw do
...
get 'not_found' => 'somecontroller#not_found', as: :not_found
DynamicRouter.load
end
Finally to reload routes when a user is added/updated:
class User < ActiveRecord::Base
...
after_save :reload_routes
def reload_routes
DynamicRouter.reload
end
end
Hope it helps!
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'd like to be able to set the root url in routes.rb based on the logged in/out state of Authlogic. Here is an example of what I need to do:
root :to => 'users#index', :constraints => lambda { |request| # ??? }
root :to => 'welcome#index'
get '/' => 'users#index', :as => 'user_root'
This would enable logged in users to go to users#index and logged out users to go to welcome#index without doing a redirect. I do know how to set a before_filter and redirect accordingly, but I'd rather not do that.
I know this is possible with Devise but I need to solve this for Authlogic. Similar question here without an answer.
You can create a custom route constraint pretty easily. Any object that responds to matches? can be handed to the :constraints route option. To check for Authlogic authentication, you would need to pull your Authlogic methods from your ApplicationController and put them somewhere else. I recommend using a concern for these anyways.
# app/concerns/authlogic_methods.rb
module AuthlogicHelpers
extend ActiveSupport::Concern
module InstanceMethods
private # All methods are private
def current_user_session
return #current_user_session if defined?(#current_user_session)
#current_user_session = UserSession.find
end
def current_user
return #current_user if defined?(#current_user)
#current_user = current_user_session && current_user_session.user
end
end
end
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
include AuthlogicHelpers
end
# config/initializers/route_constraints.rb
module RouteConstraints
class LoggedIn
include AuthlogicHelpers
def matches?(request)
current_user
end
end
end
# config/routes.rb
constraints RouteConstraints::LoggedIn.new do
root :to => 'dashboard#show'
end
Note that we're instantiating a new RouteConstraints::LoggedIn object. This is because the Authlogic methods current_user and current_user_session are defined as instance methods and create instance variables — if they aren't separated for each request, you won't be sure a user is actually logged in or another one was logged in before.
You might run into problems if Authlogic tries to access params directly. In this case, you will probably need to override the UserSession.new method to accept the request object as an argument and call request.params to get the params.
Authlogic already have the machinery native (tested against Rails 6.0 and Authlogic 6.0):
class AuthlogicSignedInConstraint
def matches?(request)
Authlogic::Session::Base.controller = Authlogic::ControllerAdapters::AbstractAdapter.new(request)
user_session = UserSession.find
user_session && user_session.user
end
end
Authlogic in recent versions require a controller object to be instantiated otherwise it can't find the session data necessary for loading a session.
In fact, contained under authlogic/controller_adapters/ are adapters for various controller implementations: Rails Controllers, Sinatra and Rack.
The interface is fairly simple, so I wrote an adapter to work with the ActionDispatch::Request that one has access to within a constraint.
This has been tested on Rails 4.2, Rails 5 should be similar.
class AuthlogicRequestAdapter
attr_reader :request
def initialize(request)
#request = request
end
def authenticate_with_http_basic
end
def cookies
request.cookies
end
def params
request.params
end
def session
_, session_hash = Rails.application.config.session_store.new(Rails.application, Rails.application.config.session_options).load_session(request.env)
session_hash
end
end
Example of use in Rails
Route Constraint
class UserRouteConstraint
def matches?(request)
Authlogic::Session::Base.controller = AuthlogicRequestAdapter.new(request)
user = UserSession.find
if user&.record.present?
true
else
false
end
end
end
routes.rb
MyRailsApp::Application.routes.draw do
# ... other routes
constraints(UserRouteContraint.new) do
root :to => 'users#index'
end
root :to => 'welcome#index'
get '/' => 'users#index', :as => 'user_root'
end
Using devise, how do i make my Sign Up as my landing/welcome page and then after sign up they go inside the site to the Profile/signed in area?
I am trying to figure out how to make it like where Facebook their is two sides of the website; the Outside (sign up, about,contact,etc) and The Inside (Profile,sign out,etc) for Users only after sign up or sign in.
Thank you.
P.S. I am new at Ruby on Rails and creating applications but i did do the authentication system with the Rails 3 Tutorial, i understand most things to start using Devise, i jst dont know where to start with this situation.
I was going to use 2 application layouts, 1 before sign up which is layouts/welcome.html.erb with PagesController (about,terms,etc) and the other for signed in users which will be layouts/application.html.erb with ApplicationController (profile,news,add,etc), is this the best steps?
in your routes.rb:
root :to => 'welcome#index'
Where welcome is the controller and index is the action.
In your application controller:
def after_sign_in_path_for(user)
"/url_you_want_to_redirect_to/"
end
This my new and updated way using Rails 3.1.0 and Devise 1.5.0:
routes.rb
root :to => "pages#redirect_to_sign_up"
devise_for :users do
get "welcome" => "devise/registrations#new", :as => :new_user_registration
get "account_settings" => "devise/registrations#edit"
get "sign_in" => "devise/sessions#new"
get "sign_out" => "devise/sessions#destroy"
get "new_password", :to => "devise/passwords#new"
end
match 'home', :to => "user_pages#home"
namespace :user do
root :to => "user_pages#home"
end
application_controller.rb
class ApplicationController < ActionController::Base
protect_from_forgery
protected
def after_sign_in_path_for(resource)
stored_location_for(:user) || root_path
end
private
def after_sign_out_path_for(resource)
stored_location_for(:user) || root_path
end
end
pages_controller.rb
class PagesController < ApplicationController
def redirect_to_sign_up
if signed_in?.blank?
redirect_to new_user_registration_path
else
redirect_to home_path
end
end
end
user_pages_controller.rb
class UserPagesController < ApplicationController
before_filter :authenticate_user!
def home
end
def profile
end
end
I find it easiest to root to the desired authenticated landing page and just use a before_filter to force the user to sign in/sign up first via a before_filter.
In this case, let's say your "signed in area" is a controller/action called profile/index.
In your routes.rb, set the root to profile/index.
root :to => 'profile#index'
Then in your profile_controller, set the following before_filter.
before_filter :authenticate_user!
This will automatically push a logged in user (or one that logged in earlier and set a Remember Me cookie) straight to the profile page. Any unauthenticated users will automatically end up on Sign In. You'll want a link (or separate tab) on that page to Sign Up as well.
On the root page check to see if the user is signed in, and redirect based on that.
redirect_to sign_up_path if current_user.nil?
Alternatively, you could render different templates instead of a redirect, but I think it's cleaner to have a 1:1 mapping between urls and pages.
Another approach is to modify devise. Edit devise_gem_path/lib/devise/failure_app.rb and replace the two occurrences in the redirect_url method of:
:"new_#{scope}_session_path"
with:
:"new_#{scope}_registration_path"
A less destructive solution would make redirect_url's response more configurable.
How can I have make auto redirecting every user who goes to mysite.com/ to mysite.com/features?
thanks
Set your root route to direct folks there (these are Rails 3 routes):
in config/routes.rb
root "content#features"
in app/controllers/contents_controller.rb
class ContentsController < ApplicationController
def features
end
end
That won't do a redirect, however. To do that, you'll need something like this:
in config/routes.rb
match "features" => "contents#features", :as => "features"
root "content#index"
in app/controllers/contents_controller.rb
class ContentsController < ApplicationController
def index
redirect_to features_url
end
def features
end
end