sorcery 0.1.4 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. data/Gemfile +4 -2
  2. data/Gemfile.lock +16 -13
  3. data/README.rdoc +111 -78
  4. data/Rakefile +5 -0
  5. data/VERSION +1 -1
  6. data/lib/generators/sorcery_migration/sorcery_migration_generator.rb +24 -0
  7. data/lib/generators/sorcery_migration/templates/activity_logging.rb +17 -0
  8. data/lib/generators/sorcery_migration/templates/brute_force_protection.rb +11 -0
  9. data/lib/generators/sorcery_migration/templates/core.rb +16 -0
  10. data/lib/generators/sorcery_migration/templates/oauth.rb +14 -0
  11. data/lib/generators/sorcery_migration/templates/remember_me.rb +15 -0
  12. data/lib/generators/sorcery_migration/templates/reset_password.rb +13 -0
  13. data/lib/generators/sorcery_migration/templates/user_activation.rb +17 -0
  14. data/lib/sorcery/controller/adapters/sinatra.rb +97 -0
  15. data/lib/sorcery/controller/submodules/activity_logging.rb +20 -7
  16. data/lib/sorcery/controller/submodules/brute_force_protection.rb +9 -2
  17. data/lib/sorcery/controller/submodules/http_basic_auth.rb +18 -9
  18. data/lib/sorcery/controller/submodules/oauth/oauth1.rb +33 -0
  19. data/lib/sorcery/controller/submodules/oauth/oauth2.rb +24 -0
  20. data/lib/sorcery/controller/submodules/oauth/providers/facebook.rb +64 -0
  21. data/lib/sorcery/controller/submodules/oauth/providers/twitter.rb +61 -0
  22. data/lib/sorcery/controller/submodules/oauth.rb +95 -0
  23. data/lib/sorcery/controller/submodules/remember_me.rb +14 -5
  24. data/lib/sorcery/controller/submodules/session_timeout.rb +6 -1
  25. data/lib/sorcery/controller.rb +29 -17
  26. data/lib/sorcery/engine.rb +9 -2
  27. data/lib/sorcery/model/submodules/activity_logging.rb +13 -8
  28. data/lib/sorcery/model/submodules/brute_force_protection.rb +11 -8
  29. data/lib/sorcery/model/submodules/oauth.rb +53 -0
  30. data/lib/sorcery/model/submodules/remember_me.rb +5 -3
  31. data/lib/sorcery/model/submodules/reset_password.rb +16 -13
  32. data/lib/sorcery/model/submodules/user_activation.rb +38 -19
  33. data/lib/sorcery/model/temporary_token.rb +22 -0
  34. data/lib/sorcery/model.rb +10 -3
  35. data/lib/sorcery/sinatra.rb +14 -0
  36. data/lib/sorcery/test_helpers/rails.rb +57 -0
  37. data/lib/sorcery/test_helpers/sinatra.rb +131 -0
  38. data/lib/sorcery/test_helpers.rb +40 -0
  39. data/lib/sorcery.rb +20 -0
  40. data/sorcery.gemspec +144 -41
  41. data/spec/Gemfile +3 -2
  42. data/spec/Gemfile.lock +15 -2
  43. data/spec/rails3/app_root/.rspec +1 -0
  44. data/spec/rails3/{Gemfile → app_root/Gemfile} +4 -4
  45. data/spec/rails3/{Gemfile.lock → app_root/Gemfile.lock} +23 -3
  46. data/spec/rails3/app_root/app/controllers/application_controller.rb +42 -1
  47. data/spec/rails3/app_root/app/models/authentication.rb +3 -0
  48. data/spec/rails3/app_root/app/models/user.rb +4 -1
  49. data/spec/rails3/app_root/config/application.rb +1 -3
  50. data/spec/rails3/app_root/config/routes.rb +1 -10
  51. data/spec/rails3/app_root/db/migrate/activation/20101224223622_add_activation_to_users.rb +6 -4
  52. data/spec/rails3/app_root/db/migrate/core/20101224223620_create_users.rb +4 -4
  53. data/spec/rails3/app_root/db/migrate/oauth/20101224223628_create_authentications.rb +14 -0
  54. data/spec/rails3/{controller_activity_logging_spec.rb → app_root/spec/controller_activity_logging_spec.rb} +13 -13
  55. data/spec/rails3/{controller_brute_force_protection_spec.rb → app_root/spec/controller_brute_force_protection_spec.rb} +16 -6
  56. data/spec/rails3/{controller_http_basic_auth_spec.rb → app_root/spec/controller_http_basic_auth_spec.rb} +3 -3
  57. data/spec/rails3/app_root/spec/controller_oauth2_spec.rb +119 -0
  58. data/spec/rails3/app_root/spec/controller_oauth_spec.rb +122 -0
  59. data/spec/rails3/{controller_remember_me_spec.rb → app_root/spec/controller_remember_me_spec.rb} +4 -4
  60. data/spec/rails3/{controller_session_timeout_spec.rb → app_root/spec/controller_session_timeout_spec.rb} +6 -6
  61. data/spec/rails3/{controller_spec.rb → app_root/spec/controller_spec.rb} +20 -13
  62. data/spec/rails3/app_root/spec/spec_helper.orig.rb +27 -0
  63. data/spec/rails3/app_root/spec/spec_helper.rb +62 -0
  64. data/spec/rails3/{user_activation_spec.rb → app_root/spec/user_activation_spec.rb} +60 -20
  65. data/spec/rails3/{user_activity_logging_spec.rb → app_root/spec/user_activity_logging_spec.rb} +4 -4
  66. data/spec/rails3/{user_brute_force_protection_spec.rb → app_root/spec/user_brute_force_protection_spec.rb} +7 -7
  67. data/spec/rails3/app_root/spec/user_oauth_spec.rb +39 -0
  68. data/spec/rails3/{user_remember_me_spec.rb → app_root/spec/user_remember_me_spec.rb} +4 -4
  69. data/spec/rails3/{user_reset_password_spec.rb → app_root/spec/user_reset_password_spec.rb} +21 -41
  70. data/spec/rails3/app_root/spec/user_spec.rb +317 -0
  71. data/spec/sinatra/Gemfile +12 -0
  72. data/spec/sinatra/Gemfile.lock +134 -0
  73. data/spec/sinatra/Rakefile +10 -0
  74. data/spec/sinatra/authentication.rb +3 -0
  75. data/spec/sinatra/db/migrate/activation/20101224223622_add_activation_to_users.rb +17 -0
  76. data/spec/sinatra/db/migrate/activity_logging/20101224223624_add_activity_logging_to_users.rb +17 -0
  77. data/spec/sinatra/db/migrate/brute_force_protection/20101224223626_add_brute_force_protection_to_users.rb +11 -0
  78. data/spec/sinatra/db/migrate/core/20101224223620_create_users.rb +16 -0
  79. data/spec/sinatra/db/migrate/oauth/20101224223628_create_authentications.rb +14 -0
  80. data/spec/sinatra/db/migrate/remember_me/20101224223623_add_remember_me_token_to_users.rb +15 -0
  81. data/spec/sinatra/db/migrate/reset_password/20101224223622_add_reset_password_to_users.rb +13 -0
  82. data/spec/sinatra/filters.rb +21 -0
  83. data/spec/sinatra/myapp.rb +133 -0
  84. data/spec/sinatra/sorcery_mailer.rb +25 -0
  85. data/spec/sinatra/spec/controller_activity_logging_spec.rb +85 -0
  86. data/spec/sinatra/spec/controller_brute_force_protection_spec.rb +69 -0
  87. data/spec/sinatra/spec/controller_http_basic_auth_spec.rb +53 -0
  88. data/spec/sinatra/spec/controller_oauth2_spec.rb +119 -0
  89. data/spec/sinatra/spec/controller_oauth_spec.rb +121 -0
  90. data/spec/sinatra/spec/controller_remember_me_spec.rb +64 -0
  91. data/spec/sinatra/spec/controller_session_timeout_spec.rb +52 -0
  92. data/spec/sinatra/spec/controller_spec.rb +120 -0
  93. data/spec/sinatra/spec/spec.opts +4 -0
  94. data/spec/sinatra/spec/spec_helper.rb +44 -0
  95. data/spec/sinatra/spec/user_activation_spec.rb +188 -0
  96. data/spec/sinatra/spec/user_activity_logging_spec.rb +36 -0
  97. data/spec/sinatra/spec/user_brute_force_protection_spec.rb +76 -0
  98. data/spec/sinatra/spec/user_oauth_spec.rb +39 -0
  99. data/spec/sinatra/spec/user_remember_me_spec.rb +66 -0
  100. data/spec/sinatra/spec/user_reset_password_spec.rb +178 -0
  101. data/spec/{rails3 → sinatra/spec}/user_spec.rb +68 -38
  102. data/spec/sinatra/user.rb +6 -0
  103. data/spec/sinatra/views/test_login.erb +4 -0
  104. data/spec/untitled folder +18 -0
  105. metadata +201 -58
  106. data/spec/rails3/app_root/test/fixtures/users.yml +0 -9
  107. data/spec/rails3/app_root/test/performance/browsing_test.rb +0 -9
  108. data/spec/rails3/app_root/test/test_helper.rb +0 -13
  109. data/spec/rails3/app_root/test/unit/user_test.rb +0 -8
  110. data/spec/rails3/spec_helper.rb +0 -135
  111. /data/spec/rails3/{Rakefile → app_root/Rakefile} +0 -0
@@ -1,6 +1,9 @@
1
1
  module Sorcery
2
2
  module Controller
3
3
  module Submodules
4
+ # This module helps protect user accounts by locking them down after too many failed attemps to login were detected.
5
+ # This is the controller part of the submodule which takes care of updating the failed logins and resetting them.
6
+ # See Sorcery::Model::Submodules::BruteForceProtection for configuration options.
4
7
  module BruteForceProtection
5
8
  def self.included(base)
6
9
  base.send(:include, InstanceMethods)
@@ -13,13 +16,17 @@ module Sorcery
13
16
 
14
17
  protected
15
18
 
19
+ # Increments the failed logins counter on every failed login.
20
+ # Runs as a hook after a failed login.
16
21
  def update_failed_logins_count!(credentials)
17
- user = User.where("#{User.sorcery_config.username_attribute_name} = ?", credentials[0]).first
22
+ user = Config.user_class.where("#{Config.user_class.sorcery_config.username_attribute_name} = ?", credentials[0]).first
18
23
  user.register_failed_login! if user
19
24
  end
20
25
 
26
+ # Resets the failed logins counter.
27
+ # Runs as a hook after a successful login.
21
28
  def reset_failed_logins_count!(user, credentials)
22
- user.update_attributes!(User.sorcery_config.failed_logins_count_attribute_name => 0)
29
+ user.update_attributes!(Config.user_class.sorcery_config.failed_logins_count_attribute_name => 0)
23
30
  end
24
31
  end
25
32
  end
@@ -1,6 +1,9 @@
1
1
  module Sorcery
2
2
  module Controller
3
3
  module Submodules
4
+ # This submodule integrates HTTP Basic authentication into sorcery.
5
+ # You are provided with a before filter, require_login_from_http_basic, which requests the browser for authentication.
6
+ # Then the rest of the submodule takes care of logging the user in into the session, so that the next requests will keep him logged in.
4
7
  module HttpBasicAuth
5
8
  def self.included(base)
6
9
  base.send(:include, InstanceMethods)
@@ -31,25 +34,31 @@ module Sorcery
31
34
  def require_login_from_http_basic
32
35
  (request_http_basic_authentication(realm_name_by_controller) and (session[:http_authentication_used] = true) and return) if (request.authorization.nil? || session[:http_authentication_used].nil?)
33
36
  require_login
37
+ session[:http_authentication_used] = nil unless logged_in?
34
38
  end
35
39
 
36
40
  # given to main controller module as a login source callback
37
41
  def login_from_basic_auth
38
42
  authenticate_with_http_basic do |username, password|
39
- @logged_in_user = (Config.user_class.authenticate(username, password) if session[:http_authentication_used]) || false
40
- login_user(@logged_in_user) if @logged_in_user
41
- @logged_in_user
43
+ @current_user = (Config.user_class.authenticate(username, password) if session[:http_authentication_used]) || false
44
+ login_user(@current_user) if @current_user
45
+ @current_user
42
46
  end
43
47
  end
44
48
 
49
+ # Sets the realm name by searching the controller name in the hash given at configuration time.
45
50
  def realm_name_by_controller
46
- current_controller = self.class
47
- while current_controller != ActionController::Base
48
- result = Config.controller_to_realm_map[current_controller.controller_name]
49
- return result if result
50
- current_controller = self.class.superclass
51
+ if defined?(ActionController::Base)
52
+ current_controller = self.class
53
+ while current_controller != ActionController::Base
54
+ result = Config.controller_to_realm_map[current_controller.controller_name]
55
+ return result if result
56
+ current_controller = self.class.superclass
57
+ end
58
+ nil
59
+ else
60
+ Config.controller_to_realm_map["application"]
51
61
  end
52
- nil
53
62
  end
54
63
 
55
64
  end
@@ -0,0 +1,33 @@
1
+ require 'oauth'
2
+ module Sorcery
3
+ module Controller
4
+ module Submodules
5
+ module Oauth
6
+ module Oauth1
7
+ def oauth_version
8
+ "1.0"
9
+ end
10
+
11
+ def get_request_token(token=nil,secret=nil)
12
+ return ::OAuth::RequestToken.new(get_consumer,token,secret) if token && secret
13
+ get_consumer.get_request_token(:oauth_callback => @callback_url)
14
+ end
15
+
16
+ def authorize_url(args)
17
+ get_request_token(args[:request_token],args[:request_token_secret]).authorize_url(:oauth_callback => @callback_url)
18
+ end
19
+
20
+ def get_access_token(args)
21
+ get_request_token(args[:request_token],args[:request_token_secret]).get_access_token(:oauth_verifier => args[:oauth_verifier])
22
+ end
23
+
24
+ protected
25
+
26
+ def get_consumer
27
+ ::OAuth::Consumer.new(@key, @secret, :site => @site)
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,24 @@
1
+ require 'oauth2'
2
+ module Sorcery
3
+ module Controller
4
+ module Submodules
5
+ module Oauth
6
+ module Oauth2
7
+ def oauth_version
8
+ "2.0"
9
+ end
10
+
11
+ def authorize_url(*args)
12
+ client = ::OAuth2::Client.new(@key, @secret, :site => @site)
13
+ client.web_server.authorize_url(:redirect_uri => @callback_url, :scope => @scope)
14
+ end
15
+
16
+ def get_access_token(args)
17
+ client = ::OAuth2::Client.new(@key, @secret, :site => @site)
18
+ client.web_server.get_access_token(args[:code], :redirect_uri => @callback_url)
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,64 @@
1
+ module Sorcery
2
+ module Controller
3
+ module Submodules
4
+ module Oauth
5
+ module Providers
6
+ # This module adds support for OAuth with facebook.com.
7
+ # When included in the 'config.providers' option, it adds a new option, 'config.facebook'.
8
+ # Via this new option you can configure Facebook specific settings like your app's key and secret.
9
+ #
10
+ # config.facebook.key = <key>
11
+ # config.facebook.secret = <secret>
12
+ # ...
13
+ #
14
+ module Facebook
15
+ def self.included(base)
16
+ base.module_eval do
17
+ class << self
18
+ attr_reader :facebook # access to facebook_client.
19
+
20
+ def merge_facebook_defaults!
21
+ @defaults.merge!(:@facebook => FacebookClient)
22
+ end
23
+ end
24
+ merge_facebook_defaults!
25
+ update!
26
+ end
27
+ end
28
+
29
+ module FacebookClient
30
+ class << self
31
+ attr_accessor :key,
32
+ :secret,
33
+ :callback_url,
34
+ :site,
35
+ :user_info_path,
36
+ :scope,
37
+ :user_info_mapping
38
+
39
+ include Oauth2
40
+
41
+ def init
42
+ @site = "https://graph.facebook.com"
43
+ @user_info_path = "/me"
44
+ @scope = "email,offline_access"
45
+ @user_info_mapping = {}
46
+ end
47
+
48
+ def get_user_hash(access_token)
49
+ user_hash = {}
50
+ response = access_token.get(@user_info_path)
51
+ user_hash[:user_info] = JSON.parse(response)
52
+ user_hash[:uid] = user_hash[:user_info]['id'].to_i
53
+ user_hash
54
+ end
55
+ end
56
+ init
57
+ end
58
+
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,61 @@
1
+ module Sorcery
2
+ module Controller
3
+ module Submodules
4
+ module Oauth
5
+ module Providers
6
+ # This module adds support for OAuth with Twitter.com.
7
+ # When included in the 'config.providers' option, it adds a new option, 'config.twitter'.
8
+ # Via this new option you can configure Twitter specific settings like your app's key and secret.
9
+ #
10
+ # config.twitter.key = <key>
11
+ # config.twitter.secret = <secret>
12
+ # ...
13
+ #
14
+ module Twitter
15
+ def self.included(base)
16
+ base.module_eval do
17
+ class << self
18
+ attr_reader :twitter # access to twitter_client.
19
+
20
+ def merge_twitter_defaults!
21
+ @defaults.merge!(:@twitter => TwitterClient)
22
+ end
23
+ end
24
+ merge_twitter_defaults!
25
+ update!
26
+ end
27
+ end
28
+
29
+ module TwitterClient
30
+ class << self
31
+ attr_accessor :key,
32
+ :secret,
33
+ :callback_url,
34
+ :site,
35
+ :user_info_path,
36
+ :user_info_mapping
37
+
38
+ include Oauth1
39
+
40
+ def init
41
+ @site = "https://api.twitter.com"
42
+ @user_info_path = "/1/account/verify_credentials.json"
43
+ @user_info_mapping = {}
44
+ end
45
+
46
+ def get_user_hash(access_token)
47
+ user_hash = {}
48
+ response = access_token.get(@user_info_path)
49
+ user_hash[:user_info] = JSON.parse(response.body)
50
+ user_hash[:uid] = user_hash[:user_info]['id']
51
+ user_hash
52
+ end
53
+ end
54
+ init
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,95 @@
1
+ module Sorcery
2
+ module Controller
3
+ module Submodules
4
+ # This submodule helps you login users from OAuth providers such as Twitter.
5
+ # This is the controller part which handles the http requests and tokens passed between the app and the provider.
6
+ # For more configuration options see Sorcery::Model::Oauth.
7
+ module Oauth
8
+ def self.included(base)
9
+ base.send(:include, InstanceMethods)
10
+ Config.module_eval do
11
+ class << self
12
+ attr_reader :oauth_providers # oauth providers like twitter.
13
+
14
+ def merge_oauth_defaults!
15
+ @defaults.merge!(:@oauth_providers => [])
16
+ end
17
+
18
+ def oauth_providers=(providers)
19
+ providers.each do |provider|
20
+ include Providers.const_get(provider.to_s.split("_").map {|p| p.capitalize}.join(""))
21
+ end
22
+ end
23
+ end
24
+ merge_oauth_defaults!
25
+ end
26
+ end
27
+
28
+ module InstanceMethods
29
+ protected
30
+
31
+ # sends user to authenticate at the provider's website.
32
+ # after authentication the user is redirected to the callback defined in the provider config
33
+ def auth_at_provider(provider)
34
+ @provider = Config.send(provider)
35
+ args = {}
36
+ if @provider.respond_to?(:get_request_token)
37
+ req_token = @provider.get_request_token
38
+ session[:request_token] = req_token.token
39
+ session[:request_token_secret] = req_token.secret
40
+ args.merge!({:request_token => req_token.token, :request_token_secret => req_token.secret})
41
+ end
42
+ redirect_to @provider.authorize_url(args)
43
+ end
44
+
45
+ # tries to login the user from access token
46
+ def login_from_access_token(provider)
47
+ @provider = Config.send(provider)
48
+ args = {}
49
+ args.merge!({:oauth_verifier => params[:oauth_verifier], :request_token => session[:request_token], :request_token_secret => session[:request_token_secret]}) if @provider.respond_to?(:get_request_token)
50
+ args.merge!({:code => params[:code]}) if params[:code]
51
+ @access_token = @provider.get_access_token(args)
52
+ @user_hash = @provider.get_user_hash(@access_token)
53
+ if user = Config.user_class.load_from_provider(provider,@user_hash[:uid])
54
+ reset_session
55
+ login_user(user)
56
+ user
57
+ end
58
+ end
59
+
60
+ def get_user_hash(provider)
61
+ @provider = Config.send(provider)
62
+ @provider.get_user_hash(@access_token)
63
+ end
64
+
65
+ # this method automatically creates a new user from the data in the external user hash.
66
+ # The mappings from user hash fields to user db fields are set at controller config.
67
+ # If the hash field you would like to map is nested, use slashes. For example, Given a hash like:
68
+ #
69
+ # "user" => {"name"=>"moishe"}
70
+ #
71
+ # You will set the mapping:
72
+ #
73
+ # {:username => "user/name"}
74
+ #
75
+ # And this will cause 'moishe' to be set as the value of :username field.
76
+ def create_from_provider!(provider)
77
+ provider = provider.to_sym
78
+ @provider = Config.send(provider)
79
+ @user_hash = get_user_hash(provider)
80
+ config = Config.user_class.sorcery_config
81
+ attrs = {}
82
+ @provider.user_info_mapping.each do |k,v|
83
+ (varr = v.split("/")).size > 1 ? attrs.merge!(k => varr.inject(@user_hash[:user_info]) {|hsh,v| hsh[v] }) : attrs.merge!(k => @user_hash[:user_info][v])
84
+ end
85
+ Config.user_class.transaction do
86
+ @user = Config.user_class.create!(attrs)
87
+ Config.user_class.sorcery_config.authentications_class.create!({config.authentications_user_id_attribute_name => @user.id, config.provider_attribute_name => provider, config.provider_uid_attribute_name => @user_hash[:uid]})
88
+ end
89
+ @user
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
@@ -1,6 +1,9 @@
1
1
  module Sorcery
2
2
  module Controller
3
3
  module Submodules
4
+ # The Remember Me submodule takes care of setting the user's cookie so that he will be automatically logged in to the site on every visit,
5
+ # until the cookie expires.
6
+ # See Sorcery::Model::Submodules::RememberMe for configuration options.
4
7
  module RememberMe
5
8
  def self.included(base)
6
9
  base.send(:include, InstanceMethods)
@@ -10,29 +13,35 @@ module Sorcery
10
13
  end
11
14
 
12
15
  module InstanceMethods
16
+ # This method sets the cookie and calls the user to save the token and the expiration to db.
13
17
  def remember_me!
14
- logged_in_user.remember_me!
15
- cookies[:remember_me_token] = { :value => logged_in_user.remember_me_token, :expires => logged_in_user.remember_me_token_expires_at }
18
+ current_user.remember_me!
19
+ cookies[:remember_me_token] = { :value => current_user.remember_me_token, :expires => current_user.remember_me_token_expires_at }
16
20
  end
17
21
 
22
+ # Clears the cookie and clears the token from the db.
18
23
  def forget_me!
19
- logged_in_user.forget_me!
24
+ current_user.forget_me!
20
25
  cookies[:remember_me_token] = nil
21
26
  end
22
27
 
23
28
  protected
24
29
 
30
+ # calls remember_me! if a third credential was passed to the login method.
31
+ # Runs as a hook after login.
25
32
  def remember_me_if_asked_to(user, credentials)
26
33
  remember_me! if credentials.size == 3 && credentials[2]
27
34
  end
28
35
 
36
+ # Checks the cookie for a remember me token, tried to find a user with that token and logs the user in if found.
37
+ # Runs as a login source. See 'current_user' method for how it is used.
29
38
  def login_from_cookie
30
39
  user = cookies[:remember_me_token] && Config.user_class.find_by_remember_me_token(cookies[:remember_me_token])
31
40
  if user && user.remember_me_token?
32
41
  cookies[:remember_me_token] = { :value => user.remember_me_token, :expires => user.remember_me_token_expires_at }
33
- @logged_in_user = user
42
+ @current_user = user
34
43
  else
35
- @logged_in_user = false
44
+ @current_user = false
36
45
  end
37
46
  end
38
47
  end
@@ -1,6 +1,8 @@
1
1
  module Sorcery
2
2
  module Controller
3
3
  module Submodules
4
+ # This submodule helps you set a timeout to all user sessions.
5
+ # The timeout can be configured and also you can choose to reset it on every user action.
4
6
  module SessionTimeout
5
7
  def self.included(base)
6
8
  base.send(:include, InstanceMethods)
@@ -23,16 +25,19 @@ module Sorcery
23
25
  module InstanceMethods
24
26
  protected
25
27
 
28
+ # Registers last login to be used as the timeout starting point.
29
+ # Runs as a hook after a successful login.
26
30
  def register_login_time(user, credentials)
27
31
  session[:login_time] = session[:last_action_time] = Time.now.utc
28
32
  end
29
33
 
34
+ # Checks if session timeout was reached and expires the current session if so.
30
35
  # To be used as a before_filter, before require_login
31
36
  def validate_session
32
37
  session_to_use = Config.session_timeout_from_last_action ? session[:last_action_time] : session[:login_time]
33
38
  if session_to_use && (Time.now.utc - session_to_use > Config.session_timeout)
34
39
  reset_session
35
- @logged_in_user = false
40
+ @current_user = false
36
41
  else
37
42
  session[:last_action_time] = Time.now.utc
38
43
  end
@@ -5,7 +5,7 @@ module Sorcery
5
5
  extend ClassMethods
6
6
  include InstanceMethods
7
7
  Config.submodules.each do |mod|
8
- begin
8
+ begin # FIXME: is this protection needed?
9
9
  include Submodules.const_get(mod.to_s.split("_").map {|p| p.capitalize}.join(""))
10
10
  rescue NameError
11
11
  # don't stop on a missing submodule.
@@ -32,62 +32,74 @@ module Sorcery
32
32
  # If all attempts to auto-login fail, the failure callback will be called.
33
33
  def require_login
34
34
  if !logged_in?
35
- session[:user_wanted_url] = request.url if Config.save_user_wanted_url
35
+ session[:return_to_url] = request.url if Config.save_return_to_url
36
36
  self.send(Config.not_authenticated_action)
37
37
  end
38
38
  end
39
39
 
40
+ # Takes credentials and returns a user on successful authentication.
41
+ # Runs hooks after login or failed login.
40
42
  def login(*credentials)
41
43
  user = Config.user_class.authenticate(*credentials)
42
44
  if user
45
+ return_to_url = session[:return_to_url]
43
46
  reset_session # protect from session fixation attacks
47
+ session[:return_to_url] = return_to_url
44
48
  login_user(user)
45
49
  after_login!(user, credentials)
46
- logged_in_user
50
+ current_user
47
51
  else
48
52
  after_failed_login!(credentials)
49
53
  nil
50
54
  end
51
55
  end
52
56
 
57
+ # Resets the session and runs hooks before and after.
53
58
  def logout
54
59
  if logged_in?
55
- before_logout!(logged_in_user)
60
+ before_logout!(current_user)
56
61
  reset_session
57
62
  after_logout!
58
63
  end
59
64
  end
60
65
 
61
66
  def logged_in?
62
- !!logged_in_user
67
+ !!current_user
63
68
  end
64
69
 
65
70
  # attempts to auto-login from the sources defined (session, basic_auth, cookie, etc.)
66
71
  # returns the logged in user if found, false if not (using old restful-authentication trick, nil != false).
67
- def logged_in_user
68
- @logged_in_user ||= login_from_session || login_from_other_sources unless @logged_in_user == false
72
+ def current_user
73
+ @current_user ||= login_from_session || login_from_other_sources unless @current_user == false
69
74
  end
70
75
 
71
- def login_from_other_sources
72
- result = nil
73
- Config.login_sources.find do |source|
74
- result = send(source)
75
- end
76
- result || false
76
+ def return_or_redirect_to(url, flash_hash = {})
77
+ redirect_to(session[:return_to_url] || url, :flash => flash_hash)
77
78
  end
78
79
 
80
+ # The default action for denying non-authenticated users.
81
+ # You can override this method in your controllers.
79
82
  def not_authenticated
80
83
  redirect_to root_path
81
84
  end
82
85
 
83
86
  protected
84
87
 
88
+ # Tries all available sources (methods) until one doesn't return false.
89
+ def login_from_other_sources
90
+ result = nil
91
+ Config.login_sources.find do |source|
92
+ result = send(source)
93
+ end
94
+ result || false
95
+ end
96
+
85
97
  def login_user(user)
86
98
  session[:user_id] = user.id
87
99
  end
88
100
 
89
101
  def login_from_session
90
- @logged_in_user = (Config.user_class.find_by_id(session[:user_id]) if session[:user_id]) || false
102
+ @current_user = (Config.user_class.find_by_id(session[:user_id]) if session[:user_id]) || false
91
103
  end
92
104
 
93
105
  def after_login!(user, credentials)
@@ -112,11 +124,11 @@ module Sorcery
112
124
  class << self
113
125
  attr_accessor :submodules,
114
126
 
115
- :user_class, # what class to use as the user class.
127
+ :user_class, # what class to use as the user class. Set automatically when you call activate_sorcery! in the User class.
116
128
 
117
129
  :not_authenticated_action, # what controller action to call for non-authenticated users.
118
130
 
119
- :save_user_wanted_url, # when a non logged in user tries to enter a page that requires login, save the URL he wanted to reach,
131
+ :save_return_to_url, # when a non logged in user tries to enter a page that requires login, save the URL he wanted to reach,
120
132
  # and send him there after login.
121
133
 
122
134
  :login_sources,
@@ -138,7 +150,7 @@ module Sorcery
138
150
  :@before_logout => [],
139
151
  :@after_logout => [],
140
152
  :@after_config => [],
141
- :@save_user_wanted_url => true
153
+ :@save_return_to_url => true
142
154
  }
143
155
  end
144
156
 
@@ -13,8 +13,15 @@ module Sorcery
13
13
 
14
14
  initializer "extend Controller with sorcery" do |app|
15
15
  ActionController::Base.send(:include, Sorcery::Controller)
16
- ActionController::Base.helper_method :logged_in_user
16
+ ActionController::Base.helper_method :current_user
17
17
  end
18
-
18
+
19
+ initializer "attempt to preload user model" do |app|
20
+ begin
21
+ require Rails.root + "app/models/user.rb"
22
+ rescue LoadError
23
+ end
24
+ end
25
+
19
26
  end
20
27
  end
@@ -1,20 +1,25 @@
1
1
  module Sorcery
2
2
  module Model
3
3
  module Submodules
4
+ # This submodule keeps track of events such as login, logout, and last activity time, per user.
5
+ # It helps in estimating which users are active now in the site.
6
+ # This cannot be determined absolutely because a user might be reading a page without clicking anything for a while.
7
+
8
+ # This is the model part of the submodule, which provides configuration options.
4
9
  module ActivityLogging
5
10
  def self.included(base)
6
11
  base.extend(ClassMethods)
7
12
  base.sorcery_config.class_eval do
8
- attr_accessor :last_login_at_attribute_name, # last login attribute name.
9
- :last_logout_at_attribute_name, # last logout attribute name.
10
- :last_activity_at_attribute_name, # last activity attribute name.
13
+ attr_accessor :last_login_at_attribute_name, # last login attribute name.
14
+ :last_logout_at_attribute_name, # last logout attribute name.
15
+ :last_activity_at_attribute_name, # last activity attribute name.
11
16
  :activity_timeout # how long since last activity is the user defined logged out?
12
17
  end
13
18
 
14
19
  base.sorcery_config.instance_eval do
15
- @defaults.merge!(:@last_login_at_attribute_name => :last_login_at,
16
- :@last_logout_at_attribute_name => :last_logout_at,
17
- :@last_activity_at_attribute_name => :last_activity_at,
20
+ @defaults.merge!(:@last_login_at_attribute_name => :last_login_at,
21
+ :@last_logout_at_attribute_name => :last_logout_at,
22
+ :@last_activity_at_attribute_name => :last_activity_at,
18
23
  :@activity_timeout => 10.minutes)
19
24
  reset!
20
25
  end
@@ -22,11 +27,11 @@ module Sorcery
22
27
 
23
28
  module ClassMethods
24
29
  # get all users with last_activity within timeout
25
- def logged_in_users
30
+ def current_users
26
31
  config = sorcery_config
27
32
  where("#{config.last_activity_at_attribute_name} IS NOT NULL") \
28
33
  .where("#{config.last_logout_at_attribute_name} IS NULL OR #{config.last_activity_at_attribute_name} > #{config.last_logout_at_attribute_name}") \
29
- .where("#{config.last_activity_at_attribute_name} > ? ", config.activity_timeout.seconds.ago)
34
+ .where("#{config.last_activity_at_attribute_name} > ? ", config.activity_timeout.seconds.ago.utc.to_s(:db))
30
35
  end
31
36
  end
32
37
  end