sorcery 0.8.6 → 0.9.0

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.

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 +23 -1
  5. data/Gemfile +1 -0
  6. data/README.md +137 -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 +34 -9
  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.rb +75 -43
  21. data/lib/sorcery/adapters/active_record_adapter.rb +120 -0
  22. data/lib/sorcery/adapters/base_adapter.rb +30 -0
  23. data/lib/sorcery/adapters/data_mapper_adapter.rb +176 -0
  24. data/lib/sorcery/adapters/mongo_mapper_adapter.rb +110 -0
  25. data/lib/sorcery/adapters/mongoid_adapter.rb +97 -0
  26. data/lib/sorcery/controller.rb +5 -64
  27. data/lib/sorcery/controller/config.rb +65 -0
  28. data/lib/sorcery/controller/submodules/activity_logging.rb +16 -21
  29. data/lib/sorcery/controller/submodules/brute_force_protection.rb +6 -6
  30. data/lib/sorcery/controller/submodules/external.rb +8 -28
  31. data/lib/sorcery/controller/submodules/remember_me.rb +4 -4
  32. data/lib/sorcery/controller/submodules/session_timeout.rb +10 -6
  33. data/lib/sorcery/model.rb +43 -175
  34. data/lib/sorcery/model/config.rb +96 -0
  35. data/lib/sorcery/model/submodules/activity_logging.rb +29 -36
  36. data/lib/sorcery/model/submodules/brute_force_protection.rb +21 -37
  37. data/lib/sorcery/model/submodules/external.rb +53 -9
  38. data/lib/sorcery/model/submodules/remember_me.rb +12 -31
  39. data/lib/sorcery/model/submodules/reset_password.rb +21 -39
  40. data/lib/sorcery/model/submodules/user_activation.rb +21 -63
  41. data/lib/sorcery/model/temporary_token.rb +4 -4
  42. data/lib/sorcery/providers/base.rb +11 -0
  43. data/lib/sorcery/providers/facebook.rb +1 -1
  44. data/lib/sorcery/providers/github.rb +1 -1
  45. data/lib/sorcery/providers/google.rb +1 -1
  46. data/lib/sorcery/providers/heroku.rb +57 -0
  47. data/lib/sorcery/providers/jira.rb +77 -0
  48. data/lib/sorcery/providers/linkedin.rb +1 -1
  49. data/lib/sorcery/providers/liveid.rb +1 -1
  50. data/lib/sorcery/providers/salesforce.rb +50 -0
  51. data/lib/sorcery/providers/twitter.rb +1 -1
  52. data/lib/sorcery/providers/vk.rb +6 -4
  53. data/lib/sorcery/providers/xing.rb +1 -1
  54. data/lib/sorcery/test_helpers/internal.rb +7 -3
  55. data/lib/sorcery/test_helpers/rails/controller.rb +5 -1
  56. data/lib/sorcery/version.rb +3 -0
  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 +128 -56
  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/{datamapper → data_mapper}/authentication.rb +0 -0
  81. data/spec/rails_app/app/{datamapper → data_mapper}/user.rb +0 -0
  82. data/spec/rails_app/app/mongo_mapper/user.rb +2 -0
  83. data/spec/rails_app/config/routes.rb +9 -0
  84. data/spec/rails_app/db/migrate/core/20101224223620_create_users.rb +2 -2
  85. data/spec/shared_examples/user_activation_shared_examples.rb +7 -7
  86. data/spec/shared_examples/user_activity_logging_shared_examples.rb +73 -5
  87. data/spec/shared_examples/user_brute_force_protection_shared_examples.rb +127 -9
  88. data/spec/shared_examples/user_oauth_shared_examples.rb +3 -6
  89. data/spec/shared_examples/user_remember_me_shared_examples.rb +6 -3
  90. data/spec/shared_examples/user_reset_password_shared_examples.rb +10 -10
  91. data/spec/shared_examples/user_shared_examples.rb +117 -30
  92. data/spec/spec_helper.rb +7 -22
  93. metadata +36 -58
  94. data/Gemfile.rails4 +0 -22
  95. data/VERSION +0 -1
  96. data/lib/sorcery/model/adapters/active_record.rb +0 -54
  97. data/lib/sorcery/model/adapters/datamapper.rb +0 -123
  98. data/lib/sorcery/model/adapters/mongo_mapper.rb +0 -60
  99. data/lib/sorcery/model/adapters/mongoid.rb +0 -88
  100. data/lib/sorcery/test_helpers/rails.rb +0 -7
  101. data/spec/active_record/controller_activity_logging_spec.rb +0 -29
  102. data/spec/active_record/controller_brute_force_protection_spec.rb +0 -158
  103. data/spec/active_record/controller_spec.rb +0 -8
  104. data/spec/active_record/integration_spec.rb +0 -23
  105. data/spec/datamapper/controller_activity_logging_spec.rb +0 -17
  106. data/spec/datamapper/controller_spec.rb +0 -8
  107. data/spec/datamapper/user_activity_logging_spec.rb +0 -9
  108. data/spec/mongo_mapper/controller_spec.rb +0 -8
  109. data/spec/mongoid/controller_activity_logging_spec.rb +0 -16
  110. data/spec/mongoid/controller_spec.rb +0 -8
  111. data/spec/rails_app/public/404.html +0 -26
  112. data/spec/rails_app/public/422.html +0 -26
  113. data/spec/rails_app/public/500.html +0 -26
  114. data/spec/rails_app/public/favicon.ico +0 -0
  115. data/spec/rails_app/public/images/rails.png +0 -0
  116. data/spec/rails_app/public/javascripts/application.js +0 -2
  117. data/spec/rails_app/public/javascripts/controls.js +0 -965
  118. data/spec/rails_app/public/javascripts/dragdrop.js +0 -974
  119. data/spec/rails_app/public/javascripts/effects.js +0 -1123
  120. data/spec/rails_app/public/javascripts/prototype.js +0 -6001
  121. data/spec/rails_app/public/javascripts/rails.js +0 -175
  122. data/spec/rails_app/public/robots.txt +0 -5
  123. data/spec/rails_app/public/stylesheets/.gitkeep +0 -0
  124. data/spec/shared_examples/controller_activity_logging_shared_examples.rb +0 -125
  125. data/spec/shared_examples/controller_oauth2_shared_examples.rb +0 -52
  126. data/spec/shared_examples/controller_oauth_shared_examples.rb +0 -62
@@ -50,13 +50,7 @@ module Sorcery
50
50
  base.extend(ClassMethods)
51
51
 
52
52
  base.sorcery_config.after_config << :validate_mailer_defined
53
- base.sorcery_config.after_config << :define_reset_password_mongoid_fields if defined?(Mongoid) and base.ancestors.include?(Mongoid::Document)
54
- if defined?(MongoMapper) and base.ancestors.include?(MongoMapper::Document)
55
- base.sorcery_config.after_config << :define_reset_password_mongo_mapper_fields
56
- end
57
- if defined?(DataMapper) and base.ancestors.include?(DataMapper::Resource)
58
- base.sorcery_config.after_config << :define_reset_password_datamapper_fields
59
- end
53
+ base.sorcery_config.after_config << :define_reset_password_fields
60
54
 
61
55
  base.send(:include, InstanceMethods)
62
56
 
@@ -80,45 +74,33 @@ module Sorcery
80
74
  raise ArgumentError, msg if @sorcery_config.reset_password_mailer == nil and @sorcery_config.reset_password_mailer_disabled == false
81
75
  end
82
76
 
83
- def define_reset_password_mongoid_fields
84
- field sorcery_config.reset_password_token_attribute_name, :type => String
85
- field sorcery_config.reset_password_token_expires_at_attribute_name, :type => Time
86
- field sorcery_config.reset_password_email_sent_at_attribute_name, :type => Time
77
+ def define_reset_password_fields
78
+ sorcery_adapter.define_field sorcery_config.reset_password_token_attribute_name, String
79
+ sorcery_adapter.define_field sorcery_config.reset_password_token_expires_at_attribute_name, Time
80
+ sorcery_adapter.define_field sorcery_config.reset_password_email_sent_at_attribute_name, Time
87
81
  end
88
82
 
89
- def define_reset_password_mongo_mapper_fields
90
- key sorcery_config.reset_password_token_attribute_name, String
91
- key sorcery_config.reset_password_token_expires_at_attribute_name, Time
92
- key sorcery_config.reset_password_email_sent_at_attribute_name, Time
93
- end
94
-
95
- def define_reset_password_datamapper_fields
96
- property sorcery_config.reset_password_token_attribute_name, String
97
- property sorcery_config.reset_password_token_expires_at_attribute_name, Time
98
- property sorcery_config.reset_password_email_sent_at_attribute_name, Time
99
- [sorcery_config.reset_password_token_expires_at_attribute_name,
100
- sorcery_config.reset_password_email_sent_at_attribute_name].each do |sym|
101
- alias_method "orig_#{sym}", sym
102
- define_method(sym) do
103
- t = send("orig_#{sym}")
104
- t && Time.new(t.year, t.month, t.day, t.hour, t.min, t.sec, 0)
105
- end
106
- end
107
- end
108
83
  end
109
84
 
110
85
  module InstanceMethods
111
- # generates a reset code with expiration and sends an email to the user.
112
- def deliver_reset_password_instructions!
86
+ # generates a reset code with expiration
87
+ def generate_reset_password_token!
113
88
  config = sorcery_config
114
- # hammering protection
115
- return false if config.reset_password_time_between_emails && self.send(config.reset_password_email_sent_at_attribute_name) && self.send(config.reset_password_email_sent_at_attribute_name) > config.reset_password_time_between_emails.ago.utc
116
89
  attributes = {config.reset_password_token_attribute_name => TemporaryToken.generate_random_token,
117
90
  config.reset_password_email_sent_at_attribute_name => Time.now.in_time_zone}
118
91
  attributes[config.reset_password_token_expires_at_attribute_name] = Time.now.in_time_zone + config.reset_password_expiration_period if config.reset_password_expiration_period
119
- self.class.transaction do
120
- self.update_many_attributes(attributes)
121
- send_reset_password_email! unless sorcery_config.reset_password_mailer_disabled
92
+
93
+ self.sorcery_adapter.update_attributes(attributes)
94
+ end
95
+
96
+ # generates a reset code with expiration and sends an email to the user.
97
+ def deliver_reset_password_instructions!
98
+ config = sorcery_config
99
+ # hammering protection
100
+ return false if config.reset_password_time_between_emails.present? && self.send(config.reset_password_email_sent_at_attribute_name) && self.send(config.reset_password_email_sent_at_attribute_name) > config.reset_password_time_between_emails.seconds.ago.utc
101
+ self.class.sorcery_adapter.transaction do
102
+ generate_reset_password_token!
103
+ send_reset_password_email! unless config.reset_password_mailer_disabled
122
104
  end
123
105
  end
124
106
 
@@ -126,11 +108,11 @@ module Sorcery
126
108
  def change_password!(new_password)
127
109
  clear_reset_password_token
128
110
  self.send(:"#{sorcery_config.password_attribute_name}=", new_password)
129
- sorcery_save
111
+ sorcery_adapter.save
130
112
  end
131
113
 
132
114
  protected
133
-
115
+
134
116
  def send_reset_password_email!
135
117
  generic_send_email(:reset_password_email_method_name, :reset_password_mailer)
136
118
  end
@@ -15,7 +15,7 @@ module Sorcery
15
15
  # (sent by email).
16
16
 
17
17
  :activation_token_expires_at_attribute_name, # the attribute name to hold activation code
18
- # expiration date.
18
+ # expiration date.
19
19
 
20
20
  :activation_token_expiration_period, # how many seconds before the activation code
21
21
  # expires. nil for never expires.
@@ -51,33 +51,14 @@ module Sorcery
51
51
  end
52
52
 
53
53
  base.class_eval do
54
- if defined?(DataMapper) && self.ancestors.include?(DataMapper::Resource)
55
- before :valid? do
56
- if self.send(sorcery_config.password_attribute_name).present?
57
- setup_activation
58
- end
59
- end
60
- after :create do
61
- if send_activation_needed_email?
62
- send_activation_needed_email!
63
- end
64
- end
65
- else
66
- # don't setup activation if no password supplied - this user is created automatically
67
- before_create :setup_activation, :if => Proc.new { |user| user.send(sorcery_config.password_attribute_name).present? }
68
- # don't send activation needed email if no crypted password created - this user is external (OAuth etc.)
69
- after_create :send_activation_needed_email!, :if => :send_activation_needed_email?
70
- end
54
+ # don't setup activation if no password supplied - this user is created automatically
55
+ sorcery_adapter.define_callback :before, :create, :setup_activation, :if => Proc.new { |user| user.send(sorcery_config.password_attribute_name).present? }
56
+ # don't send activation needed email if no crypted password created - this user is external (OAuth etc.)
57
+ sorcery_adapter.define_callback :after, :create, :send_activation_needed_email!, :if => :send_activation_needed_email?
71
58
  end
72
59
 
73
60
  base.sorcery_config.after_config << :validate_mailer_defined
74
- base.sorcery_config.after_config << :define_user_activation_mongoid_fields if defined?(Mongoid) and base.ancestors.include?(Mongoid::Document)
75
- if defined?(MongoMapper) and base.ancestors.include?(MongoMapper::Document)
76
- base.sorcery_config.after_config << :define_user_activation_mongo_mapper_fields
77
- end
78
- if defined?(DataMapper) and base.ancestors.include?(DataMapper::Resource)
79
- base.sorcery_config.after_config << :define_user_activation_datamapper_fields
80
- end
61
+ base.sorcery_config.after_config << :define_user_activation_fields
81
62
  base.sorcery_config.before_authenticate << :prevent_non_active_login
82
63
 
83
64
  base.extend(ClassMethods)
@@ -104,58 +85,35 @@ module Sorcery
104
85
  raise ArgumentError, msg if @sorcery_config.user_activation_mailer == nil and @sorcery_config.activation_mailer_disabled == false
105
86
  end
106
87
 
107
- def define_user_activation_mongoid_fields
108
- self.class_eval do
109
- field sorcery_config.activation_state_attribute_name, :type => String
110
- field sorcery_config.activation_token_attribute_name, :type => String
111
- field sorcery_config.activation_token_expires_at_attribute_name, :type => Time
112
- end
113
- end
114
-
115
- def define_user_activation_mongo_mapper_fields
88
+ def define_user_activation_fields
116
89
  self.class_eval do
117
- key sorcery_config.activation_state_attribute_name, String
118
- key sorcery_config.activation_token_attribute_name, String
119
- key sorcery_config.activation_token_expires_at_attribute_name, Time
120
- end
121
- end
122
-
123
- def define_user_activation_datamapper_fields
124
- self.class_eval do
125
- property sorcery_config.activation_state_attribute_name, String
126
- property sorcery_config.activation_token_attribute_name, String
127
- property sorcery_config.activation_token_expires_at_attribute_name, Time
128
- [sorcery_config.activation_token_expires_at_attribute_name].each do |sym|
129
- alias_method "orig_#{sym}", sym
130
- define_method(sym) do
131
- t = send("orig_#{sym}")
132
- t && Time.new(t.year, t.month, t.day, t.hour, t.min, t.sec, 0)
133
- end
134
- end
90
+ sorcery_adapter.define_field sorcery_config.activation_state_attribute_name, String
91
+ sorcery_adapter.define_field sorcery_config.activation_token_attribute_name, String
92
+ sorcery_adapter.define_field sorcery_config.activation_token_expires_at_attribute_name, Time
135
93
  end
136
94
  end
137
95
  end
138
96
 
139
97
  module InstanceMethods
98
+ def setup_activation
99
+ config = sorcery_config
100
+ generated_activation_token = TemporaryToken.generate_random_token
101
+ self.send(:"#{config.activation_token_attribute_name}=", generated_activation_token)
102
+ self.send(:"#{config.activation_state_attribute_name}=", "pending")
103
+ self.send(:"#{config.activation_token_expires_at_attribute_name}=", Time.now.in_time_zone + config.activation_token_expiration_period) if config.activation_token_expiration_period
104
+ end
105
+
140
106
  # clears activation code, sets the user as 'active' and optionaly sends a success email.
141
107
  def activate!
142
108
  config = sorcery_config
143
109
  self.send(:"#{config.activation_token_attribute_name}=", nil)
144
110
  self.send(:"#{config.activation_state_attribute_name}=", "active")
145
111
  send_activation_success_email! if send_activation_success_email?
146
- sorcery_save(:validate => false, :raise_on_failure => true)
112
+ sorcery_adapter.save(:validate => false, :raise_on_failure => true)
147
113
  end
148
114
 
149
115
  protected
150
116
 
151
- def setup_activation
152
- config = sorcery_config
153
- generated_activation_token = TemporaryToken.generate_random_token
154
- self.send(:"#{config.activation_token_attribute_name}=", generated_activation_token)
155
- self.send(:"#{config.activation_state_attribute_name}=", "pending")
156
- self.send(:"#{config.activation_token_expires_at_attribute_name}=", Time.now.in_time_zone + config.activation_token_expiration_period) if config.activation_token_expiration_period
157
- end
158
-
159
117
  # called automatically after user initial creation.
160
118
  def send_activation_needed_email!
161
119
  generic_send_email(:activation_needed_email_method_name, :user_activation_mailer)
@@ -164,14 +122,14 @@ module Sorcery
164
122
  def send_activation_success_email!
165
123
  generic_send_email(:activation_success_email_method_name, :user_activation_mailer)
166
124
  end
167
-
125
+
168
126
  def send_activation_success_email?
169
127
  !external? && (
170
128
  !(sorcery_config.activation_success_email_method_name.nil? ||
171
129
  sorcery_config.activation_mailer_disabled == true)
172
130
  )
173
131
  end
174
-
132
+
175
133
  def send_activation_needed_email?
176
134
  !external? && (
177
135
  !(sorcery_config.activation_needed_email_method_name.nil? ||
@@ -3,22 +3,22 @@ require 'securerandom'
3
3
  module Sorcery
4
4
  module Model
5
5
  # This module encapsulates the logic for temporary token.
6
- # A temporary token is created to identify a user in scenarios
6
+ # A temporary token is created to identify a user in scenarios
7
7
  # such as reseting password and activating the user by email.
8
8
  module TemporaryToken
9
9
  def self.included(base)
10
10
  base.extend(ClassMethods)
11
11
  end
12
-
12
+
13
13
  # Random code, used for salt and temporary tokens.
14
14
  def self.generate_random_token
15
15
  SecureRandom.base64(15).tr('+/=lIO0', 'pqrsxyz')
16
16
  end
17
-
17
+
18
18
  module ClassMethods
19
19
  def load_from_token(token, token_attr_name, token_expiration_date_attr)
20
20
  return nil if token.blank?
21
- user = find_by_sorcery_token(token_attr_name,token)
21
+ user = sorcery_adapter.find_by_token(token_attr_name,token)
22
22
  if !user.blank? && !user.send(token_expiration_date_attr).nil?
23
23
  return Time.now.in_time_zone < user.send(token_expiration_date_attr) ? user : nil
24
24
  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
@@ -30,7 +30,7 @@ module Sorcery
30
30
  def get_user_hash(access_token)
31
31
  response = access_token.get(user_info_path)
32
32
 
33
- {}.tap do |h|
33
+ auth_hash(access_token).tap do |h|
34
34
  h[:user_info] = JSON.parse(response.body)
35
35
  h[:uid] = h[:user_info]['id']
36
36
  end
@@ -25,7 +25,7 @@ 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|
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
@@ -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