sorcery 0.7.6 → 0.8.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 (63) hide show
  1. data/.travis.yml +3 -0
  2. data/Gemfile +9 -4
  3. data/Gemfile.lock +99 -58
  4. data/README.rdoc +33 -8
  5. data/Rakefile +6 -7
  6. data/VERSION +1 -1
  7. data/lib/generators/sorcery/install_generator.rb +9 -7
  8. data/lib/generators/sorcery/templates/initializer.rb +324 -127
  9. data/lib/generators/sorcery/templates/migration/brute_force_protection.rb +2 -0
  10. data/lib/sorcery/controller/submodules/brute_force_protection.rb +1 -2
  11. data/lib/sorcery/controller/submodules/external/protocols/oauth2.rb +2 -2
  12. data/lib/sorcery/controller/submodules/external/providers/facebook.rb +16 -4
  13. data/lib/sorcery/controller/submodules/external/providers/github.rb +6 -4
  14. data/lib/sorcery/controller/submodules/external/providers/google.rb +4 -4
  15. data/lib/sorcery/controller/submodules/external/providers/linkedin.rb +101 -0
  16. data/lib/sorcery/controller/submodules/external/providers/liveid.rb +1 -1
  17. data/lib/sorcery/controller/submodules/external/providers/vkontakte.rb +94 -0
  18. data/lib/sorcery/controller/submodules/external.rb +83 -15
  19. data/lib/sorcery/controller/submodules/http_basic_auth.rb +1 -1
  20. data/lib/sorcery/controller/submodules/remember_me.rb +11 -2
  21. data/lib/sorcery/controller.rb +14 -6
  22. data/lib/sorcery/crypto_providers/bcrypt.rb +1 -0
  23. data/lib/sorcery/model/adapters/active_record.rb +9 -4
  24. data/lib/sorcery/model/adapters/mongo_mapper.rb +16 -14
  25. data/lib/sorcery/model/adapters/mongoid.rb +12 -6
  26. data/lib/sorcery/model/submodules/brute_force_protection.rb +44 -13
  27. data/lib/sorcery/model/submodules/remember_me.rb +4 -6
  28. data/lib/sorcery/model/submodules/reset_password.rb +14 -7
  29. data/lib/sorcery/model/submodules/user_activation.rb +14 -6
  30. data/lib/sorcery/model.rb +40 -39
  31. data/lib/sorcery/railties/tasks.rake +2 -0
  32. data/lib/sorcery.rb +3 -1
  33. data/sorcery.gemspec +31 -141
  34. data/spec/Gemfile +1 -1
  35. data/spec/Gemfile.lock +20 -19
  36. data/spec/README.md +8 -8
  37. data/spec/rails3/Gemfile +1 -0
  38. data/spec/rails3/Gemfile.lock +27 -24
  39. data/spec/rails3/app/controllers/application_controller.rb +54 -0
  40. data/spec/rails3/app/mailers/sorcery_mailer.rb +7 -0
  41. data/spec/rails3/app/views/sorcery_mailer/send_unlock_token_email.text.erb +1 -0
  42. data/spec/rails3/db/migrate/brute_force_protection/20101224223626_add_brute_force_protection_to_users.rb +2 -0
  43. data/spec/rails3/spec/controller_activity_logging_spec.rb +3 -0
  44. data/spec/rails3/spec/controller_brute_force_protection_spec.rb +23 -1
  45. data/spec/rails3/spec/controller_oauth2_spec.rb +162 -24
  46. data/spec/rails3/spec/controller_oauth_spec.rb +111 -6
  47. data/spec/rails3/spec/controller_spec.rb +7 -0
  48. data/spec/rails3_mongo_mapper/Gemfile +2 -1
  49. data/spec/rails3_mongo_mapper/Gemfile.lock +37 -39
  50. data/spec/rails3_mongo_mapper/app/controllers/application_controller.rb +14 -0
  51. data/spec/rails3_mongo_mapper/spec/controller_spec.rb +7 -0
  52. data/spec/rails3_mongoid/Gemfile +1 -0
  53. data/spec/rails3_mongoid/Gemfile.lock +28 -25
  54. data/spec/rails3_mongoid/app/controllers/application_controller.rb +14 -0
  55. data/spec/rails3_mongoid/config/mongoid.yml +1 -1
  56. data/spec/rails3_mongoid/spec/controller_activity_logging_spec.rb +11 -11
  57. data/spec/rails3_mongoid/spec/controller_spec.rb +7 -0
  58. data/spec/shared_examples/controller_oauth2_shared_examples.rb +20 -1
  59. data/spec/shared_examples/controller_oauth_shared_examples.rb +18 -0
  60. data/spec/shared_examples/user_activation_shared_examples.rb +71 -41
  61. data/spec/shared_examples/user_reset_password_shared_examples.rb +76 -31
  62. data/spec/sorcery_crypto_providers_spec.rb +9 -0
  63. metadata +159 -246
@@ -34,6 +34,7 @@ module Sorcery
34
34
  :auth_path,
35
35
  :token_path,
36
36
  :site,
37
+ :scope,
37
38
  :user_info_path,
38
39
  :user_info_mapping
39
40
  attr_reader :access_token
@@ -43,6 +44,7 @@ module Sorcery
43
44
  def init
44
45
  @site = "https://github.com/"
45
46
  @user_info_path = "https://api.github.com/user"
47
+ @scope = nil
46
48
  @auth_path = "/login/oauth/authorize"
47
49
  @token_path = "/login/oauth/access_token"
48
50
  @user_info_mapping = {}
@@ -51,7 +53,7 @@ module Sorcery
51
53
  def get_user_hash
52
54
  user_hash = {}
53
55
  response = @access_token.get(@user_info_path)
54
- user_hash[:user_info] = JSON.parse(response)
56
+ user_hash[:user_info] = JSON.parse(response.body)
55
57
  user_hash[:uid] = user_hash[:user_info]['id']
56
58
  user_hash
57
59
  end
@@ -63,7 +65,7 @@ module Sorcery
63
65
  # calculates and returns the url to which the user should be redirected,
64
66
  # to get authenticated at the external provider's site.
65
67
  def login_url(params,session)
66
- self.authorize_url({:authorize_path => @auth_path})
68
+ self.authorize_url({:authorize_url => @auth_path})
67
69
  end
68
70
 
69
71
  # tries to login the user from access token
@@ -71,8 +73,8 @@ module Sorcery
71
73
  args = {}
72
74
  args.merge!({:code => params[:code]}) if params[:code]
73
75
  options = {
74
- :access_token_path => @token_path,
75
- :access_token_method => :post
76
+ :token_url => @token_path,
77
+ :token_method => :post
76
78
  }
77
79
  @access_token = self.get_access_token(args, options)
78
80
  end
@@ -44,7 +44,7 @@ module Sorcery
44
44
  def init
45
45
  @site = "https://accounts.google.com"
46
46
  @auth_url = "/o/oauth2/auth"
47
- @token_path = "/o/oauth2/token"
47
+ @token_url = "/o/oauth2/token"
48
48
  @user_info_url = "https://www.googleapis.com/oauth2/v1/userinfo"
49
49
  @scope = "https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile"
50
50
  @user_info_mapping = {}
@@ -53,7 +53,7 @@ module Sorcery
53
53
  def get_user_hash
54
54
  user_hash = {}
55
55
  response = @access_token.get(@user_info_url)
56
- user_hash[:user_info] = JSON.parse(response)
56
+ user_hash[:user_info] = JSON.parse(response.body)
57
57
  user_hash[:uid] = user_hash[:user_info]['id']
58
58
  user_hash
59
59
  end
@@ -73,8 +73,8 @@ module Sorcery
73
73
  args = {}
74
74
  args.merge!({:code => params[:code]}) if params[:code]
75
75
  options = {
76
- :access_token_path => @token_path,
77
- :access_token_method => :post
76
+ :token_url => @token_url,
77
+ :token_method => :post
78
78
  }
79
79
  @access_token = self.get_access_token(args, options)
80
80
  end
@@ -0,0 +1,101 @@
1
+ module Sorcery
2
+ module Controller
3
+ module Submodules
4
+ module External
5
+ module Providers
6
+ # This module adds support for OAuth with Linkedin.com.
7
+ # When included in the 'config.providers' option, it adds a new option, 'config.linkedin'.
8
+ # Via this new option you can configure Linkedin specific settings like your app's key and secret.
9
+ #
10
+ # config.linkedin.key = <key>
11
+ # config.linkedin.secret = <secret>
12
+ # ...
13
+ #
14
+ module Linkedin
15
+ def self.included(base)
16
+ base.module_eval do
17
+ class << self
18
+ attr_reader :linkedin
19
+
20
+ def merge_linkedin_defaults!
21
+ @defaults.merge!(:@linkedin => LinkedinClient)
22
+ end
23
+ end
24
+ merge_linkedin_defaults!
25
+ update!
26
+ end
27
+ end
28
+
29
+ module LinkedinClient
30
+ class << self
31
+ attr_accessor :key,
32
+ :secret,
33
+ :callback_url,
34
+ :site,
35
+ :authorize_path,
36
+ :request_token_path,
37
+ :access_token_path,
38
+ :user_info_path,
39
+ :user_info_mapping,
40
+ :user_info_fields,
41
+ :access_permissions
42
+ attr_reader :access_token
43
+
44
+ include Protocols::Oauth1
45
+
46
+ # Override included get_consumer method to provide authorize_path
47
+ def get_consumer
48
+ # Add access permissions to request token path
49
+ @configuration[:request_token_path] += "?scope=" + self.access_permissions.join('+') unless self.access_permissions.blank? or @configuration[:request_token_path].include? "?scope="
50
+ ::OAuth::Consumer.new(@key, @secret, @configuration)
51
+ end
52
+
53
+ def init
54
+ @configuration = {
55
+ site: "https://api.linkedin.com",
56
+ authorize_path: '/uas/oauth/authenticate',
57
+ request_token_path: '/uas/oauth/requestToken',
58
+ access_token_path: '/uas/oauth/accessToken'
59
+ }
60
+ @user_info_path = "/v1/people/~"
61
+ end
62
+
63
+ def get_user_hash
64
+ user_hash = {}
65
+ fields = self.user_info_fields.join(',')
66
+ response = @access_token.get("#{@user_info_path}:(#{fields})", 'x-li-format' => 'json')
67
+ user_hash[:user_info] = JSON.parse(response.body)
68
+ user_hash[:uid] = user_hash[:user_info]['id'].to_s
69
+ user_hash
70
+ end
71
+
72
+ def has_callback?
73
+ true
74
+ end
75
+
76
+ # calculates and returns the url to which the user should be redirected,
77
+ # to get authenticated at the external provider's site.
78
+ def login_url(params,session)
79
+ req_token = self.get_request_token
80
+ session[:request_token] = req_token.token
81
+ session[:request_token_secret] = req_token.secret
82
+ self.authorize_url({:request_token => req_token.token, :request_token_secret => req_token.secret})
83
+ end
84
+
85
+ # tries to login the user from access token
86
+ def process_callback(params,session)
87
+ args = {}
88
+ args.merge!({:oauth_verifier => params[:oauth_verifier], :request_token => session[:request_token], :request_token_secret => session[:request_token_secret]})
89
+ args.merge!({:code => params[:code]}) if params[:code]
90
+ @access_token = self.get_access_token(args)
91
+ end
92
+
93
+ end
94
+ init
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
@@ -54,7 +54,7 @@ module Sorcery
54
54
  user_hash = {}
55
55
  @access_token.token_param = "access_token"
56
56
  response = @access_token.get(@user_info_url)
57
- user_hash[:user_info] = JSON.parse(response)
57
+ user_hash[:user_info] = JSON.parse(response.body)
58
58
  user_hash[:uid] = user_hash[:user_info]['id']
59
59
  user_hash
60
60
  end
@@ -0,0 +1,94 @@
1
+ module Sorcery
2
+ module Controller
3
+ module Submodules
4
+ module External
5
+ module Providers
6
+ # This module adds support for OAuth with vkontakte.com.
7
+ # When included in the 'config.providers' option, it adds a new option, 'config.vkontakte'.
8
+ # Via this new option you can configure Vkontakte specific settings like your app's key and secret.
9
+ #
10
+ # config.vkontakte.key = <key>
11
+ # config.vkontakte.secret = <secret>
12
+ # ...
13
+ #
14
+ module Vkontakte
15
+ def self.included(base)
16
+ base.module_eval do
17
+ class << self
18
+ attr_reader :vkontakte # access to vkontakte_client.
19
+
20
+ def merge_vkontakte_defaults!
21
+ @defaults.merge!(:@vkontakte => VkontakteClient)
22
+ end
23
+ end
24
+ merge_vkontakte_defaults!
25
+ update!
26
+ end
27
+ end
28
+
29
+ module VkontakteClient
30
+ class << self
31
+ attr_accessor :key,
32
+ :secret,
33
+ :callback_url,
34
+ :auth_path,
35
+ :token_path,
36
+ :site,
37
+ :scope,
38
+ :user_info_path,
39
+ :user_info_mapping
40
+ attr_reader :access_token
41
+
42
+ include Protocols::Oauth2
43
+
44
+ def init
45
+ @site = "https://oauth.vk.com/"
46
+ @user_info_url = "https://api.vk.com/method/getUserInfo"
47
+ @scope = nil
48
+ @auth_path = "/authorize"
49
+ @token_path = "/access_token"
50
+ @user_info_mapping = {}
51
+ end
52
+
53
+ def get_user_hash
54
+ user_hash = {}
55
+ response = @access_token.get("#{@user_info_url}?access_token=#{@access_token.token}")
56
+ user_hash[:user_info] = JSON.parse(response.body)
57
+ if user_hash[:user_info]
58
+ user_hash[:user_info] = user_hash[:user_info]["response"]
59
+ end
60
+ user_hash[:uid] = user_hash[:user_info]['user_id']
61
+ user_hash
62
+ end
63
+
64
+ def has_callback?
65
+ true
66
+ end
67
+
68
+ # calculates and returns the url to which the user should be redirected,
69
+ # to get authenticated at the external provider's site.
70
+ def login_url(params,session)
71
+ self.authorize_url({:authorize_url => @auth_path})
72
+ end
73
+
74
+ # tries to login the user from access token
75
+ def process_callback(params,session)
76
+ args = {}
77
+ args.merge!({:code => params[:code]}) if params[:code]
78
+ options = {
79
+ :token_url => @token_path,
80
+ :token_method => :post
81
+ }
82
+ @access_token = self.get_access_token(args, options)
83
+ end
84
+
85
+ end
86
+ init
87
+ end
88
+
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end
@@ -10,12 +10,12 @@ module Sorcery
10
10
  class << self
11
11
  attr_reader :external_providers # external providers like twitter.
12
12
  attr_accessor :ca_file # path to ca_file. By default use a internal ca-bundle.crt.
13
-
13
+
14
14
  def merge_external_defaults!
15
15
  @defaults.merge!(:@external_providers => [],
16
16
  :@ca_file => File.join(File.expand_path(File.dirname(__FILE__)), 'external/protocols/certs/ca-bundle.crt'))
17
17
  end
18
-
18
+
19
19
  def external_providers=(providers)
20
20
  providers.each do |provider|
21
21
  include Providers.const_get(provider.to_s.split("_").map {|p| p.capitalize}.join(""))
@@ -28,26 +28,37 @@ module Sorcery
28
28
 
29
29
  module InstanceMethods
30
30
  protected
31
-
31
+
32
32
  # sends user to authenticate at the provider's website.
33
33
  # after authentication the user is redirected to the callback defined in the provider config
34
34
  def login_at(provider, args = {})
35
35
  @provider = Config.send(provider)
36
+ if @provider.callback_url.present? && @provider.callback_url[0] == '/'
37
+ uri = URI.parse(request.url.gsub(/\?.*$/,''))
38
+ uri.path = ''
39
+ uri.query = nil
40
+ uri.scheme = 'https' if(request.env['HTTP_X_FORWARDED_PROTO'] == 'https')
41
+ host = uri.to_s
42
+ @provider.callback_url = "#{host}#{@provider.callback_url}"
43
+ end
36
44
  if @provider.has_callback?
37
45
  redirect_to @provider.login_url(params,session)
38
46
  else
39
47
  #@provider.login(args)
40
48
  end
41
49
  end
42
-
50
+
43
51
  # tries to login the user from provider's callback
44
52
  def login_from(provider)
45
53
  @provider = Config.send(provider)
46
54
  @provider.process_callback(params,session)
47
55
  @user_hash = @provider.get_user_hash
48
- if user = user_class.load_from_provider(provider,@user_hash[:uid])
56
+ if user = user_class.load_from_provider(provider,@user_hash[:uid].to_s)
57
+ return_to_url = session[:return_to_url]
49
58
  reset_session
59
+ session[:return_to_url] = return_to_url
50
60
  auto_login(user)
61
+ after_login!(user)
51
62
  user
52
63
  end
53
64
  end
@@ -57,7 +68,48 @@ module Sorcery
57
68
  @provider = Config.send(provider)
58
69
  @provider.access_token
59
70
  end
60
-
71
+
72
+ # If user is logged, he can add all available providers into his account
73
+ def add_provider_to_user(provider)
74
+ provider_name = provider.to_sym
75
+ @provider = Config.send(provider_name)
76
+ @provider.process_callback(params,session)
77
+ @user_hash = @provider.get_user_hash
78
+ config = user_class.sorcery_config
79
+
80
+ # first check to see if user has a particular authentication already
81
+ unless (current_user.send(config.authentications_class.to_s.downcase.pluralize).send("find_by_#{config.provider_attribute_name}_and_#{config.provider_uid_attribute_name}", provider, @user_hash[:uid].to_s))
82
+ user = current_user.send(config.authentications_class.to_s.downcase.pluralize).build(config.provider_uid_attribute_name => @user_hash[:uid], config.provider_attribute_name => provider_name.to_s)
83
+ user.save(:validate => false)
84
+ else
85
+ user = false
86
+ end
87
+
88
+ return user
89
+ end
90
+
91
+ # Initialize new user from provider informations.
92
+ # If a provider doesn't give required informations or username/email is already taken,
93
+ # we store provider/user infos into a session and can be rendered into registration form
94
+ def create_and_validate_from(provider)
95
+ provider = provider.to_sym
96
+ @provider = Config.send(provider)
97
+ @user_hash = @provider.get_user_hash
98
+ config = user_class.sorcery_config
99
+
100
+ attrs = user_attrs(@provider.user_info_mapping, @user_hash)
101
+
102
+ user = user_class.new(attrs)
103
+ user.send(config.authentications_class.to_s.downcase.pluralize).build(config.provider_uid_attribute_name => @user_hash[:uid], config.provider_attribute_name => provider)
104
+
105
+ session[:incomplete_user] = {
106
+ :provider => {config.provider_uid_attribute_name => @user_hash[:uid], config.provider_attribute_name => provider},
107
+ :user_hash => attrs
108
+ } unless user.save
109
+
110
+ return user
111
+ end
112
+
61
113
  # this method automatically creates a new user from the data in the external user hash.
62
114
  # The mappings from user hash fields to user db fields are set at controller config.
63
115
  # If the hash field you would like to map is nested, use slashes. For example, Given a hash like:
@@ -70,30 +122,46 @@ module Sorcery
70
122
  #
71
123
  # And this will cause 'moishe' to be set as the value of :username field.
72
124
  # Note: Be careful. This method skips validations model.
125
+ # Instead you can pass a block, if the block returns false the user will not be created
126
+ #
127
+ # create_from(provider) {|user| user.some_check }
128
+ #
73
129
  def create_from(provider)
74
130
  provider = provider.to_sym
75
131
  @provider = Config.send(provider)
76
132
  @user_hash = @provider.get_user_hash
77
133
  config = user_class.sorcery_config
78
- attrs = {}
79
- @provider.user_info_mapping.each do |k,v|
80
- if (varr = v.split("/")).size > 1
81
- attribute_value = varr.inject(@user_hash[:user_info]) {|hsh,v| hsh[v] } rescue nil
82
- attribute_value.nil? ? attrs : attrs.merge!(k => attribute_value)
83
- else
84
- attrs.merge!(k => @user_hash[:user_info][v])
85
- end
86
- end
134
+
135
+ attrs = user_attrs(@provider.user_info_mapping, @user_hash)
136
+
87
137
  user_class.transaction do
88
138
  @user = user_class.new()
89
139
  attrs.each do |k,v|
90
140
  @user.send(:"#{k}=", v)
91
141
  end
142
+
143
+ if block_given?
144
+ return false unless yield @user
145
+ end
146
+
92
147
  @user.save(:validate => false)
93
148
  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]})
94
149
  end
95
150
  @user
96
151
  end
152
+
153
+ def user_attrs(user_info_mapping, user_hash)
154
+ attrs = {}
155
+ user_info_mapping.each do |k,v|
156
+ if (varr = v.split("/")).size > 1
157
+ attribute_value = varr.inject(user_hash[:user_info]) {|hash, value| hash[value]} rescue nil
158
+ attribute_value.nil? ? attrs : attrs.merge!(k => attribute_value)
159
+ else
160
+ attrs.merge!(k => user_hash[:user_info][v])
161
+ end
162
+ end
163
+ return attrs
164
+ end
97
165
  end
98
166
  end
99
167
  end
@@ -58,7 +58,7 @@ module Sorcery
58
58
  while current_controller != ActionController::Base
59
59
  result = Config.controller_to_realm_map[current_controller.controller_name]
60
60
  return result if result
61
- current_controller = self.class.superclass
61
+ current_controller = current_controller.superclass
62
62
  end
63
63
  nil
64
64
  else
@@ -8,6 +8,15 @@ module Sorcery
8
8
  module RememberMe
9
9
  def self.included(base)
10
10
  base.send(:include, InstanceMethods)
11
+ Config.module_eval do
12
+ class << self
13
+ attr_accessor :remember_me_httponly
14
+ def merge_remember_me_defaults!
15
+ @defaults.merge!(:@remember_me_httponly => true)
16
+ end
17
+ end
18
+ merge_remember_me_defaults!
19
+ end
11
20
  Config.login_sources << :login_from_cookie
12
21
  Config.after_login << :remember_me_if_asked_to
13
22
  Config.after_logout << :forget_me!
@@ -60,7 +69,7 @@ module Sorcery
60
69
  cookies.signed[:remember_me_token] = {
61
70
  :value => user.send(user.sorcery_config.remember_me_token_attribute_name),
62
71
  :expires => user.send(user.sorcery_config.remember_me_token_expires_at_attribute_name),
63
- :httponly => true,
72
+ :httponly => Config.remember_me_httponly,
64
73
  :domain => Config.cookie_domain
65
74
  }
66
75
  end
@@ -69,4 +78,4 @@ module Sorcery
69
78
  end
70
79
  end
71
80
  end
72
- end
81
+ end
@@ -21,19 +21,22 @@ module Sorcery
21
21
  # If all attempts to auto-login fail, the failure callback will be called.
22
22
  def require_login
23
23
  if !logged_in?
24
- session[:return_to_url] = request.url if Config.save_return_to_url
24
+ session[:return_to_url] = request.url if Config.save_return_to_url && request.get?
25
25
  self.send(Config.not_authenticated_action)
26
26
  end
27
27
  end
28
28
 
29
29
  # Takes credentials and returns a user on successful authentication.
30
30
  # Runs hooks after login or failed login.
31
- def login(*credentials)
31
+ def login(*credentials)
32
+ @current_user = nil
32
33
  user = user_class.authenticate(*credentials)
33
34
  if user
34
- return_to_url = session[:return_to_url]
35
+ old_session = session.dup
35
36
  reset_session # protect from session fixation attacks
36
- session[:return_to_url] = return_to_url
37
+ old_session.to_hash.each_pair do |k,v|
38
+ session[k.to_sym] = v
39
+ end
37
40
  auto_login(user)
38
41
  after_login!(user, credentials)
39
42
  current_user
@@ -71,6 +74,7 @@ module Sorcery
71
74
  # and we want to return him back to the page he originally wanted.
72
75
  def redirect_back_or_to(url, flash_hash = {})
73
76
  redirect_to(session[:return_to_url] || url, :flash => flash_hash)
77
+ session[:return_to_url] = nil
74
78
  end
75
79
 
76
80
  # The default action for denying non-authenticated users.
@@ -108,10 +112,14 @@ module Sorcery
108
112
  end
109
113
 
110
114
  def login_from_session
111
- @current_user = (user_class.find_by_id(session[:user_id]) if session[:user_id]) || false
115
+ @current_user = (user_class.find(session[:user_id]) if session[:user_id]) || false
116
+ rescue => exception
117
+ return false if defined?(Mongoid) and exception.is_a?(Mongoid::Errors::DocumentNotFound)
118
+ return false if defined?(ActiveRecord) and exception.is_a?(ActiveRecord::RecordNotFound)
119
+ raise exception
112
120
  end
113
121
 
114
- def after_login!(user, credentials)
122
+ def after_login!(user, credentials = [])
115
123
  Config.after_login.each {|c| self.send(c, user, credentials)}
116
124
  end
117
125
 
@@ -48,6 +48,7 @@ module Sorcery
48
48
  @cost ||= 10
49
49
  end
50
50
  attr_writer :cost
51
+ alias :stretches :cost
51
52
  alias :stretches= :cost=
52
53
 
53
54
  # Creates a BCrypt hash for the password passed.
@@ -8,11 +8,16 @@ module Sorcery
8
8
  end
9
9
 
10
10
  module InstanceMethods
11
- def update_single_attribute(name, value)
12
- self.send(:"#{name}=", value)
13
-
11
+ def update_many_attributes(attrs)
12
+ attrs.each do |name, value|
13
+ self.send(:"#{name}=", value)
14
+ end
14
15
  primary_key = self.class.primary_key
15
- self.class.where(:"#{primary_key}" => self.send(:"#{primary_key}")).update_all(name => value)
16
+ self.class.where(:"#{primary_key}" => self.send(:"#{primary_key}")).update_all(attrs)
17
+ end
18
+
19
+ def update_single_attribute(name, value)
20
+ update_many_attributes(name => value)
16
21
  end
17
22
  end
18
23
 
@@ -3,19 +3,21 @@ module Sorcery
3
3
  module Adapters
4
4
  module MongoMapper
5
5
  extend ActiveSupport::Concern
6
-
6
+
7
7
  included do
8
8
  include Sorcery::Model
9
9
  end
10
-
11
- module InstanceMethods
12
- def increment(attr)
13
- self.inc(attr,1)
14
- end
15
-
16
- def save!(options = {})
17
- save(options)
18
- end
10
+
11
+ def increment(attr)
12
+ self.class.increment(id, attr => 1)
13
+ end
14
+
15
+ def save!(options = {})
16
+ save(options)
17
+ end
18
+
19
+ def update_many_attributes(attrs)
20
+ update_attributes(attrs)
19
21
  end
20
22
 
21
23
  module ClassMethods
@@ -31,19 +33,19 @@ module Sorcery
31
33
  end
32
34
  @user
33
35
  end
34
-
36
+
35
37
  def find_by_id(id)
36
38
  find(id)
37
39
  end
38
-
40
+
39
41
  def find_by_activation_token(token)
40
42
  where(sorcery_config.activation_token_attribute_name => token).first
41
43
  end
42
-
44
+
43
45
  def transaction(&blk)
44
46
  tap(&blk)
45
47
  end
46
-
48
+
47
49
  def find_by_sorcery_token(token_attr_name, token)
48
50
  where(token_attr_name => token).first
49
51
  end
@@ -11,11 +11,17 @@ module Sorcery
11
11
  def increment(attr)
12
12
  self.inc(attr,1)
13
13
  end
14
-
14
+
15
+ def update_many_attributes(attrs)
16
+ attrs.each do |name, value|
17
+ attrs[name] = value.utc if value.is_a?(ActiveSupport::TimeWithZone)
18
+ self.send(:"#{name}=", value)
19
+ end
20
+ self.class.where(:_id => self.id).update_all(attrs)
21
+ end
22
+
15
23
  def update_single_attribute(name, value)
16
- value = value.utc if value.is_a?(ActiveSupport::TimeWithZone)
17
- self.send(:"#{name}=", value)
18
- self.class.where(:_id => self.id).update_all(name => value)
24
+ update_many_attributes(name => value)
19
25
  end
20
26
  end
21
27
 
@@ -72,8 +78,8 @@ module Sorcery
72
78
  def get_current_users
73
79
  config = sorcery_config
74
80
  where(config.last_activity_at_attribute_name.ne => nil) \
75
- .any_of({config.last_logout_at_attribute_name => nil},{config.last_activity_at_attribute_name.gt => config.last_logout_at_attribute_name}) \
76
- .and(config.last_activity_at_attribute_name.gt => config.activity_timeout.seconds.ago.utc.to_s(:db)).order_by([:_id,:asc])
81
+ .where("this.#{config.last_logout_at_attribute_name} == null || this.#{config.last_activity_at_attribute_name} > this.#{config.last_logout_at_attribute_name}") \
82
+ .where(config.last_activity_at_attribute_name.gt => config.activity_timeout.seconds.ago.utc).order_by([:_id,:asc])
77
83
  end
78
84
  end
79
85
  end