diff --git a/CHANGELOG.md b/CHANGELOG.md index bfb175dd..afe2799f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,9 @@ * Splat the arguments to `strong_parameters#permit` to work around a limitation in the `strong_parameters` gem (by @memberful) * Omniauth now uses `mapping.fullpath` when generating routes. This means if you call `devise_for :users` inside a scope, like `scope "/api"`, the scope will now apply to the omniauth route (by @AlexanderZaytsev) +* deprecations + * `expire_session_data_after_sign_in!` has been deprecated in favor of `expire_data_after_sign_in!` + ### 3.1.1 * bug fix diff --git a/app/controllers/devise/registrations_controller.rb b/app/controllers/devise/registrations_controller.rb index 22406ad8..aeeb15bd 100644 --- a/app/controllers/devise/registrations_controller.rb +++ b/app/controllers/devise/registrations_controller.rb @@ -19,7 +19,7 @@ class Devise::RegistrationsController < DeviseController respond_with resource, :location => after_sign_up_path_for(resource) else set_flash_message :notice, :"signed_up_but_#{resource.inactive_message}" if is_flashing_format? - expire_session_data_after_sign_in! + expire_data_after_sign_in! respond_with resource, :location => after_inactive_sign_up_path_for(resource) end else @@ -68,7 +68,7 @@ class Devise::RegistrationsController < DeviseController # cancel oauth signing in/up in the middle of the process, # removing all OAuth session data. def cancel - expire_session_data_after_sign_in! + expire_data_after_sign_in! redirect_to new_registration_path(resource_name) end diff --git a/lib/devise.rb b/lib/devise.rb index 99e585df..3455cb58 100644 --- a/lib/devise.rb +++ b/lib/devise.rb @@ -20,9 +20,14 @@ module Devise autoload :Helpers, 'devise/controllers/helpers' autoload :Rememberable, 'devise/controllers/rememberable' autoload :ScopedViews, 'devise/controllers/scoped_views' + autoload :SignInOut, 'devise/controllers/sign_in_out' autoload :UrlHelpers, 'devise/controllers/url_helpers' end + module Hooks + autoload :Proxy, 'devise/hooks/proxy' + end + module Mailers autoload :Helpers, 'devise/mailers/helpers' end diff --git a/lib/devise/controllers/helpers.rb b/lib/devise/controllers/helpers.rb index 697f6a3c..842a8d09 100644 --- a/lib/devise/controllers/helpers.rb +++ b/lib/devise/controllers/helpers.rb @@ -3,6 +3,7 @@ module Devise # Those helpers are convenience methods added to ApplicationController. module Helpers extend ActiveSupport::Concern + include Devise::Controllers::SignInOut included do helper_method :warden, :signed_in?, :devise_controller? @@ -96,84 +97,6 @@ module Devise request.env["devise.allow_params_authentication"] = true end - # Return true if the given scope is signed in session. If no scope given, return - # true if any scope is signed in. Does not run authentication hooks. - def signed_in?(scope=nil) - [ scope || Devise.mappings.keys ].flatten.any? do |_scope| - warden.authenticate?(:scope => _scope) - end - end - - # Sign in a user that already was authenticated. This helper is useful for logging - # users in after sign up. - # - # All options given to sign_in is passed forward to the set_user method in warden. - # The only exception is the :bypass option, which bypass warden callbacks and stores - # the user straight in session. This option is useful in cases the user is already - # signed in, but we want to refresh the credentials in session. - # - # Examples: - # - # sign_in :user, @user # sign_in(scope, resource) - # sign_in @user # sign_in(resource) - # sign_in @user, :event => :authentication # sign_in(resource, options) - # sign_in @user, :store => false # sign_in(resource, options) - # sign_in @user, :bypass => true # sign_in(resource, options) - # - def sign_in(resource_or_scope, *args) - options = args.extract_options! - scope = Devise::Mapping.find_scope!(resource_or_scope) - resource = args.last || resource_or_scope - - expire_session_data_after_sign_in! - - if options[:bypass] - warden.session_serializer.store(resource, scope) - elsif warden.user(scope) == resource && !options.delete(:force) - # Do nothing. User already signed in and we are not forcing it. - true - else - warden.set_user(resource, options.merge!(:scope => scope)) - end - end - - # Sign out a given user or scope. This helper is useful for signing out a user - # after deleting accounts. Returns true if there was a logout and false if there - # is no user logged in on the referred scope - # - # Examples: - # - # sign_out :user # sign_out(scope) - # sign_out @user # sign_out(resource) - # - def sign_out(resource_or_scope=nil) - return sign_out_all_scopes unless resource_or_scope - scope = Devise::Mapping.find_scope!(resource_or_scope) - user = warden.user(:scope => scope, :run_callbacks => false) # If there is no user - - warden.raw_session.inspect # Without this inspect here. The session does not clear. - warden.logout(scope) - warden.clear_strategies_cache!(:scope => scope) - instance_variable_set(:"@current_#{scope}", nil) - - !!user - end - - # Sign out all active users or scopes. This helper is useful for signing out all roles - # in one click. This signs out ALL scopes in warden. Returns true if there was at least one logout - # and false if there was no user logged in on all scopes. - def sign_out_all_scopes(lock=true) - users = Devise.mappings.keys.map { |s| warden.user(:scope => s, :run_callbacks => false) } - - warden.raw_session.inspect - warden.logout - expire_devise_cached_variables! - warden.clear_strategies_cache! - warden.lock! if lock - - users.any? - end - # Returns and delete (if it's navigational format) the url stored in the session for # the given scope. Useful for giving redirect backs after sign up: # @@ -257,14 +180,6 @@ module Devise redirect_to after_sign_in_path_for(resource) end - def expire_session_data_after_sign_in! - # session.keys will return an empty array if the session is not yet loaded. - # This is a bug in both Rack and Rails. - # A call to #empty? forces the session to be loaded. - session.empty? - session.keys.grep(/^devise\./).each { |k| session.delete(k) } - end - # Sign out a user and tries to redirect to the url specified by # after_sign_out_path_for. def sign_out_and_redirect(resource_or_scope) @@ -279,7 +194,7 @@ module Devise def handle_unverified_request sign_out_all_scopes(false) request.env["devise.skip_storage"] = true - expire_devise_cached_variables! + expire_data_after_sign_out! super # call the default behaviour which resets the session end @@ -299,8 +214,15 @@ module Devise private - def expire_devise_cached_variables! + def expire_session_data_after_sign_in! + ActiveSupport::Deprecation.warn "expire_session_data_after_sign_in! is deprecated " \ + "in favor of expire_data_after_sign_in!" + expire_data_after_sign_in! + end + + def expire_data_after_sign_out! Devise.mappings.each { |_,m| instance_variable_set("@current_#{m.name}", nil) } + super end end end diff --git a/lib/devise/controllers/rememberable.rb b/lib/devise/controllers/rememberable.rb index d216e6d8..c2958aab 100644 --- a/lib/devise/controllers/rememberable.rb +++ b/lib/devise/controllers/rememberable.rb @@ -1,24 +1,14 @@ module Devise module Controllers # A module that may be optionally included in a controller in order - # to provide remember me behavior. + # to provide remember me behavior. Useful when signing in is done + # through a callback, like in Omniauth. module Rememberable # Return default cookie values retrieved from session options. def self.cookie_values Rails.configuration.session_options.slice(:path, :domain, :secure) end - # A small warden proxy so we can remember and forget uses from hooks. - class Proxy #:nodoc: - include Devise::Controllers::Rememberable - - delegate :cookies, :env, :to => :@warden - - def initialize(warden) - @warden = warden - end - end - # Remembers the given resource by setting up a cookie def remember_me(resource) return if env["devise.skip_storage"] diff --git a/lib/devise/controllers/sign_in_out.rb b/lib/devise/controllers/sign_in_out.rb new file mode 100644 index 00000000..78585387 --- /dev/null +++ b/lib/devise/controllers/sign_in_out.rb @@ -0,0 +1,103 @@ +module Devise + module Controllers + # Provide sign in and sign out functionality. + # Included by default in all controllers. + module SignInOut + # Return true if the given scope is signed in session. If no scope given, return + # true if any scope is signed in. Does not run authentication hooks. + def signed_in?(scope=nil) + [ scope || Devise.mappings.keys ].flatten.any? do |_scope| + warden.authenticate?(:scope => _scope) + end + end + + # Sign in a user that already was authenticated. This helper is useful for logging + # users in after sign up. + # + # All options given to sign_in is passed forward to the set_user method in warden. + # The only exception is the :bypass option, which bypass warden callbacks and stores + # the user straight in session. This option is useful in cases the user is already + # signed in, but we want to refresh the credentials in session. + # + # Examples: + # + # sign_in :user, @user # sign_in(scope, resource) + # sign_in @user # sign_in(resource) + # sign_in @user, :event => :authentication # sign_in(resource, options) + # sign_in @user, :store => false # sign_in(resource, options) + # sign_in @user, :bypass => true # sign_in(resource, options) + # + def sign_in(resource_or_scope, *args) + options = args.extract_options! + scope = Devise::Mapping.find_scope!(resource_or_scope) + resource = args.last || resource_or_scope + + expire_data_after_sign_in! + + if options[:bypass] + warden.session_serializer.store(resource, scope) + elsif warden.user(scope) == resource && !options.delete(:force) + # Do nothing. User already signed in and we are not forcing it. + true + else + warden.set_user(resource, options.merge!(:scope => scope)) + end + end + + # Sign out a given user or scope. This helper is useful for signing out a user + # after deleting accounts. Returns true if there was a logout and false if there + # is no user logged in on the referred scope + # + # Examples: + # + # sign_out :user # sign_out(scope) + # sign_out @user # sign_out(resource) + # + def sign_out(resource_or_scope=nil) + return sign_out_all_scopes unless resource_or_scope + scope = Devise::Mapping.find_scope!(resource_or_scope) + user = warden.user(:scope => scope, :run_callbacks => false) # If there is no user + + warden.raw_session.inspect # Without this inspect here. The session does not clear. + warden.logout(scope) + warden.clear_strategies_cache!(:scope => scope) + instance_variable_set(:"@current_#{scope}", nil) + + !!user + end + + # Sign out all active users or scopes. This helper is useful for signing out all roles + # in one click. This signs out ALL scopes in warden. Returns true if there was at least one logout + # and false if there was no user logged in on all scopes. + def sign_out_all_scopes(lock=true) + users = Devise.mappings.keys.map { |s| warden.user(:scope => s, :run_callbacks => false) } + + warden.raw_session.inspect + warden.logout + expire_data_after_sign_out! + warden.clear_strategies_cache! + warden.lock! if lock + + users.any? + end + + private + + def expire_data_after_sign_in! + # session.keys will return an empty array if the session is not yet loaded. + # This is a bug in both Rack and Rails. + # A call to #empty? forces the session to be loaded. + session.empty? + session.keys.grep(/^devise\./).each { |k| session.delete(k) } + end + + def expire_data_after_sign_out! + # session.keys will return an empty array if the session is not yet loaded. + # This is a bug in both Rack and Rails. + # A call to #empty? forces the session to be loaded. + session.empty? + session.keys.grep(/^devise\./).each { |k| session.delete(k) } + end + end + end +end \ No newline at end of file diff --git a/lib/devise/hooks/forgetable.rb b/lib/devise/hooks/forgetable.rb index bf0374f4..50e2062c 100644 --- a/lib/devise/hooks/forgetable.rb +++ b/lib/devise/hooks/forgetable.rb @@ -4,6 +4,6 @@ # This avoids forgetting deleted users. Warden::Manager.before_logout do |record, warden, options| if record.respond_to?(:forget_me!) - Devise::Controllers::Rememberable::Proxy.new(warden).forget_me(record) + Devise::Hooks::Proxy.new(warden).forget_me(record) end end diff --git a/lib/devise/hooks/proxy.rb b/lib/devise/hooks/proxy.rb new file mode 100644 index 00000000..866d404d --- /dev/null +++ b/lib/devise/hooks/proxy.rb @@ -0,0 +1,16 @@ +module Devise + module Hooks + # A small warden proxy so we can remember, forget and + # sign out users from hooks. + class Proxy #:nodoc: + include Devise::Controllers::Rememberable + include Devise::Controllers::SignInOut + + delegate :cookies, :env, :session, :to => :@warden + + def initialize(warden) + @warden = warden + end + end + end +end \ No newline at end of file diff --git a/lib/devise/hooks/rememberable.rb b/lib/devise/hooks/rememberable.rb index c27557b6..0dc19bbd 100644 --- a/lib/devise/hooks/rememberable.rb +++ b/lib/devise/hooks/rememberable.rb @@ -2,6 +2,6 @@ Warden::Manager.after_set_user :except => :fetch do |record, warden, options| scope = options[:scope] if record.respond_to?(:remember_me) && options[:store] != false && record.remember_me && warden.authenticated?(scope) - Devise::Controllers::Rememberable::Proxy.new(warden).remember_me(record) + Devise::Hooks::Proxy.new(warden).remember_me(record) end end \ No newline at end of file