sorcery 0.7.12 → 0.7.13
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of sorcery might be problematic. Click here for more details.
- data/Gemfile +5 -4
- data/Gemfile.lock +8 -7
- data/README.rdoc +5 -1
- data/VERSION +1 -1
- data/lib/generators/sorcery/templates/initializer.rb +5 -13
- data/lib/sorcery/controller.rb +6 -3
- data/lib/sorcery/controller/submodules/brute_force_protection.rb +1 -2
- data/lib/sorcery/controller/submodules/external.rb +80 -14
- data/lib/sorcery/crypto_providers/bcrypt.rb +1 -0
- data/lib/sorcery/model/adapters/active_record.rb +9 -4
- data/lib/sorcery/model/adapters/mongo_mapper.rb +16 -14
- data/lib/sorcery/model/adapters/mongoid.rb +10 -4
- data/lib/sorcery/model/submodules/brute_force_protection.rb +8 -8
- data/lib/sorcery/model/submodules/remember_me.rb +4 -6
- data/lib/sorcery/model/submodules/reset_password.rb +4 -4
- data/lib/sorcery/railties/tasks.rake +2 -0
- data/sorcery.gemspec +9 -6
- data/spec/Gemfile +1 -1
- data/spec/Gemfile.lock +7 -10
- data/spec/rails3/Gemfile.lock +7 -9
- data/spec/rails3/app/controllers/application_controller.rb +14 -0
- data/spec/rails3/spec/controller_activity_logging_spec.rb +3 -0
- data/spec/rails3/spec/controller_oauth2_spec.rb +125 -21
- data/spec/rails3/spec/controller_oauth_spec.rb +102 -6
- data/spec/rails3/spec/controller_spec.rb +7 -0
- data/spec/rails3_mongo_mapper/Gemfile.lock +7 -10
- data/spec/rails3_mongo_mapper/app/controllers/application_controller.rb +14 -0
- data/spec/rails3_mongo_mapper/spec/controller_spec.rb +7 -0
- data/spec/rails3_mongoid/Gemfile.lock +7 -10
- data/spec/rails3_mongoid/app/controllers/application_controller.rb +14 -0
- data/spec/rails3_mongoid/spec/controller_spec.rb +7 -0
- data/spec/shared_examples/controller_oauth2_shared_examples.rb +20 -1
- data/spec/shared_examples/controller_oauth_shared_examples.rb +18 -0
- data/spec/sorcery_crypto_providers_spec.rb +9 -0
- metadata +139 -123
data/Gemfile
CHANGED
@@ -3,12 +3,13 @@ source :rubygems
|
|
3
3
|
# Example:
|
4
4
|
# gem "activesupport", ">= 2.3.5"
|
5
5
|
gem 'oauth', "~> 0.4.4"
|
6
|
-
gem 'oauth2', "~> 0.
|
6
|
+
gem 'oauth2', "~> 0.6.0"
|
7
7
|
gem 'bcrypt-ruby', "~> 3.0.0"
|
8
8
|
|
9
9
|
# Add dependencies to develop your gem here.
|
10
10
|
# Include everything needed to run rake, tests, features, etc.
|
11
11
|
group :development do
|
12
|
+
gem 'abstract', '>= 1.0.0'
|
12
13
|
gem "rails", ">= 3.0.0"
|
13
14
|
gem 'json', ">= 1.5.1"
|
14
15
|
gem "rspec", "~> 2.5.0"
|
@@ -20,7 +21,7 @@ group :development do
|
|
20
21
|
gem "jeweler", "~> 1.8.3"
|
21
22
|
gem 'simplecov', '>= 0.3.8', :require => false # Will install simplecov-html as a dependency
|
22
23
|
gem 'timecop'
|
23
|
-
|
24
|
-
|
25
|
-
|
24
|
+
gem 'capybara'
|
25
|
+
gem 'mongo_mapper'
|
26
|
+
gem 'mongoid', "~> 2.4.4"
|
26
27
|
end
|
data/Gemfile.lock
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
GEM
|
2
2
|
remote: http://rubygems.org/
|
3
3
|
specs:
|
4
|
+
abstract (1.0.0)
|
4
5
|
actionmailer (3.2.2)
|
5
6
|
actionpack (= 3.2.2)
|
6
7
|
mail (~> 2.4.0)
|
@@ -28,7 +29,6 @@ GEM
|
|
28
29
|
activesupport (3.2.2)
|
29
30
|
i18n (~> 0.6)
|
30
31
|
multi_json (~> 1.0)
|
31
|
-
addressable (2.2.7)
|
32
32
|
archive-tar-minitar (0.5.2)
|
33
33
|
arel (3.0.2)
|
34
34
|
bcrypt-ruby (3.0.1)
|
@@ -46,13 +46,12 @@ GEM
|
|
46
46
|
columnize (0.3.6)
|
47
47
|
diff-lcs (1.1.3)
|
48
48
|
erubis (2.7.0)
|
49
|
-
faraday (0.
|
50
|
-
addressable (~> 2.2)
|
49
|
+
faraday (0.8.1)
|
51
50
|
multipart-post (~> 1.1)
|
52
|
-
rack (~> 1.1)
|
53
51
|
ffi (1.0.11)
|
54
52
|
git (1.2.5)
|
55
53
|
hike (1.2.1)
|
54
|
+
httpauth (0.1)
|
56
55
|
i18n (0.6.0)
|
57
56
|
jeweler (1.8.3)
|
58
57
|
bundler (~> 1.0)
|
@@ -82,9 +81,10 @@ GEM
|
|
82
81
|
multipart-post (1.1.5)
|
83
82
|
nokogiri (1.5.2)
|
84
83
|
oauth (0.4.5)
|
85
|
-
oauth2 (0.
|
84
|
+
oauth2 (0.6.1)
|
86
85
|
faraday (~> 0.7)
|
87
|
-
|
86
|
+
httpauth (~> 0.1)
|
87
|
+
multi_json (~> 1.3)
|
88
88
|
plucky (0.4.4)
|
89
89
|
mongo (~> 1.5)
|
90
90
|
polyglot (0.3.3)
|
@@ -168,6 +168,7 @@ PLATFORMS
|
|
168
168
|
ruby
|
169
169
|
|
170
170
|
DEPENDENCIES
|
171
|
+
abstract (>= 1.0.0)
|
171
172
|
bcrypt-ruby (~> 3.0.0)
|
172
173
|
bundler (>= 1.1.0)
|
173
174
|
capybara
|
@@ -176,7 +177,7 @@ DEPENDENCIES
|
|
176
177
|
mongo_mapper
|
177
178
|
mongoid (~> 2.4.4)
|
178
179
|
oauth (~> 0.4.4)
|
179
|
-
oauth2 (~> 0.
|
180
|
+
oauth2 (~> 0.6.0)
|
180
181
|
rails (>= 3.0.0)
|
181
182
|
rspec (~> 2.5.0)
|
182
183
|
rspec-rails (~> 2.5.0)
|
data/README.rdoc
CHANGED
@@ -29,7 +29,7 @@ Railscast: http://railscasts.com/episodes/283-authentication-with-sorcery
|
|
29
29
|
|
30
30
|
Example Rails 3 app using sorcery: https://github.com/NoamB/sorcery-example-app
|
31
31
|
|
32
|
-
Documentation: http://rubydoc.info/gems/sorcery/0.7.
|
32
|
+
Documentation: http://rubydoc.info/gems/sorcery/0.7.13/frames
|
33
33
|
|
34
34
|
Check out the tutorials in the github wiki!
|
35
35
|
|
@@ -138,6 +138,10 @@ the `config/initializers/sorcery.rb` file. After that all emails will be sent as
|
|
138
138
|
end
|
139
139
|
|
140
140
|
|
141
|
+
== Single Table Inheritance (STI) Support
|
142
|
+
STI is supported via a single setting in config/initializers/sorcery.rb.
|
143
|
+
|
144
|
+
|
141
145
|
== Full Features List by module:
|
142
146
|
|
143
147
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.7.
|
1
|
+
0.7.13
|
@@ -218,14 +218,14 @@ Rails.application.config.sorcery.configure do |config|
|
|
218
218
|
# Default: `nil`
|
219
219
|
#
|
220
220
|
# user.user_activation_mailer =
|
221
|
-
|
222
|
-
|
221
|
+
|
222
|
+
|
223
223
|
# when true sorcery will not automatically
|
224
224
|
# email activation details and allow you to
|
225
225
|
# manually handle how and when email is sent.
|
226
226
|
# Default: `false`
|
227
227
|
#
|
228
|
-
# user.activation_mailer_disabled =
|
228
|
+
# user.activation_mailer_disabled =
|
229
229
|
|
230
230
|
|
231
231
|
# activation needed email method on your mailer class.
|
@@ -282,17 +282,9 @@ Rails.application.config.sorcery.configure do |config|
|
|
282
282
|
# manually handle how and when email is sent
|
283
283
|
# Default: `false`
|
284
284
|
#
|
285
|
-
# user.reset_password_mailer_disabled =
|
285
|
+
# user.reset_password_mailer_disabled =
|
286
286
|
|
287
|
-
|
288
|
-
# reset password email
|
289
|
-
# method on your mailer
|
290
|
-
# class.
|
291
|
-
# Default: `:reset_password_email`
|
292
|
-
#
|
293
|
-
# user.reset_password_email_method_name =
|
294
287
|
|
295
|
-
|
296
288
|
# how many seconds before the reset request expires. nil for never expires.
|
297
289
|
# Default: `nil`
|
298
290
|
#
|
@@ -347,7 +339,7 @@ Rails.application.config.sorcery.configure do |config|
|
|
347
339
|
|
348
340
|
# Unlock token mailer class
|
349
341
|
# Default: `nil`
|
350
|
-
#
|
342
|
+
#
|
351
343
|
# user.unlock_token_mailer = UserMailer
|
352
344
|
|
353
345
|
# -- activity logging --
|
data/lib/sorcery/controller.rb
CHANGED
@@ -21,7 +21,7 @@ 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
|
@@ -110,10 +110,13 @@ module Sorcery
|
|
110
110
|
end
|
111
111
|
|
112
112
|
def login_from_session
|
113
|
-
@current_user = (user_class.
|
113
|
+
@current_user = (user_class.find(session[:user_id]) if session[:user_id]) || false
|
114
|
+
rescue => exception
|
115
|
+
return false if defined?(Mongoid) and exception.is_a?(Mongoid::Errors::DocumentNotFound)
|
116
|
+
raise exception
|
114
117
|
end
|
115
118
|
|
116
|
-
def after_login!(user, credentials)
|
119
|
+
def after_login!(user, credentials = [])
|
117
120
|
Config.after_login.each {|c| self.send(c, user, credentials)}
|
118
121
|
end
|
119
122
|
|
@@ -29,8 +29,7 @@ module Sorcery
|
|
29
29
|
# Resets the failed logins counter.
|
30
30
|
# Runs as a hook after a successful login.
|
31
31
|
def reset_failed_logins_count!(user, credentials)
|
32
|
-
user.
|
33
|
-
user.save!(:validate => false)
|
32
|
+
user.update_many_attributes(user_class.sorcery_config.failed_logins_count_attribute_name => 0)
|
34
33
|
end
|
35
34
|
end
|
36
35
|
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,18 +28,26 @@ 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)
|
@@ -50,6 +58,7 @@ module Sorcery
|
|
50
58
|
reset_session
|
51
59
|
session[:return_to_url] = return_to_url
|
52
60
|
auto_login(user)
|
61
|
+
after_login!(user)
|
53
62
|
user
|
54
63
|
end
|
55
64
|
end
|
@@ -59,7 +68,48 @@ module Sorcery
|
|
59
68
|
@provider = Config.send(provider)
|
60
69
|
@provider.access_token
|
61
70
|
end
|
62
|
-
|
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]))
|
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
|
+
|
63
113
|
# this method automatically creates a new user from the data in the external user hash.
|
64
114
|
# The mappings from user hash fields to user db fields are set at controller config.
|
65
115
|
# If the hash field you would like to map is nested, use slashes. For example, Given a hash like:
|
@@ -72,30 +122,46 @@ module Sorcery
|
|
72
122
|
#
|
73
123
|
# And this will cause 'moishe' to be set as the value of :username field.
|
74
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
|
+
#
|
75
129
|
def create_from(provider)
|
76
130
|
provider = provider.to_sym
|
77
131
|
@provider = Config.send(provider)
|
78
132
|
@user_hash = @provider.get_user_hash
|
79
133
|
config = user_class.sorcery_config
|
80
|
-
|
81
|
-
@provider.user_info_mapping
|
82
|
-
|
83
|
-
attribute_value = varr.inject(@user_hash[:user_info]) {|hsh,v| hsh[v] } rescue nil
|
84
|
-
attribute_value.nil? ? attrs : attrs.merge!(k => attribute_value)
|
85
|
-
else
|
86
|
-
attrs.merge!(k => @user_hash[:user_info][v])
|
87
|
-
end
|
88
|
-
end
|
134
|
+
|
135
|
+
attrs = user_attrs(@provider.user_info_mapping, @user_hash)
|
136
|
+
|
89
137
|
user_class.transaction do
|
90
138
|
@user = user_class.new()
|
91
139
|
attrs.each do |k,v|
|
92
140
|
@user.send(:"#{k}=", v)
|
93
141
|
end
|
142
|
+
|
143
|
+
if block_given?
|
144
|
+
return false unless yield @user
|
145
|
+
end
|
146
|
+
|
94
147
|
@user.save(:validate => false)
|
95
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]})
|
96
149
|
end
|
97
150
|
@user
|
98
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
|
99
165
|
end
|
100
166
|
end
|
101
167
|
end
|
@@ -8,11 +8,16 @@ module Sorcery
|
|
8
8
|
end
|
9
9
|
|
10
10
|
module InstanceMethods
|
11
|
-
def
|
12
|
-
|
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(
|
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
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
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
|
-
|
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
|
|