sorcery 0.8.6 → 0.9.1

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 (126) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -1
  3. data/.travis.yml +75 -14
  4. data/CHANGELOG.md +32 -1
  5. data/Gemfile +1 -0
  6. data/README.md +138 -86
  7. data/gemfiles/active_record-rails40.gemfile +7 -0
  8. data/gemfiles/active_record-rails41.gemfile +3 -2
  9. data/gemfiles/mongo_mapper-rails40.gemfile +9 -0
  10. data/gemfiles/mongo_mapper-rails41.gemfile +2 -1
  11. data/gemfiles/mongoid-rails40.gemfile +9 -0
  12. data/gemfiles/mongoid-rails41.gemfile +3 -5
  13. data/gemfiles/mongoid3-rails32.gemfile +9 -0
  14. data/lib/generators/sorcery/USAGE +1 -1
  15. data/lib/generators/sorcery/install_generator.rb +19 -5
  16. data/lib/generators/sorcery/templates/initializer.rb +36 -10
  17. data/lib/generators/sorcery/templates/migration/brute_force_protection.rb +3 -1
  18. data/lib/generators/sorcery/templates/migration/core.rb +2 -2
  19. data/lib/generators/sorcery/templates/migration/external.rb +3 -1
  20. data/lib/sorcery/adapters/active_record_adapter.rb +120 -0
  21. data/lib/sorcery/adapters/base_adapter.rb +30 -0
  22. data/lib/sorcery/adapters/data_mapper_adapter.rb +176 -0
  23. data/lib/sorcery/adapters/mongo_mapper_adapter.rb +110 -0
  24. data/lib/sorcery/adapters/mongoid_adapter.rb +97 -0
  25. data/lib/sorcery/controller/config.rb +65 -0
  26. data/lib/sorcery/controller/submodules/activity_logging.rb +16 -21
  27. data/lib/sorcery/controller/submodules/brute_force_protection.rb +6 -6
  28. data/lib/sorcery/controller/submodules/external.rb +9 -29
  29. data/lib/sorcery/controller/submodules/remember_me.rb +4 -4
  30. data/lib/sorcery/controller/submodules/session_timeout.rb +10 -6
  31. data/lib/sorcery/controller.rb +5 -64
  32. data/lib/sorcery/model/config.rb +96 -0
  33. data/lib/sorcery/model/submodules/activity_logging.rb +29 -36
  34. data/lib/sorcery/model/submodules/brute_force_protection.rb +21 -37
  35. data/lib/sorcery/model/submodules/external.rb +53 -9
  36. data/lib/sorcery/model/submodules/remember_me.rb +12 -31
  37. data/lib/sorcery/model/submodules/reset_password.rb +21 -39
  38. data/lib/sorcery/model/submodules/user_activation.rb +21 -63
  39. data/lib/sorcery/model/temporary_token.rb +4 -4
  40. data/lib/sorcery/model.rb +47 -175
  41. data/lib/sorcery/providers/base.rb +11 -0
  42. data/lib/sorcery/providers/facebook.rb +16 -5
  43. data/lib/sorcery/providers/github.rb +11 -2
  44. data/lib/sorcery/providers/google.rb +1 -1
  45. data/lib/sorcery/providers/heroku.rb +57 -0
  46. data/lib/sorcery/providers/jira.rb +77 -0
  47. data/lib/sorcery/providers/linkedin.rb +1 -1
  48. data/lib/sorcery/providers/liveid.rb +1 -1
  49. data/lib/sorcery/providers/salesforce.rb +50 -0
  50. data/lib/sorcery/providers/twitter.rb +1 -1
  51. data/lib/sorcery/providers/vk.rb +6 -4
  52. data/lib/sorcery/providers/xing.rb +2 -2
  53. data/lib/sorcery/test_helpers/internal.rb +7 -3
  54. data/lib/sorcery/test_helpers/rails/controller.rb +5 -1
  55. data/lib/sorcery/version.rb +3 -0
  56. data/lib/sorcery.rb +75 -43
  57. data/sorcery.gemspec +6 -2
  58. data/spec/active_record/user_activity_logging_spec.rb +9 -0
  59. data/spec/controllers/controller_activity_logging_spec.rb +124 -0
  60. data/spec/controllers/controller_brute_force_protection_spec.rb +43 -0
  61. data/spec/{active_record → controllers}/controller_http_basic_auth_spec.rb +14 -11
  62. data/spec/{active_record → controllers}/controller_oauth2_spec.rb +151 -64
  63. data/spec/{active_record → controllers}/controller_oauth_spec.rb +94 -70
  64. data/spec/{active_record → controllers}/controller_remember_me_spec.rb +32 -12
  65. data/spec/{active_record → controllers}/controller_session_timeout_spec.rb +15 -5
  66. data/spec/{shared_examples/controller_shared_examples.rb → controllers/controller_spec.rb} +34 -19
  67. data/spec/{datamapper → data_mapper}/user_activation_spec.rb +1 -1
  68. data/spec/data_mapper/user_activity_logging_spec.rb +14 -0
  69. data/spec/{datamapper → data_mapper}/user_brute_force_protection_spec.rb +1 -1
  70. data/spec/{datamapper → data_mapper}/user_oauth_spec.rb +1 -1
  71. data/spec/{datamapper → data_mapper}/user_remember_me_spec.rb +1 -1
  72. data/spec/{datamapper → data_mapper}/user_reset_password_spec.rb +1 -1
  73. data/spec/{datamapper → data_mapper}/user_spec.rb +1 -1
  74. data/spec/mongoid/user_spec.rb +13 -0
  75. data/spec/orm/active_record.rb +12 -0
  76. data/spec/orm/{datamapper.rb → data_mapper.rb} +16 -2
  77. data/spec/orm/mongo_mapper.rb +0 -1
  78. data/spec/orm/mongoid.rb +4 -0
  79. data/spec/rails_app/app/controllers/sorcery_controller.rb +62 -1
  80. data/spec/rails_app/app/mongo_mapper/user.rb +2 -0
  81. data/spec/rails_app/config/routes.rb +9 -0
  82. data/spec/rails_app/db/migrate/core/20101224223620_create_users.rb +2 -2
  83. data/spec/shared_examples/user_activation_shared_examples.rb +7 -7
  84. data/spec/shared_examples/user_activity_logging_shared_examples.rb +73 -5
  85. data/spec/shared_examples/user_brute_force_protection_shared_examples.rb +127 -9
  86. data/spec/shared_examples/user_oauth_shared_examples.rb +3 -6
  87. data/spec/shared_examples/user_remember_me_shared_examples.rb +6 -3
  88. data/spec/shared_examples/user_reset_password_shared_examples.rb +10 -10
  89. data/spec/shared_examples/user_shared_examples.rb +146 -46
  90. data/spec/spec_helper.rb +7 -22
  91. metadata +36 -57
  92. data/Gemfile.rails4 +0 -22
  93. data/VERSION +0 -1
  94. data/lib/sorcery/model/adapters/active_record.rb +0 -54
  95. data/lib/sorcery/model/adapters/datamapper.rb +0 -123
  96. data/lib/sorcery/model/adapters/mongo_mapper.rb +0 -60
  97. data/lib/sorcery/model/adapters/mongoid.rb +0 -88
  98. data/lib/sorcery/test_helpers/rails.rb +0 -7
  99. data/spec/active_record/controller_activity_logging_spec.rb +0 -29
  100. data/spec/active_record/controller_brute_force_protection_spec.rb +0 -158
  101. data/spec/active_record/controller_spec.rb +0 -8
  102. data/spec/active_record/integration_spec.rb +0 -23
  103. data/spec/datamapper/controller_activity_logging_spec.rb +0 -17
  104. data/spec/datamapper/controller_spec.rb +0 -8
  105. data/spec/datamapper/user_activity_logging_spec.rb +0 -9
  106. data/spec/mongo_mapper/controller_spec.rb +0 -8
  107. data/spec/mongoid/controller_activity_logging_spec.rb +0 -16
  108. data/spec/mongoid/controller_spec.rb +0 -8
  109. data/spec/rails_app/public/404.html +0 -26
  110. data/spec/rails_app/public/422.html +0 -26
  111. data/spec/rails_app/public/500.html +0 -26
  112. data/spec/rails_app/public/favicon.ico +0 -0
  113. data/spec/rails_app/public/images/rails.png +0 -0
  114. data/spec/rails_app/public/javascripts/application.js +0 -2
  115. data/spec/rails_app/public/javascripts/controls.js +0 -965
  116. data/spec/rails_app/public/javascripts/dragdrop.js +0 -974
  117. data/spec/rails_app/public/javascripts/effects.js +0 -1123
  118. data/spec/rails_app/public/javascripts/prototype.js +0 -6001
  119. data/spec/rails_app/public/javascripts/rails.js +0 -175
  120. data/spec/rails_app/public/robots.txt +0 -5
  121. data/spec/rails_app/public/stylesheets/.gitkeep +0 -0
  122. data/spec/shared_examples/controller_activity_logging_shared_examples.rb +0 -125
  123. data/spec/shared_examples/controller_oauth2_shared_examples.rb +0 -52
  124. data/spec/shared_examples/controller_oauth_shared_examples.rb +0 -62
  125. /data/spec/rails_app/app/{datamapper → data_mapper}/authentication.rb +0 -0
  126. /data/spec/rails_app/app/{datamapper → data_mapper}/user.rb +0 -0
data/lib/sorcery/model.rb CHANGED
@@ -19,17 +19,28 @@ module Sorcery
19
19
  # This runs the options block set in the initializer on the model class.
20
20
  ::Sorcery::Controller::Config.user_config.tap{|blk| blk.call(@sorcery_config) if blk}
21
21
 
22
- init_mongoid_support! if defined?(Mongoid) and self.ancestors.include?(Mongoid::Document)
23
- init_mongo_mapper_support! if defined?(MongoMapper) and self.ancestors.include?(MongoMapper::Document)
24
- init_datamapper_support! if defined?(DataMapper) and self.ancestors.include?(DataMapper::Resource)
25
-
22
+ define_base_fields
26
23
  init_orm_hooks!
27
24
 
28
25
  @sorcery_config.after_config << :add_config_inheritance if @sorcery_config.subclasses_inherit_config
29
26
  @sorcery_config.after_config.each { |c| send(c) }
30
27
  end
31
28
 
32
- protected
29
+ private
30
+
31
+ def define_base_fields
32
+ self.class_eval do
33
+ sorcery_config.username_attribute_names.each do |username|
34
+ sorcery_adapter.define_field username, String, length: 255
35
+ end
36
+ unless sorcery_config.username_attribute_names.include?(sorcery_config.email_attribute_name)
37
+ sorcery_adapter.define_field sorcery_config.email_attribute_name, String, length: 255
38
+ end
39
+ sorcery_adapter.define_field sorcery_config.crypted_password_attribute_name, String, length: 255
40
+ sorcery_adapter.define_field sorcery_config.salt_attribute_name, String, length: 255
41
+ end
42
+
43
+ end
33
44
 
34
45
  # includes required submodules into the model class,
35
46
  # which usually is called User.
@@ -47,77 +58,17 @@ module Sorcery
47
58
  end
48
59
  end
49
60
 
50
- # defines mongoid fields on the model class,
51
- # using 1.8.x hash syntax to perserve compatibility.
52
- def init_mongoid_support!
53
- self.class_eval do
54
- sorcery_config.username_attribute_names.each do |username|
55
- field username, :type => String
56
- end
57
- field sorcery_config.email_attribute_name, :type => String unless sorcery_config.username_attribute_names.include?(sorcery_config.email_attribute_name)
58
- field sorcery_config.crypted_password_attribute_name, :type => String
59
- field sorcery_config.salt_attribute_name, :type => String
60
- end
61
- end
62
-
63
- # defines mongo_mapper fields on the model class,
64
- def init_mongo_mapper_support!
65
- self.class_eval do
66
- sorcery_config.username_attribute_names.each do |username|
67
- key username, String
68
- end
69
- key sorcery_config.email_attribute_name, String unless sorcery_config.username_attribute_names.include?(sorcery_config.email_attribute_name)
70
- key sorcery_config.crypted_password_attribute_name, String
71
- key sorcery_config.salt_attribute_name, String
72
- end
73
- end
74
-
75
- # defines datamapper fields on the model class
76
- def init_datamapper_support!
77
- self.class_eval do
78
- sorcery_config.username_attribute_names.each do |username|
79
- property username, String, :length => 255
80
- end
81
- unless sorcery_config.username_attribute_names.include?(sorcery_config.email_attribute_name)
82
- property sorcery_config.email_attribute_name, String, :length => 255
83
- end
84
- property sorcery_config.crypted_password_attribute_name, String, :length => 255
85
- property sorcery_config.salt_attribute_name, String, :length => 255
86
- end
87
- end
88
-
89
61
  # add virtual password accessor and ORM callbacks.
90
62
  def init_orm_hooks!
91
- if defined?(DataMapper) and self.ancestors.include?(DataMapper::Resource)
92
- init_datamapper_hooks!
93
- return
94
- end
95
- self.class_eval do
96
- attr_accessor @sorcery_config.password_attribute_name
97
- #attr_protected @sorcery_config.crypted_password_attribute_name, @sorcery_config.salt_attribute_name
98
- before_save :encrypt_password, :if => Proc.new { |record|
99
- record.send(sorcery_config.password_attribute_name).present?
100
- }
101
- after_save :clear_virtual_password, :if => Proc.new { |record|
102
- record.send(sorcery_config.password_attribute_name).present?
103
- }
104
- end
105
- end
63
+ sorcery_adapter.define_callback :before, :validation, :encrypt_password, if: Proc.new {|record|
64
+ record.send(sorcery_config.password_attribute_name).present?
65
+ }
106
66
 
107
- def init_datamapper_hooks!
108
- self.class_eval do
109
- attr_accessor @sorcery_config.password_attribute_name
110
- before :valid? do
111
- if self.send(sorcery_config.password_attribute_name).present?
112
- encrypt_password
113
- end
114
- end
115
- after :save do
116
- if self.send(sorcery_config.password_attribute_name).present?
117
- clear_virtual_password
118
- end
119
- end
120
- end
67
+ sorcery_adapter.define_callback :after, :save, :clear_virtual_password, if: Proc.new {|record|
68
+ record.send(sorcery_config.password_attribute_name).present?
69
+ }
70
+
71
+ attr_accessor sorcery_config.password_attribute_name
121
72
  end
122
73
 
123
74
  module ClassMethods
@@ -139,12 +90,15 @@ module Sorcery
139
90
  credentials[0].downcase!
140
91
  end
141
92
 
142
- user = find_by_credentials(credentials)
93
+ user = sorcery_adapter.find_by_credentials(credentials)
94
+
95
+ if user.respond_to?(:active_for_authentication?)
96
+ return nil if !user.active_for_authentication?
97
+ end
143
98
 
144
99
  set_encryption_attributes
145
100
 
146
- _salt = user.send(@sorcery_config.salt_attribute_name) if user && !@sorcery_config.salt_attribute_name.nil? && !@sorcery_config.encryption_provider.nil?
147
- user if user && @sorcery_config.before_authenticate.all? {|c| user.send(c)} && credentials_match?(user.send(@sorcery_config.crypted_password_attribute_name),credentials[1],_salt)
101
+ user if user && @sorcery_config.before_authenticate.all? {|c| user.send(c)} && user.valid_password?(credentials[1])
148
102
  end
149
103
 
150
104
  # encrypt tokens using current encryption_provider.
@@ -163,13 +117,7 @@ module Sorcery
163
117
  @sorcery_config.encryption_provider.stretches = @sorcery_config.stretches if @sorcery_config.encryption_provider.respond_to?(:stretches) && @sorcery_config.stretches
164
118
  @sorcery_config.encryption_provider.join_token = @sorcery_config.salt_join_token if @sorcery_config.encryption_provider.respond_to?(:join_token) && @sorcery_config.salt_join_token
165
119
  end
166
-
167
- # Calls the configured encryption provider to compare the supplied password with the encrypted one.
168
- def credentials_match?(crypted, *tokens)
169
- return crypted == tokens.join if @sorcery_config.encryption_provider.nil?
170
- @sorcery_config.encryption_provider.matches?(crypted, *tokens)
171
- end
172
-
120
+
173
121
  def add_config_inheritance
174
122
  self.class_eval do
175
123
  def self.inherited(subclass)
@@ -198,6 +146,16 @@ module Sorcery
198
146
  send(sorcery_config.crypted_password_attribute_name).nil?
199
147
  end
200
148
 
149
+ # Calls the configured encryption provider to compare the supplied password with the encrypted one.
150
+ def valid_password?(pass)
151
+ _crypted = self.send(sorcery_config.crypted_password_attribute_name)
152
+ return _crypted == pass if sorcery_config.encryption_provider.nil?
153
+
154
+ _salt = self.send(sorcery_config.salt_attribute_name) unless sorcery_config.salt_attribute_name.nil?
155
+
156
+ sorcery_config.encryption_provider.matches?(_crypted, pass, _salt)
157
+ end
158
+
201
159
  protected
202
160
 
203
161
  # creates new salt and saves it.
@@ -223,100 +181,14 @@ module Sorcery
223
181
  config = sorcery_config
224
182
  mail = config.send(mailer).send(config.send(method),self)
225
183
  if defined?(ActionMailer) and config.send(mailer).kind_of?(Class) and config.send(mailer) < ActionMailer::Base
226
- mail.deliver
227
- end
228
- end
229
- end
230
-
231
- # Each class which calls 'activate_sorcery!' receives an instance of this class.
232
- # Every submodule which gets loaded may add accessors to this class so that all
233
- # options will be configured from a single place.
234
- class Config
235
-
236
- attr_accessor :username_attribute_names, # change default username attribute, for example, to use :email
237
- # as the login.
238
-
239
- :password_attribute_name, # change *virtual* password attribute, the one which is used
240
- # until an encrypted one is generated.
241
-
242
- :email_attribute_name, # change default email attribute.
243
-
244
- :downcase_username_before_authenticating, # downcase the username before trying to authenticate, default is false
245
-
246
- :crypted_password_attribute_name, # change default crypted_password attribute.
247
- :salt_join_token, # what pattern to use to join the password with the salt
248
- :salt_attribute_name, # change default salt attribute.
249
- :stretches, # how many times to apply encryption to the password.
250
- :encryption_key, # encryption key used to encrypt reversible encryptions such as
251
- # AES256.
252
-
253
- :subclasses_inherit_config, # make this configuration inheritable for subclasses. Useful for
254
- # ActiveRecord's STI.
255
-
256
- :submodules, # configured in config/application.rb
257
- :before_authenticate, # an array of method names to call before authentication
258
- # completes. used internally.
259
-
260
- :after_config # an array of method names to call after configuration by user.
261
- # used internally.
262
-
263
- attr_reader :encryption_provider, # change default encryption_provider.
264
- :custom_encryption_provider, # use an external encryption class.
265
- :encryption_algorithm # encryption algorithm name. See 'encryption_algorithm=' below
266
- # for available options.
267
-
268
- def initialize
269
- @defaults = {
270
- :@submodules => [],
271
- :@username_attribute_names => [:email],
272
- :@password_attribute_name => :password,
273
- :@downcase_username_before_authenticating => false,
274
- :@email_attribute_name => :email,
275
- :@crypted_password_attribute_name => :crypted_password,
276
- :@encryption_algorithm => :bcrypt,
277
- :@encryption_provider => CryptoProviders::BCrypt,
278
- :@custom_encryption_provider => nil,
279
- :@encryption_key => nil,
280
- :@salt_join_token => "",
281
- :@salt_attribute_name => :salt,
282
- :@stretches => nil,
283
- :@subclasses_inherit_config => false,
284
- :@before_authenticate => [],
285
- :@after_config => []
286
- }
287
- reset!
288
- end
289
-
290
- # Resets all configuration options to their default values.
291
- def reset!
292
- @defaults.each do |k,v|
293
- instance_variable_set(k,v)
294
- end
295
- end
296
-
297
- def username_attribute_names=(fields)
298
- @username_attribute_names = fields.kind_of?(Array) ? fields : [fields]
299
- end
300
-
301
- def custom_encryption_provider=(provider)
302
- @custom_encryption_provider = @encryption_provider = provider
303
- end
304
-
305
- def encryption_algorithm=(algo)
306
- @encryption_algorithm = algo
307
- @encryption_provider = case @encryption_algorithm.to_sym
308
- when :none then nil
309
- when :md5 then CryptoProviders::MD5
310
- when :sha1 then CryptoProviders::SHA1
311
- when :sha256 then CryptoProviders::SHA256
312
- when :sha512 then CryptoProviders::SHA512
313
- when :aes256 then CryptoProviders::AES256
314
- when :bcrypt then CryptoProviders::BCrypt
315
- when :custom then @custom_encryption_provider
316
- else raise ArgumentError.new("Encryption algorithm supplied, #{algo}, is invalid")
184
+ # Rails 4.2 deprecates #deliver
185
+ if mail.respond_to?(:deliver_now)
186
+ mail.deliver_now
187
+ else
188
+ mail.deliver
189
+ end
317
190
  end
318
191
  end
319
-
320
192
  end
321
193
 
322
194
  end
@@ -13,6 +13,17 @@ module Sorcery
13
13
  @user_info_mapping = {}
14
14
  end
15
15
 
16
+ def auth_hash(access_token, hash={})
17
+ return hash if access_token.nil?
18
+
19
+ token_hash = hash.dup
20
+ token_hash[:token] = access_token.token if access_token.respond_to?(:token)
21
+ token_hash[:refresh_token] = access_token.refresh_token if access_token.respond_to?(:refresh_token)
22
+ token_hash[:expires_at] = access_token.expires_at if access_token.respond_to?(:expires_at)
23
+ token_hash[:expires_in] = access_token.expires_at if access_token.respond_to?(:expires_in)
24
+ token_hash
25
+ end
26
+
16
27
  def self.name
17
28
  super.gsub(/Sorcery::Providers::/, '').downcase
18
29
  end
@@ -12,16 +12,18 @@ module Sorcery
12
12
 
13
13
  attr_reader :mode, :param_name, :parse
14
14
  attr_accessor :access_permissions, :display, :scope, :token_url,
15
- :user_info_path
15
+ :user_info_path, :auth_path, :api_version
16
16
 
17
17
  def initialize
18
18
  super
19
19
 
20
20
  @site = 'https://graph.facebook.com'
21
- @user_info_path = '/me'
22
- @scope = 'email,offline_access'
21
+ @auth_site = 'https://www.facebook.com'
22
+ @user_info_path = 'me'
23
+ @scope = 'email'
23
24
  @display = 'page'
24
25
  @token_url = 'oauth/access_token'
26
+ @auth_path = 'dialog/oauth'
25
27
  @mode = :query
26
28
  @parse = :query
27
29
  @param_name = 'access_token'
@@ -30,7 +32,7 @@ module Sorcery
30
32
  def get_user_hash(access_token)
31
33
  response = access_token.get(user_info_path)
32
34
 
33
- {}.tap do |h|
35
+ auth_hash(access_token).tap do |h|
34
36
  h[:user_info] = JSON.parse(response.body)
35
37
  h[:uid] = h[:user_info]['id']
36
38
  end
@@ -44,8 +46,17 @@ module Sorcery
44
46
 
45
47
  # overrides oauth2#authorize_url to allow customized scope.
46
48
  def authorize_url
49
+
50
+ # Fix: replace default oauth2 options, specially to prevent the Faraday gem which
51
+ # concatenates with "/", removing the Facebook api version
52
+ options = {
53
+ site: File::join(@site, api_version.to_s),
54
+ authorize_url: File::join(@auth_site, api_version.to_s, auth_path),
55
+ token_url: token_url
56
+ }
57
+
47
58
  @scope = access_permissions.present? ? access_permissions.join(',') : scope
48
- super
59
+ super(options)
49
60
  end
50
61
 
51
62
  # tries to login the user from access token
@@ -25,8 +25,10 @@ module Sorcery
25
25
  def get_user_hash(access_token)
26
26
  response = access_token.get(user_info_path)
27
27
 
28
- {}.tap do |h|
29
- h[:user_info] = JSON.parse(response.body)
28
+ auth_hash(access_token).tap do |h|
29
+ h[:user_info] = JSON.parse(response.body).tap do |uih|
30
+ uih['email'] = primary_email(access_token) if scope =~ /user/
31
+ end
30
32
  h[:uid] = h[:user_info]['id']
31
33
  end
32
34
  end
@@ -46,6 +48,13 @@ module Sorcery
46
48
  get_access_token(args, token_url: token_url, token_method: :post)
47
49
  end
48
50
 
51
+ def primary_email(access_token)
52
+ response = access_token.get(user_info_path + "/emails")
53
+ emails = JSON.parse(response.body)
54
+ primary = emails.find{|i| i['primary'] }
55
+ primary && primary['email'] || emails.first && emails.first['email']
56
+ end
57
+
49
58
  end
50
59
  end
51
60
  end
@@ -25,7 +25,7 @@ module Sorcery
25
25
  def get_user_hash(access_token)
26
26
  response = access_token.get(user_info_url)
27
27
 
28
- {}.tap do |h|
28
+ auth_hash(access_token).tap do |h|
29
29
  h[:user_info] = JSON.parse(response.body)
30
30
  h[:uid] = h[:user_info]['id']
31
31
  end
@@ -0,0 +1,57 @@
1
+ module Sorcery
2
+ module Providers
3
+
4
+ # This class adds support for OAuth with heroku.com.
5
+
6
+ # config.heroku.key = <key>
7
+ # config.heroku.secret = <secret>
8
+ # config.heroku.callback_url = "<host>/oauth/callback?provider=heroku"
9
+ # config.heroku.scope = "read"
10
+ # config.heroku.user_info_mapping = {:email => "email", :name => "email" }
11
+
12
+ # NOTE:
13
+ # The full path must be set for OAuth Callback URL when configuring the API Client Information on Heroku.
14
+
15
+ class Heroku < Base
16
+
17
+ include Protocols::Oauth2
18
+
19
+ attr_accessor :auth_path, :scope, :token_url, :user_info_path
20
+
21
+ def initialize
22
+ super
23
+
24
+ @scope = nil
25
+ @site = 'https://id.heroku.com'
26
+ @user_info_path = 'https://api.heroku.com/account'
27
+ @auth_path = '/oauth/authorize'
28
+ @token_url = '/oauth/token'
29
+ @user_info_path = '/account'
30
+ @state = SecureRandom.hex(16)
31
+ end
32
+
33
+ def get_user_hash(access_token)
34
+ response = access_token.get(user_info_path)
35
+ body = JSON.parse(response.body)
36
+ auth_hash(access_token).tap do |h|
37
+ h[:user_info] = body
38
+ h[:uid] = body['id'].to_s
39
+ h[:email] = body['email'].to_s
40
+ end
41
+ end
42
+
43
+ def login_url(params, session)
44
+ authorize_url({ authorize_url: auth_path })
45
+ end
46
+
47
+ # tries to login the user from access token
48
+ def process_callback(params, session)
49
+ raise "Invalid state. Potential Cross Site Forgery" if params[:state] != state
50
+ args = { }.tap do |a|
51
+ a[:code] = params[:code] if params[:code]
52
+ end
53
+ get_access_token(args, token_url: token_url, token_method: :post)
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,77 @@
1
+ module Sorcery
2
+ module Providers
3
+ # This class adds support for OAuth with Jira
4
+ #
5
+ # config.jira.key = <key>
6
+ # config.jira.secret = <secret>
7
+ # ...
8
+ #
9
+ class Jira < Base
10
+
11
+ include Protocols::Oauth
12
+
13
+ attr_accessor :access_token_path, :authorize_path, :request_token_path,
14
+ :user_info_path, :site, :signature_method, :private_key_file, :callback_url
15
+
16
+
17
+ def initialize
18
+ @configuration = {
19
+ authorize_path: '/authorize',
20
+ request_token_path: '/request-token',
21
+ access_token_path: '/access-token'
22
+ }
23
+ @user_info_path = '/users/me'
24
+ end
25
+
26
+ # Override included get_consumer method to provide authorize_path
27
+ #read extra configurations
28
+ def get_consumer
29
+ @configuration = @configuration.merge({
30
+ site: site,
31
+ signature_method: signature_method,
32
+ consumer_key: key,
33
+ private_key_file: private_key_file
34
+ })
35
+ ::OAuth::Consumer.new(@key, @secret, @configuration)
36
+ end
37
+
38
+ def get_user_hash(access_token)
39
+ response = access_token.get(user_info_path)
40
+
41
+ auth_hash(access_token).tap do |h|
42
+ h[:user_info] = JSON.parse(response.body)['users'].first
43
+ h[:uid] = user_hash[:user_info]['id'].to_s
44
+ end
45
+ end
46
+
47
+ # calculates and returns the url to which the user should be redirected,
48
+ # to get authenticated at the external provider's site.
49
+ def login_url(params, session)
50
+ req_token = get_request_token
51
+ session[:request_token] = req_token.token
52
+ session[:request_token_secret] = req_token.secret
53
+
54
+ #it was like that -> redirect_to authorize_url({ request_token: req_token.token, request_token_secret: req_token.secret })
55
+ #for some reason Jira does not need these parameters
56
+
57
+ get_request_token(
58
+ session[:request_token],
59
+ session[:request_token_secret]
60
+ ).authorize_url
61
+ end
62
+
63
+ # tries to login the user from access token
64
+ def process_callback(params, session)
65
+ args = {
66
+ oauth_verifier: params[:oauth_verifier],
67
+ request_token: session[:request_token],
68
+ request_token_secret: session[:request_token_secret]
69
+ }
70
+
71
+ args.merge!({ code: params[:code] }) if params[:code]
72
+ get_access_token(args)
73
+ end
74
+
75
+ end
76
+ end
77
+ end
@@ -34,7 +34,7 @@ module Sorcery
34
34
  fields = self.user_info_fields.join(',')
35
35
  response = access_token.get("#{@user_info_path}:(#{fields})", 'x-li-format' => 'json')
36
36
 
37
- {}.tap do |h|
37
+ auth_hash(access_token).tap do |h|
38
38
  h[:user_info] = JSON.parse(response.body)
39
39
  h[:uid] = h[:user_info]['id'].to_s
40
40
  end
@@ -26,7 +26,7 @@ module Sorcery
26
26
  access_token.token_param = 'access_token'
27
27
  response = access_token.get(user_info_url)
28
28
 
29
- {}.tap do |h|
29
+ auth_hash(access_token).tap do |h|
30
30
  h[:user_info] = JSON.parse(response.body)
31
31
  h[:uid] = h[:user_info]['id']
32
32
  end
@@ -0,0 +1,50 @@
1
+ module Sorcery
2
+ module Providers
3
+ # This class adds support for OAuth with salesforce.com.
4
+ #
5
+ # config.salesforce.key = <key>
6
+ # config.salesforce.secret = <secret>
7
+ # ...
8
+ #
9
+ class Salesforce < Base
10
+
11
+ include Protocols::Oauth2
12
+
13
+ attr_accessor :auth_url, :token_url, :scope
14
+
15
+ def initialize
16
+ super
17
+
18
+ @site = 'https://login.salesforce.com'
19
+ @auth_url = '/services/oauth2/authorize'
20
+ @token_url = '/services/oauth2/token'
21
+ end
22
+
23
+ def get_user_hash(access_token)
24
+ user_info_url = access_token.params['id']
25
+ response = access_token.get(user_info_url)
26
+
27
+ auth_hash(access_token).tap do |h|
28
+ h[:user_info] = JSON.parse(response.body)
29
+ h[:uid] = h[:user_info]['user_id']
30
+ end
31
+ end
32
+
33
+ # calculates and returns the url to which the user should be redirected,
34
+ # to get authenticated at the external provider's site.
35
+ def login_url(params, session)
36
+ authorize_url({ authorize_url: auth_url })
37
+ end
38
+
39
+ # tries to login the user from access token
40
+ def process_callback(params, session)
41
+ args = {}.tap do |a|
42
+ a[:code] = params[:code] if params[:code]
43
+ end
44
+
45
+ get_access_token(args, token_url: token_url, token_method: :post)
46
+ end
47
+
48
+ end
49
+ end
50
+ end
@@ -27,7 +27,7 @@ module Sorcery
27
27
  def get_user_hash(access_token)
28
28
  response = access_token.get(user_info_path)
29
29
 
30
- {}.tap do |h|
30
+ auth_hash(access_token).tap do |h|
31
31
  h[:user_info] = JSON.parse(response.body)
32
32
  h[:uid] = h[:user_info]['id'].to_s
33
33
  end
@@ -19,24 +19,26 @@ module Sorcery
19
19
  @user_info_url = 'https://api.vk.com/method/getProfiles'
20
20
  @auth_path = '/authorize'
21
21
  @token_path = '/access_token'
22
- @scope = ''
22
+ @scope = 'email'
23
23
  end
24
24
 
25
25
  def get_user_hash(access_token)
26
- user_hash = {}
26
+ user_hash = auth_hash(access_token)
27
27
 
28
28
  params = {
29
29
  access_token: access_token.token,
30
30
  uids: access_token.params['user_id'],
31
31
  fields: user_info_mapping.values.join(','),
32
- scope: scope,
32
+ scope: scope
33
33
  }
34
34
 
35
35
  response = access_token.get(user_info_url, params: params)
36
36
  if user_hash[:user_info] = JSON.parse(response.body)
37
37
  user_hash[:user_info] = user_hash[:user_info]['response'][0]
38
- user_hash[:user_info]['full_name'] = [user_hash[:user_info]['first_name'], user_hash[:user_info]['last_name']].join
38
+ user_hash[:user_info]['full_name'] = [user_hash[:user_info]['first_name'], user_hash[:user_info]['last_name']].join(' ')
39
+
39
40
  user_hash[:uid] = user_hash[:user_info]['uid']
41
+ user_hash[:user_info]['email'] = access_token.params['email']
40
42
  end
41
43
  user_hash
42
44
  end
@@ -32,9 +32,9 @@ module Sorcery
32
32
  def get_user_hash(access_token)
33
33
  response = access_token.get(user_info_path)
34
34
 
35
- {}.tap do |h|
35
+ auth_hash(access_token).tap do |h|
36
36
  h[:user_info] = JSON.parse(response.body)['users'].first
37
- h[:uid] = user_hash[:user_info]['id'].to_s
37
+ h[:uid] = h[:user_info]['id'].to_s
38
38
  end
39
39
  end
40
40
 
@@ -30,14 +30,14 @@ module Sorcery
30
30
 
31
31
  def create_new_user(attributes_hash = nil)
32
32
  @user = build_new_user(attributes_hash)
33
- @user.sorcery_save(:raise_on_failure => true)
33
+ @user.sorcery_adapter.save(:raise_on_failure => true)
34
34
  @user
35
35
  end
36
36
 
37
37
  def create_new_external_user(provider, attributes_hash = nil)
38
38
  user_attributes_hash = attributes_hash || {:username => 'gizmo'}
39
39
  @user = User.new(user_attributes_hash)
40
- @user.sorcery_save(:raise_on_failure => true)
40
+ @user.sorcery_adapter.save(:raise_on_failure => true)
41
41
  @user.authentications.create!({:provider => provider, :uid => 123})
42
42
  @user
43
43
  end
@@ -47,7 +47,7 @@ module Sorcery
47
47
 
48
48
  user_attributes_hash = attributes_hash || {:username => 'gizmo'}
49
49
  @user = User.new(user_attributes_hash)
50
- @user.sorcery_save(:raise_on_failure => true)
50
+ @user.sorcery_adapter.save(:raise_on_failure => true)
51
51
  @user.send(authentication_association).create!({:provider => provider, :uid => 123})
52
52
  @user
53
53
  end
@@ -58,6 +58,10 @@ module Sorcery
58
58
  end
59
59
  end
60
60
 
61
+ def update_model(&block)
62
+ User.class_exec(&block)
63
+ end
64
+
61
65
  private
62
66
 
63
67
  # reload user class between specs