require 'addressable/uri' # This controller is responsible for creating and destroying # authenticated user sessions. # # The creation uses the DatabaseAuthenticatable strategy, while the destruction # simply destroys any session, whatever strategy it was created with. Janus # hooks will be called, of course, allowing to destroy any Rememberable cookies # for instance, as well as any user defined behavior. # class Janus::SessionsController < ApplicationController include Janus::InternalHelpers # include Janus::UrlHelpers helper JanusHelper # skip_before_filter :authenticate_user! def new params[:return_to] ||= request.env["HTTP_REFERER"] if signed_in?(janus_scope) redirect_after_sign_in(send("current_#{janus_scope}")) else self.resource = resource_class.new respond_with(resource) end end def create self.resource = resource_class.find_for_database_authentication(resource_authentication_params) if resource && resource.valid_password?(params[resource_name][:password]) janus.login(resource, :scope => janus_scope, :rememberable => params[:remember_me]) respond_to do |format| format.html { redirect_after_sign_in(resource) } format.any { head :ok } end else respond_to do |format| format.html do self.resource ||= resource_class.new(resource_authentication_params) resource.clean_up_passwords resource.errors.add(:base, :not_found) render "new", :status => :unauthorized end format.any { head :unauthorized } end end end def destroy janus.logout(janus_scope) respond_to do |format| format.html { redirect_to after_sign_out_url(janus_scope) } format.any { head :ok } end end # An overridable method that returns the default path to return the just # signed in user to. Defaults to return the user object, which will be # interpreted by rails as `user_path(user)`. def after_sign_in_url(user) user end # An overridable method that returns the default path to return the just # signed out user to. Defaults to `root_url`. def after_sign_out_url(scope) root_url end # Returns true if host is request.host. You may want to overwrite this method # to check if a user can access the current host and return false otherwise. # # For instance when a user signed in from a subdomain she can't access, and # you want to redirect her to another subdomain. def valid_host?(host) host == request.host end # Must return true if host is known and we allow to redirect the user # with an auth_token. # # Warning: must be overwritten by child classes because it always # returns false by default! def valid_remote_host?(host) false end # Returns an Array of URL that we shouldn't automatically return to. It # actually returns URL to prevent infinite loops. We must for instance # never return to new_sesssion_path. # # If you ever needd to override this method, don't forget to call `super`. # For instance: # # def never_return_to(scope) # super + [ my_peculiar_path, another_path ] # end # def never_return_to(scope) scope = Janus.scope_for(scope) list = [new_session_path(scope)] begin list + [ destroy_session_path(scope), new_password_path(scope), edit_password_path(scope) ] rescue NoMethodError list end end # Either redirects the user to after_sign_in_url or to params[:return_to]. # # If params[:return_to] is an absolute URL, and not just a path, # valid_remote_host? will be invoked to check wether we should redirect # to this URL or not, in order to secure auth tokens for # RemoteAuthenticatable to leak into the wild. def redirect_after_sign_in(user) if params[:return_to].present? return_to = Addressable::URI.parse(params[:return_to]) unless never_return_to(user).include?(return_to.path) # path or same host redirection if valid_host?(return_to.host || request.host) redirect_to params[:return_to] return end # external host redirection if valid_remote_host?(return_to.host) if user.class.include?(Janus::Models::RemoteAuthenticatable) query = return_to.query_values || {} return_to.query_values = query.merge( user.class.remote_authentication_key => user.generate_remote_token! ) end redirect_to return_to.to_s return end end end redirect_to after_sign_in_url(user) end end