sorcery 0.7.0 → 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 (204) hide show
  1. data/.travis.yml +3 -0
  2. data/Gemfile +9 -6
  3. data/Gemfile.lock +106 -75
  4. data/README.rdoc +42 -55
  5. data/Rakefile +32 -7
  6. data/VERSION +1 -1
  7. data/lib/generators/sorcery/USAGE +22 -0
  8. data/lib/generators/sorcery/install_generator.rb +77 -0
  9. data/lib/generators/sorcery/templates/initializer.rb +417 -0
  10. data/lib/generators/sorcery/templates/migration/activity_logging.rb +17 -0
  11. data/lib/generators/sorcery/templates/migration/brute_force_protection.rb +13 -0
  12. data/lib/generators/{sorcery_migration/templates → sorcery/templates/migration}/core.rb +2 -2
  13. data/lib/generators/{sorcery_migration/templates → sorcery/templates/migration}/external.rb +1 -1
  14. data/lib/generators/sorcery/templates/migration/remember_me.rb +15 -0
  15. data/lib/generators/sorcery/templates/migration/reset_password.rb +17 -0
  16. data/lib/generators/sorcery/templates/migration/user_activation.rb +17 -0
  17. data/lib/sorcery/controller/submodules/activity_logging.rb +8 -11
  18. data/lib/sorcery/controller/submodules/brute_force_protection.rb +2 -3
  19. data/lib/sorcery/controller/submodules/external/protocols/oauth2.rb +25 -10
  20. data/lib/sorcery/controller/submodules/external/providers/facebook.rb +20 -5
  21. data/lib/sorcery/controller/submodules/external/providers/github.rb +15 -4
  22. data/lib/sorcery/controller/submodules/external/providers/google.rb +90 -0
  23. data/lib/sorcery/controller/submodules/external/providers/linkedin.rb +101 -0
  24. data/lib/sorcery/controller/submodules/external/providers/liveid.rb +91 -0
  25. data/lib/sorcery/controller/submodules/external/providers/twitter.rb +3 -2
  26. data/lib/sorcery/controller/submodules/external/providers/vkontakte.rb +94 -0
  27. data/lib/sorcery/controller/submodules/external.rb +89 -15
  28. data/lib/sorcery/controller/submodules/http_basic_auth.rb +1 -1
  29. data/lib/sorcery/controller/submodules/remember_me.rb +26 -15
  30. data/lib/sorcery/controller/submodules/session_timeout.rb +3 -3
  31. data/lib/sorcery/controller.rb +55 -46
  32. data/lib/sorcery/crypto_providers/bcrypt.rb +1 -0
  33. data/lib/sorcery/model/adapters/active_record.rb +21 -1
  34. data/lib/sorcery/model/adapters/mongo_mapper.rb +56 -0
  35. data/lib/sorcery/model/adapters/mongoid.rb +23 -4
  36. data/lib/sorcery/model/submodules/activity_logging.rb +4 -4
  37. data/lib/sorcery/model/submodules/brute_force_protection.rb +54 -15
  38. data/lib/sorcery/model/submodules/remember_me.rb +12 -7
  39. data/lib/sorcery/model/submodules/reset_password.rb +27 -11
  40. data/lib/sorcery/model/submodules/user_activation.rb +27 -8
  41. data/lib/sorcery/model/temporary_token.rb +4 -2
  42. data/lib/sorcery/model.rb +78 -47
  43. data/lib/sorcery/railties/tasks.rake +2 -0
  44. data/lib/sorcery.rb +10 -8
  45. data/sorcery.gemspec +107 -228
  46. data/spec/Gemfile +1 -1
  47. data/spec/Gemfile.lock +27 -23
  48. data/spec/README.md +12 -7
  49. data/spec/rails3/Gemfile +3 -3
  50. data/spec/rails3/Gemfile.lock +62 -55
  51. data/spec/rails3/app/controllers/application_controller.rb +85 -2
  52. data/spec/rails3/app/mailers/sorcery_mailer.rb +7 -0
  53. data/spec/rails3/app/views/sorcery_mailer/send_unlock_token_email.text.erb +1 -0
  54. data/spec/rails3/config/environments/in_memory.rb +35 -0
  55. data/spec/rails3/db/migrate/brute_force_protection/20101224223626_add_brute_force_protection_to_users.rb +2 -0
  56. data/spec/rails3/spec/controller_activity_logging_spec.rb +16 -6
  57. data/spec/rails3/spec/controller_brute_force_protection_spec.rb +25 -3
  58. data/spec/rails3/spec/controller_oauth2_spec.rb +260 -20
  59. data/spec/rails3/spec/controller_oauth_spec.rb +111 -6
  60. data/spec/rails3/spec/controller_session_timeout_spec.rb +4 -4
  61. data/spec/rails3/spec/controller_spec.rb +37 -2
  62. data/spec/rails3/spec/integration_spec.rb +15 -15
  63. data/spec/rails3/spec/spec_helper.rb +0 -2
  64. data/spec/rails3_mongo_mapper/.gitignore +4 -0
  65. data/spec/rails3_mongo_mapper/.rspec +1 -0
  66. data/spec/{sinatra_modular → rails3_mongo_mapper}/Gemfile +6 -5
  67. data/spec/rails3_mongo_mapper/Gemfile.lock +156 -0
  68. data/spec/{sinatra → rails3_mongo_mapper}/Rakefile +3 -3
  69. data/spec/rails3_mongo_mapper/app/controllers/application_controller.rb +122 -0
  70. data/spec/rails3_mongo_mapper/app/helpers/application_helper.rb +2 -0
  71. data/spec/rails3_mongo_mapper/app/models/authentication.rb +6 -0
  72. data/spec/rails3_mongo_mapper/app/models/user.rb +5 -0
  73. data/spec/rails3_mongo_mapper/app/views/layouts/application.html.erb +14 -0
  74. data/spec/rails3_mongo_mapper/app/views/sorcery_mailer/activation_email.html.erb +17 -0
  75. data/spec/rails3_mongo_mapper/app/views/sorcery_mailer/activation_email.text.erb +9 -0
  76. data/spec/rails3_mongo_mapper/app/views/sorcery_mailer/activation_success_email.html.erb +17 -0
  77. data/spec/rails3_mongo_mapper/app/views/sorcery_mailer/activation_success_email.text.erb +9 -0
  78. data/spec/rails3_mongo_mapper/app/views/sorcery_mailer/reset_password_email.html.erb +16 -0
  79. data/spec/rails3_mongo_mapper/app/views/sorcery_mailer/reset_password_email.text.erb +8 -0
  80. data/spec/rails3_mongo_mapper/config/application.rb +51 -0
  81. data/spec/rails3_mongo_mapper/config/boot.rb +13 -0
  82. data/spec/rails3_mongo_mapper/config/environment.rb +5 -0
  83. data/spec/rails3_mongo_mapper/config/environments/development.rb +30 -0
  84. data/spec/rails3_mongo_mapper/config/environments/in_memory.rb +0 -0
  85. data/spec/rails3_mongo_mapper/config/environments/production.rb +49 -0
  86. data/spec/rails3_mongo_mapper/config/environments/test.rb +35 -0
  87. data/spec/rails3_mongo_mapper/config/initializers/backtrace_silencers.rb +7 -0
  88. data/spec/rails3_mongo_mapper/config/initializers/inflections.rb +10 -0
  89. data/spec/rails3_mongo_mapper/config/initializers/mime_types.rb +5 -0
  90. data/spec/rails3_mongo_mapper/config/initializers/mongo.rb +2 -0
  91. data/spec/rails3_mongo_mapper/config/initializers/secret_token.rb +7 -0
  92. data/spec/rails3_mongo_mapper/config/initializers/session_store.rb +8 -0
  93. data/spec/rails3_mongo_mapper/config/locales/en.yml +5 -0
  94. data/spec/rails3_mongo_mapper/config/routes.rb +59 -0
  95. data/spec/rails3_mongo_mapper/config.ru +4 -0
  96. data/spec/rails3_mongo_mapper/db/schema.rb +23 -0
  97. data/spec/rails3_mongo_mapper/db/seeds.rb +7 -0
  98. data/spec/rails3_mongo_mapper/lib/tasks/.gitkeep +0 -0
  99. data/spec/rails3_mongo_mapper/public/404.html +26 -0
  100. data/spec/rails3_mongo_mapper/public/422.html +26 -0
  101. data/spec/rails3_mongo_mapper/public/500.html +26 -0
  102. data/spec/rails3_mongo_mapper/public/favicon.ico +0 -0
  103. data/spec/rails3_mongo_mapper/public/images/rails.png +0 -0
  104. data/spec/rails3_mongo_mapper/public/javascripts/application.js +2 -0
  105. data/spec/rails3_mongo_mapper/public/javascripts/controls.js +965 -0
  106. data/spec/rails3_mongo_mapper/public/javascripts/dragdrop.js +974 -0
  107. data/spec/rails3_mongo_mapper/public/javascripts/effects.js +1123 -0
  108. data/spec/rails3_mongo_mapper/public/javascripts/prototype.js +6001 -0
  109. data/spec/rails3_mongo_mapper/public/javascripts/rails.js +175 -0
  110. data/spec/rails3_mongo_mapper/public/robots.txt +5 -0
  111. data/spec/rails3_mongo_mapper/public/stylesheets/.gitkeep +0 -0
  112. data/spec/rails3_mongo_mapper/script/rails +6 -0
  113. data/spec/rails3_mongo_mapper/spec/controller_spec.rb +170 -0
  114. data/spec/rails3_mongo_mapper/spec/spec_helper.orig.rb +27 -0
  115. data/spec/rails3_mongo_mapper/spec/spec_helper.rb +55 -0
  116. data/spec/rails3_mongo_mapper/spec/user_activation_spec.rb +9 -0
  117. data/spec/rails3_mongo_mapper/spec/user_activity_logging_spec.rb +8 -0
  118. data/spec/rails3_mongo_mapper/spec/user_brute_force_protection_spec.rb +8 -0
  119. data/spec/rails3_mongo_mapper/spec/user_oauth_spec.rb +8 -0
  120. data/spec/rails3_mongo_mapper/spec/user_remember_me_spec.rb +8 -0
  121. data/spec/rails3_mongo_mapper/spec/user_reset_password_spec.rb +8 -0
  122. data/spec/rails3_mongo_mapper/spec/user_spec.rb +37 -0
  123. data/spec/rails3_mongo_mapper/vendor/plugins/.gitkeep +0 -0
  124. data/spec/rails3_mongoid/Gemfile +2 -1
  125. data/spec/rails3_mongoid/Gemfile.lock +47 -42
  126. data/spec/rails3_mongoid/app/controllers/application_controller.rb +19 -0
  127. data/spec/rails3_mongoid/config/mongoid.yml +1 -1
  128. data/spec/rails3_mongoid/script/rails +0 -0
  129. data/spec/rails3_mongoid/spec/controller_activity_logging_spec.rb +105 -0
  130. data/spec/rails3_mongoid/spec/controller_spec.rb +52 -1
  131. data/spec/rails3_mongoid/spec/user_spec.rb +1 -0
  132. data/spec/shared_examples/controller_oauth2_shared_examples.rb +20 -1
  133. data/spec/shared_examples/controller_oauth_shared_examples.rb +18 -0
  134. data/spec/shared_examples/user_activation_shared_examples.rb +72 -42
  135. data/spec/shared_examples/user_remember_me_shared_examples.rb +1 -1
  136. data/spec/shared_examples/user_reset_password_shared_examples.rb +81 -28
  137. data/spec/shared_examples/user_shared_examples.rb +29 -1
  138. data/spec/sorcery_crypto_providers_spec.rb +14 -1
  139. metadata +111 -229
  140. data/lib/generators/sorcery_migration/sorcery_migration_generator.rb +0 -24
  141. data/lib/generators/sorcery_migration/templates/activity_logging.rb +0 -17
  142. data/lib/generators/sorcery_migration/templates/brute_force_protection.rb +0 -11
  143. data/lib/generators/sorcery_migration/templates/remember_me.rb +0 -15
  144. data/lib/generators/sorcery_migration/templates/reset_password.rb +0 -17
  145. data/lib/generators/sorcery_migration/templates/user_activation.rb +0 -17
  146. data/lib/sorcery/controller/adapters/sinatra.rb +0 -115
  147. data/lib/sorcery/initializers/initializer.rb +0 -199
  148. data/lib/sorcery/sinatra.rb +0 -4
  149. data/lib/sorcery/test_helpers/internal/sinatra.rb +0 -74
  150. data/lib/sorcery/test_helpers/internal/sinatra_modular.rb +0 -74
  151. data/lib/sorcery/test_helpers/sinatra.rb +0 -88
  152. data/spec/rails3/Rakefile.unused +0 -7
  153. data/spec/sinatra/Gemfile +0 -15
  154. data/spec/sinatra/Gemfile.lock +0 -115
  155. data/spec/sinatra/authentication.rb +0 -3
  156. data/spec/sinatra/db/migrate/activation/20101224223622_add_activation_to_users.rb +0 -17
  157. data/spec/sinatra/db/migrate/activity_logging/20101224223624_add_activity_logging_to_users.rb +0 -17
  158. data/spec/sinatra/db/migrate/brute_force_protection/20101224223626_add_brute_force_protection_to_users.rb +0 -11
  159. data/spec/sinatra/db/migrate/core/20101224223620_create_users.rb +0 -16
  160. data/spec/sinatra/db/migrate/external/20101224223628_create_authentications.rb +0 -14
  161. data/spec/sinatra/db/migrate/remember_me/20101224223623_add_remember_me_token_to_users.rb +0 -15
  162. data/spec/sinatra/db/migrate/reset_password/20101224223622_add_reset_password_to_users.rb +0 -13
  163. data/spec/sinatra/filters.rb +0 -27
  164. data/spec/sinatra/modular.rb +0 -157
  165. data/spec/sinatra/myapp.rb +0 -133
  166. data/spec/sinatra/spec/controller_activity_logging_spec.rb +0 -85
  167. data/spec/sinatra/spec/controller_brute_force_protection_spec.rb +0 -70
  168. data/spec/sinatra/spec/controller_http_basic_auth_spec.rb +0 -53
  169. data/spec/sinatra/spec/controller_oauth2_spec.rb +0 -96
  170. data/spec/sinatra/spec/controller_oauth_spec.rb +0 -100
  171. data/spec/sinatra/spec/controller_remember_me_spec.rb +0 -64
  172. data/spec/sinatra/spec/controller_session_timeout_spec.rb +0 -57
  173. data/spec/sinatra/spec/controller_spec.rb +0 -127
  174. data/spec/sinatra/spec/spec_helper.rb +0 -45
  175. data/spec/sinatra/user.rb +0 -6
  176. data/spec/sinatra/views/test_login.erb +0 -4
  177. data/spec/sinatra_modular/Gemfile.lock +0 -115
  178. data/spec/sinatra_modular/Rakefile +0 -11
  179. data/spec/sinatra_modular/authentication.rb +0 -3
  180. data/spec/sinatra_modular/db/migrate/activation/20101224223622_add_activation_to_users.rb +0 -17
  181. data/spec/sinatra_modular/db/migrate/activity_logging/20101224223624_add_activity_logging_to_users.rb +0 -17
  182. data/spec/sinatra_modular/db/migrate/brute_force_protection/20101224223626_add_brute_force_protection_to_users.rb +0 -11
  183. data/spec/sinatra_modular/db/migrate/core/20101224223620_create_users.rb +0 -16
  184. data/spec/sinatra_modular/db/migrate/external/20101224223628_create_authentications.rb +0 -14
  185. data/spec/sinatra_modular/db/migrate/remember_me/20101224223623_add_remember_me_token_to_users.rb +0 -15
  186. data/spec/sinatra_modular/db/migrate/reset_password/20101224223622_add_reset_password_to_users.rb +0 -13
  187. data/spec/sinatra_modular/filters.rb +0 -27
  188. data/spec/sinatra_modular/modular.rb +0 -157
  189. data/spec/sinatra_modular/myapp.rb +0 -133
  190. data/spec/sinatra_modular/sorcery_mailer.rb +0 -25
  191. data/spec/sinatra_modular/spec_modular/controller_activity_logging_spec.rb +0 -85
  192. data/spec/sinatra_modular/spec_modular/controller_brute_force_protection_spec.rb +0 -70
  193. data/spec/sinatra_modular/spec_modular/controller_http_basic_auth_spec.rb +0 -53
  194. data/spec/sinatra_modular/spec_modular/controller_oauth2_spec.rb +0 -96
  195. data/spec/sinatra_modular/spec_modular/controller_oauth_spec.rb +0 -100
  196. data/spec/sinatra_modular/spec_modular/controller_remember_me_spec.rb +0 -64
  197. data/spec/sinatra_modular/spec_modular/controller_session_timeout_spec.rb +0 -57
  198. data/spec/sinatra_modular/spec_modular/controller_spec.rb +0 -116
  199. data/spec/sinatra_modular/spec_modular/spec.opts +0 -2
  200. data/spec/sinatra_modular/spec_modular/spec_helper.rb +0 -51
  201. data/spec/sinatra_modular/user.rb +0 -6
  202. data/spec/sinatra_modular/views/test_login.erb +0 -4
  203. /data/spec/{sinatra → rails3_mongo_mapper/app/mailers}/sorcery_mailer.rb +0 -0
  204. /data/spec/{sinatra → rails3_mongo_mapper}/spec/spec.opts +0 -0
@@ -34,8 +34,11 @@ module Sorcery
34
34
  :site,
35
35
  :user_info_path,
36
36
  :scope,
37
- :user_info_mapping
38
-
37
+ :user_info_mapping,
38
+ :display,
39
+ :access_permissions
40
+ attr_reader :access_token
41
+
39
42
  include Protocols::Oauth2
40
43
 
41
44
  def init
@@ -43,12 +46,17 @@ module Sorcery
43
46
  @user_info_path = "/me"
44
47
  @scope = "email,offline_access"
45
48
  @user_info_mapping = {}
49
+ @display = "page"
50
+ @token_url = "oauth/access_token"
51
+ @mode = :query
52
+ @parse = :query
53
+ @param_name = "access_token"
46
54
  end
47
55
 
48
56
  def get_user_hash
49
57
  user_hash = {}
50
58
  response = @access_token.get(@user_info_path)
51
- user_hash[:user_info] = JSON.parse(response)
59
+ user_hash[:user_info] = JSON.parse(response.body)
52
60
  user_hash[:uid] = user_hash[:user_info]['id']
53
61
  user_hash
54
62
  end
@@ -62,12 +70,19 @@ module Sorcery
62
70
  def login_url(params,session)
63
71
  self.authorize_url
64
72
  end
65
-
73
+
74
+ # overrides oauth2#authorize_url to allow customized scope.
75
+ def authorize_url
76
+ @scope = self.access_permissions.present? ? self.access_permissions.join(",") : @scope
77
+ super
78
+ end
79
+
66
80
  # tries to login the user from access token
67
81
  def process_callback(params,session)
68
82
  args = {}
83
+ options = { :token_url => @token_url, :mode => @mode, :param_name => @param_name, :parse => @parse }
69
84
  args.merge!({:code => params[:code]}) if params[:code]
70
- @access_token = self.get_access_token(args)
85
+ @access_token = self.get_access_token(args, options)
71
86
  end
72
87
 
73
88
  end
@@ -31,22 +31,29 @@ module Sorcery
31
31
  attr_accessor :key,
32
32
  :secret,
33
33
  :callback_url,
34
+ :auth_path,
35
+ :token_path,
34
36
  :site,
37
+ :scope,
35
38
  :user_info_path,
36
39
  :user_info_mapping
40
+ attr_reader :access_token
37
41
 
38
42
  include Protocols::Oauth2
39
43
 
40
44
  def init
41
45
  @site = "https://github.com/"
42
- @user_info_path = "/api/v2/json/user/show"
46
+ @user_info_path = "https://api.github.com/user"
47
+ @scope = nil
48
+ @auth_path = "/login/oauth/authorize"
49
+ @token_path = "/login/oauth/access_token"
43
50
  @user_info_mapping = {}
44
51
  end
45
52
 
46
53
  def get_user_hash
47
54
  user_hash = {}
48
55
  response = @access_token.get(@user_info_path)
49
- user_hash[:user_info] = JSON.parse(response)
56
+ user_hash[:user_info] = JSON.parse(response.body)
50
57
  user_hash[:uid] = user_hash[:user_info]['id']
51
58
  user_hash
52
59
  end
@@ -58,14 +65,18 @@ module Sorcery
58
65
  # calculates and returns the url to which the user should be redirected,
59
66
  # to get authenticated at the external provider's site.
60
67
  def login_url(params,session)
61
- self.authorize_url({:authorize_path => '/login/oauth/authorize'})
68
+ self.authorize_url({:authorize_url => @auth_path})
62
69
  end
63
70
 
64
71
  # tries to login the user from access token
65
72
  def process_callback(params,session)
66
73
  args = {}
67
74
  args.merge!({:code => params[:code]}) if params[:code]
68
- @access_token = self.get_access_token(args)
75
+ options = {
76
+ :token_url => @token_path,
77
+ :token_method => :post
78
+ }
79
+ @access_token = self.get_access_token(args, options)
69
80
  end
70
81
 
71
82
  end
@@ -0,0 +1,90 @@
1
+ module Sorcery
2
+ module Controller
3
+ module Submodules
4
+ module External
5
+ module Providers
6
+ # This module adds support for OAuth with google.com.
7
+ # When included in the 'config.providers' option, it adds a new option, 'config.google'.
8
+ # Via this new option you can configure Google specific settings like your app's key and secret.
9
+ #
10
+ # config.google.key = <key>
11
+ # config.google.secret = <secret>
12
+ # ...
13
+ #
14
+ module Google
15
+ def self.included(base)
16
+ base.module_eval do
17
+ class << self
18
+ attr_reader :google # access to google_client.
19
+
20
+ def merge_google_defaults!
21
+ @defaults.merge!(:@google => GoogleClient)
22
+ end
23
+ end
24
+ merge_google_defaults!
25
+ update!
26
+ end
27
+ end
28
+
29
+ module GoogleClient
30
+ class << self
31
+ attr_accessor :key,
32
+ :secret,
33
+ :callback_url,
34
+ :site,
35
+ :auth_url,
36
+ :token_path,
37
+ :user_info_url,
38
+ :scope,
39
+ :user_info_mapping
40
+ attr_reader :access_token
41
+
42
+ include Protocols::Oauth2
43
+
44
+ def init
45
+ @site = "https://accounts.google.com"
46
+ @auth_url = "/o/oauth2/auth"
47
+ @token_url = "/o/oauth2/token"
48
+ @user_info_url = "https://www.googleapis.com/oauth2/v1/userinfo"
49
+ @scope = "https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile"
50
+ @user_info_mapping = {}
51
+ end
52
+
53
+ def get_user_hash
54
+ user_hash = {}
55
+ response = @access_token.get(@user_info_url)
56
+ user_hash[:user_info] = JSON.parse(response.body)
57
+ user_hash[:uid] = user_hash[:user_info]['id']
58
+ user_hash
59
+ end
60
+
61
+ def has_callback?
62
+ true
63
+ end
64
+
65
+ # calculates and returns the url to which the user should be redirected,
66
+ # to get authenticated at the external provider's site.
67
+ def login_url(params,session)
68
+ self.authorize_url({:authorize_url => @auth_url})
69
+ end
70
+
71
+ # tries to login the user from access token
72
+ def process_callback(params,session)
73
+ args = {}
74
+ args.merge!({:code => params[:code]}) if params[:code]
75
+ options = {
76
+ :token_url => @token_url,
77
+ :token_method => :post
78
+ }
79
+ @access_token = self.get_access_token(args, options)
80
+ end
81
+ end
82
+ init
83
+ end
84
+
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
90
+ 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
@@ -0,0 +1,91 @@
1
+ module Sorcery
2
+ module Controller
3
+ module Submodules
4
+ module External
5
+ module Providers
6
+ # This module adds support for OAuth with microsoft liveid
7
+ # When included in the 'config.providers' option, it adds a new option, 'config.liveid'.
8
+ # Via this new option you can configure LiveId specific settings like your app's key and secret.
9
+ #
10
+ # config.liveid.key = <key>
11
+ # config.liveid.secret = <secret>
12
+ # ...
13
+ #
14
+ module Liveid
15
+ def self.included(base)
16
+ base.module_eval do
17
+ class << self
18
+ attr_reader :liveid # access to liveid_client.
19
+
20
+ def merge_liveid_defaults!
21
+ @defaults.merge!(:@liveid => LiveidClient)
22
+ end
23
+ end
24
+ merge_liveid_defaults!
25
+ update!
26
+ end
27
+ end
28
+
29
+ module LiveidClient
30
+ class << self
31
+ attr_accessor :key,
32
+ :secret,
33
+ :callback_url,
34
+ :site,
35
+ :auth_url,
36
+ :token_path,
37
+ :user_info_url,
38
+ :scope,
39
+ :user_info_mapping
40
+ attr_reader :access_token
41
+
42
+ include Protocols::Oauth2
43
+
44
+ def init
45
+ @site = "https://oauth.live.com/"
46
+ @auth_url = "/authorize"
47
+ @token_path = "/token"
48
+ @user_info_url = "https://apis.live.net/v5.0/me"
49
+ @scope = "wl.basic wl.emails wl.offline_access"
50
+ @user_info_mapping = {}
51
+ end
52
+
53
+ def get_user_hash
54
+ user_hash = {}
55
+ @access_token.token_param = "access_token"
56
+ response = @access_token.get(@user_info_url)
57
+ user_hash[:user_info] = JSON.parse(response.body)
58
+ user_hash[:uid] = user_hash[:user_info]['id']
59
+ user_hash
60
+ end
61
+
62
+ def has_callback?
63
+ true
64
+ end
65
+
66
+ # calculates and returns the url to which the user should be redirected,
67
+ # to get authenticated at the external provider's site.
68
+ def login_url(params,session)
69
+ self.authorize_url({:authorize_url => @auth_url})
70
+ end
71
+
72
+ # tries to login the user from access token
73
+ def process_callback(params,session)
74
+ args = {}
75
+ args.merge!({:code => params[:code]}) if params[:code]
76
+ options = {
77
+ :access_token_path => @token_path,
78
+ :access_token_method => :post
79
+ }
80
+ @access_token = self.get_access_token(args, options)
81
+ end
82
+ end
83
+ init
84
+ end
85
+
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
@@ -37,7 +37,8 @@ module Sorcery
37
37
  :site,
38
38
  :user_info_path,
39
39
  :user_info_mapping
40
-
40
+ attr_reader :access_token
41
+
41
42
  include Protocols::Oauth1
42
43
 
43
44
  # Override included get_consumer method to provide authorize_path
@@ -55,7 +56,7 @@ module Sorcery
55
56
  user_hash = {}
56
57
  response = @access_token.get(@user_info_path)
57
58
  user_hash[:user_info] = JSON.parse(response.body)
58
- user_hash[:uid] = user_hash[:user_info]['id']
59
+ user_hash[:uid] = user_hash[:user_info]['id'].to_s
59
60
  user_hash
60
61
  end
61
62
 
@@ -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,30 +28,88 @@ 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
54
-
65
+
66
+ # get provider access account
67
+ def access_token(provider)
68
+ @provider = Config.send(provider)
69
+ @provider.access_token
70
+ end
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
+
55
113
  # this method automatically creates a new user from the data in the external user hash.
56
114
  # The mappings from user hash fields to user db fields are set at controller config.
57
115
  # If the hash field you would like to map is nested, use slashes. For example, Given a hash like:
@@ -64,30 +122,46 @@ module Sorcery
64
122
  #
65
123
  # And this will cause 'moishe' to be set as the value of :username field.
66
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
+ #
67
129
  def create_from(provider)
68
130
  provider = provider.to_sym
69
131
  @provider = Config.send(provider)
70
132
  @user_hash = @provider.get_user_hash
71
133
  config = user_class.sorcery_config
72
- attrs = {}
73
- @provider.user_info_mapping.each do |k,v|
74
- if (varr = v.split("/")).size > 1
75
- attribute_value = varr.inject(@user_hash[:user_info]) {|hsh,v| hsh[v] } rescue nil
76
- attribute_value.nil? ? attrs : attrs.merge!(k => attribute_value)
77
- else
78
- attrs.merge!(k => @user_hash[:user_info][v])
79
- end
80
- end
134
+
135
+ attrs = user_attrs(@provider.user_info_mapping, @user_hash)
136
+
81
137
  user_class.transaction do
82
138
  @user = user_class.new()
83
139
  attrs.each do |k,v|
84
140
  @user.send(:"#{k}=", v)
85
141
  end
142
+
143
+ if block_given?
144
+ return false unless yield @user
145
+ end
146
+
86
147
  @user.save(:validate => false)
87
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]})
88
149
  end
89
150
  @user
90
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
91
165
  end
92
166
  end
93
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