devise_token_auth_multi_email 0.9.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 (183) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +13 -0
  3. data/README.md +97 -0
  4. data/Rakefile +42 -0
  5. data/app/controllers/devise_token_auth/application_controller.rb +100 -0
  6. data/app/controllers/devise_token_auth/concerns/resource_finder.rb +68 -0
  7. data/app/controllers/devise_token_auth/concerns/set_user_by_token.rb +199 -0
  8. data/app/controllers/devise_token_auth/confirmations_controller.rb +89 -0
  9. data/app/controllers/devise_token_auth/omniauth_callbacks_controller.rb +284 -0
  10. data/app/controllers/devise_token_auth/passwords_controller.rb +216 -0
  11. data/app/controllers/devise_token_auth/registrations_controller.rb +205 -0
  12. data/app/controllers/devise_token_auth/sessions_controller.rb +153 -0
  13. data/app/controllers/devise_token_auth/token_validations_controller.rb +31 -0
  14. data/app/controllers/devise_token_auth/unlocks_controller.rb +94 -0
  15. data/app/models/devise_token_auth/concerns/active_record_support.rb +18 -0
  16. data/app/models/devise_token_auth/concerns/confirmable_support.rb +28 -0
  17. data/app/models/devise_token_auth/concerns/mongoid_support.rb +19 -0
  18. data/app/models/devise_token_auth/concerns/tokens_serialization.rb +31 -0
  19. data/app/models/devise_token_auth/concerns/user.rb +282 -0
  20. data/app/models/devise_token_auth/concerns/user_omniauth_callbacks.rb +39 -0
  21. data/app/validators/devise_token_auth_email_validator.rb +31 -0
  22. data/app/views/devise/mailer/confirmation_instructions.html.erb +5 -0
  23. data/app/views/devise/mailer/reset_password_instructions.html.erb +8 -0
  24. data/app/views/devise/mailer/unlock_instructions.html.erb +7 -0
  25. data/app/views/devise_token_auth/omniauth_external_window.html.erb +38 -0
  26. data/config/locales/da-DK.yml +52 -0
  27. data/config/locales/de.yml +51 -0
  28. data/config/locales/en.yml +60 -0
  29. data/config/locales/es.yml +51 -0
  30. data/config/locales/fa.yml +60 -0
  31. data/config/locales/fr.yml +51 -0
  32. data/config/locales/he.yml +52 -0
  33. data/config/locales/it.yml +48 -0
  34. data/config/locales/ja.yml +60 -0
  35. data/config/locales/ko.yml +51 -0
  36. data/config/locales/nl.yml +32 -0
  37. data/config/locales/pl.yml +51 -0
  38. data/config/locales/pt-BR.yml +48 -0
  39. data/config/locales/pt.yml +51 -0
  40. data/config/locales/ro.yml +48 -0
  41. data/config/locales/ru.yml +52 -0
  42. data/config/locales/sq.yml +48 -0
  43. data/config/locales/sv.yml +52 -0
  44. data/config/locales/uk.yml +61 -0
  45. data/config/locales/vi.yml +52 -0
  46. data/config/locales/zh-CN.yml +48 -0
  47. data/config/locales/zh-HK.yml +50 -0
  48. data/config/locales/zh-TW.yml +50 -0
  49. data/lib/devise_token_auth/blacklist.rb +6 -0
  50. data/lib/devise_token_auth/controllers/helpers.rb +157 -0
  51. data/lib/devise_token_auth/controllers/url_helpers.rb +10 -0
  52. data/lib/devise_token_auth/engine.rb +105 -0
  53. data/lib/devise_token_auth/errors.rb +8 -0
  54. data/lib/devise_token_auth/rails/routes.rb +122 -0
  55. data/lib/devise_token_auth/token_factory.rb +126 -0
  56. data/lib/devise_token_auth/url.rb +44 -0
  57. data/lib/devise_token_auth/version.rb +5 -0
  58. data/lib/devise_token_auth.rb +14 -0
  59. data/lib/generators/devise_token_auth/USAGE +31 -0
  60. data/lib/generators/devise_token_auth/install_generator.rb +91 -0
  61. data/lib/generators/devise_token_auth/install_generator_helpers.rb +98 -0
  62. data/lib/generators/devise_token_auth/install_mongoid_generator.rb +46 -0
  63. data/lib/generators/devise_token_auth/install_views_generator.rb +18 -0
  64. data/lib/generators/devise_token_auth/templates/devise_token_auth.rb +66 -0
  65. data/lib/generators/devise_token_auth/templates/devise_token_auth_create_users.rb.erb +49 -0
  66. data/lib/generators/devise_token_auth/templates/user.rb.erb +9 -0
  67. data/lib/generators/devise_token_auth/templates/user_mongoid.rb.erb +56 -0
  68. data/lib/tasks/devise_token_auth_tasks.rake +6 -0
  69. data/test/controllers/custom/custom_confirmations_controller_test.rb +25 -0
  70. data/test/controllers/custom/custom_omniauth_callbacks_controller_test.rb +33 -0
  71. data/test/controllers/custom/custom_passwords_controller_test.rb +79 -0
  72. data/test/controllers/custom/custom_registrations_controller_test.rb +63 -0
  73. data/test/controllers/custom/custom_sessions_controller_test.rb +39 -0
  74. data/test/controllers/custom/custom_token_validations_controller_test.rb +42 -0
  75. data/test/controllers/demo_group_controller_test.rb +151 -0
  76. data/test/controllers/demo_mang_controller_test.rb +313 -0
  77. data/test/controllers/demo_user_controller_test.rb +658 -0
  78. data/test/controllers/devise_token_auth/confirmations_controller_test.rb +275 -0
  79. data/test/controllers/devise_token_auth/omniauth_callbacks_controller_test.rb +438 -0
  80. data/test/controllers/devise_token_auth/passwords_controller_test.rb +893 -0
  81. data/test/controllers/devise_token_auth/registrations_controller_test.rb +920 -0
  82. data/test/controllers/devise_token_auth/sessions_controller_test.rb +605 -0
  83. data/test/controllers/devise_token_auth/token_validations_controller_test.rb +142 -0
  84. data/test/controllers/devise_token_auth/unlocks_controller_test.rb +235 -0
  85. data/test/controllers/overrides/confirmations_controller_test.rb +47 -0
  86. data/test/controllers/overrides/omniauth_callbacks_controller_test.rb +53 -0
  87. data/test/controllers/overrides/passwords_controller_test.rb +64 -0
  88. data/test/controllers/overrides/registrations_controller_test.rb +46 -0
  89. data/test/controllers/overrides/sessions_controller_test.rb +35 -0
  90. data/test/controllers/overrides/token_validations_controller_test.rb +43 -0
  91. data/test/dummy/README.rdoc +28 -0
  92. data/test/dummy/app/active_record/confirmable_user.rb +11 -0
  93. data/test/dummy/app/active_record/lockable_user.rb +7 -0
  94. data/test/dummy/app/active_record/mang.rb +5 -0
  95. data/test/dummy/app/active_record/only_email_user.rb +7 -0
  96. data/test/dummy/app/active_record/scoped_user.rb +9 -0
  97. data/test/dummy/app/active_record/unconfirmable_user.rb +9 -0
  98. data/test/dummy/app/active_record/unregisterable_user.rb +9 -0
  99. data/test/dummy/app/active_record/user.rb +6 -0
  100. data/test/dummy/app/controllers/application_controller.rb +14 -0
  101. data/test/dummy/app/controllers/auth_origin_controller.rb +7 -0
  102. data/test/dummy/app/controllers/custom/confirmations_controller.rb +13 -0
  103. data/test/dummy/app/controllers/custom/omniauth_callbacks_controller.rb +13 -0
  104. data/test/dummy/app/controllers/custom/passwords_controller.rb +39 -0
  105. data/test/dummy/app/controllers/custom/registrations_controller.rb +39 -0
  106. data/test/dummy/app/controllers/custom/sessions_controller.rb +29 -0
  107. data/test/dummy/app/controllers/custom/token_validations_controller.rb +19 -0
  108. data/test/dummy/app/controllers/demo_group_controller.rb +15 -0
  109. data/test/dummy/app/controllers/demo_mang_controller.rb +14 -0
  110. data/test/dummy/app/controllers/demo_user_controller.rb +27 -0
  111. data/test/dummy/app/controllers/overrides/confirmations_controller.rb +29 -0
  112. data/test/dummy/app/controllers/overrides/omniauth_callbacks_controller.rb +16 -0
  113. data/test/dummy/app/controllers/overrides/passwords_controller.rb +36 -0
  114. data/test/dummy/app/controllers/overrides/registrations_controller.rb +29 -0
  115. data/test/dummy/app/controllers/overrides/sessions_controller.rb +36 -0
  116. data/test/dummy/app/controllers/overrides/token_validations_controller.rb +23 -0
  117. data/test/dummy/app/helpers/application_helper.rb +1058 -0
  118. data/test/dummy/app/models/concerns/favorite_color.rb +19 -0
  119. data/test/dummy/app/mongoid/confirmable_user.rb +52 -0
  120. data/test/dummy/app/mongoid/lockable_user.rb +38 -0
  121. data/test/dummy/app/mongoid/mang.rb +46 -0
  122. data/test/dummy/app/mongoid/only_email_user.rb +33 -0
  123. data/test/dummy/app/mongoid/scoped_user.rb +50 -0
  124. data/test/dummy/app/mongoid/unconfirmable_user.rb +44 -0
  125. data/test/dummy/app/mongoid/unregisterable_user.rb +47 -0
  126. data/test/dummy/app/mongoid/user.rb +49 -0
  127. data/test/dummy/app/views/layouts/application.html.erb +12 -0
  128. data/test/dummy/config/application.rb +50 -0
  129. data/test/dummy/config/application.yml.bk +0 -0
  130. data/test/dummy/config/boot.rb +11 -0
  131. data/test/dummy/config/environment.rb +7 -0
  132. data/test/dummy/config/environments/development.rb +36 -0
  133. data/test/dummy/config/environments/production.rb +68 -0
  134. data/test/dummy/config/environments/test.rb +58 -0
  135. data/test/dummy/config/initializers/backtrace_silencers.rb +9 -0
  136. data/test/dummy/config/initializers/cookies_serializer.rb +5 -0
  137. data/test/dummy/config/initializers/devise.rb +290 -0
  138. data/test/dummy/config/initializers/devise_token_auth.rb +55 -0
  139. data/test/dummy/config/initializers/figaro.rb +3 -0
  140. data/test/dummy/config/initializers/filter_parameter_logging.rb +6 -0
  141. data/test/dummy/config/initializers/inflections.rb +18 -0
  142. data/test/dummy/config/initializers/mime_types.rb +6 -0
  143. data/test/dummy/config/initializers/omniauth.rb +11 -0
  144. data/test/dummy/config/initializers/session_store.rb +5 -0
  145. data/test/dummy/config/initializers/wrap_parameters.rb +16 -0
  146. data/test/dummy/config/routes.rb +57 -0
  147. data/test/dummy/config/spring.rb +3 -0
  148. data/test/dummy/config.ru +18 -0
  149. data/test/dummy/db/migrate/20140715061447_devise_token_auth_create_users.rb +58 -0
  150. data/test/dummy/db/migrate/20140715061805_devise_token_auth_create_mangs.rb +57 -0
  151. data/test/dummy/db/migrate/20140829044006_add_operating_thetan_to_user.rb +8 -0
  152. data/test/dummy/db/migrate/20140916224624_add_favorite_color_to_mangs.rb +7 -0
  153. data/test/dummy/db/migrate/20141222035835_devise_token_auth_create_only_email_users.rb +55 -0
  154. data/test/dummy/db/migrate/20141222053502_devise_token_auth_create_unregisterable_users.rb +56 -0
  155. data/test/dummy/db/migrate/20150708104536_devise_token_auth_create_unconfirmable_users.rb +56 -0
  156. data/test/dummy/db/migrate/20160103235141_devise_token_auth_create_scoped_users.rb +56 -0
  157. data/test/dummy/db/migrate/20160629184441_devise_token_auth_create_lockable_users.rb +56 -0
  158. data/test/dummy/db/migrate/20190924101113_devise_token_auth_create_confirmable_users.rb +49 -0
  159. data/test/dummy/db/schema.rb +198 -0
  160. data/test/dummy/lib/migration_database_helper.rb +43 -0
  161. data/test/dummy/tmp/generators/app/models/mang.rb +9 -0
  162. data/test/dummy/tmp/generators/app/models/user.rb +9 -0
  163. data/test/dummy/tmp/generators/config/initializers/devise_token_auth.rb +60 -0
  164. data/test/dummy/tmp/generators/config/routes.rb +9 -0
  165. data/test/dummy/tmp/generators/db/migrate/20210305040222_devise_token_auth_create_mangs.rb +49 -0
  166. data/test/dummy/tmp/generators/db/migrate/20210305040222_devise_token_auth_create_users.rb +49 -0
  167. data/test/factories/users.rb +41 -0
  168. data/test/lib/devise_token_auth/blacklist_test.rb +19 -0
  169. data/test/lib/devise_token_auth/rails/custom_routes_test.rb +29 -0
  170. data/test/lib/devise_token_auth/rails/routes_test.rb +87 -0
  171. data/test/lib/devise_token_auth/token_factory_test.rb +191 -0
  172. data/test/lib/devise_token_auth/url_test.rb +26 -0
  173. data/test/lib/generators/devise_token_auth/install_generator_test.rb +217 -0
  174. data/test/lib/generators/devise_token_auth/install_generator_with_namespace_test.rb +222 -0
  175. data/test/lib/generators/devise_token_auth/install_views_generator_test.rb +25 -0
  176. data/test/models/concerns/mongoid_support_test.rb +31 -0
  177. data/test/models/concerns/tokens_serialization_test.rb +104 -0
  178. data/test/models/confirmable_user_test.rb +35 -0
  179. data/test/models/only_email_user_test.rb +29 -0
  180. data/test/models/user_test.rb +224 -0
  181. data/test/support/controllers/routes.rb +43 -0
  182. data/test/test_helper.rb +134 -0
  183. metadata +502 -0
@@ -0,0 +1,284 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DeviseTokenAuth
4
+ class OmniauthCallbacksController < DeviseTokenAuth::ApplicationController
5
+ attr_reader :auth_params
6
+
7
+ before_action :validate_auth_origin_url_param
8
+
9
+ skip_before_action :set_user_by_token, raise: false
10
+ skip_after_action :update_auth_header
11
+
12
+ # intermediary route for successful omniauth authentication. omniauth does
13
+ # not support multiple models, so we must resort to this terrible hack.
14
+ def redirect_callbacks
15
+
16
+ # derive target redirect route from 'resource_class' param, which was set
17
+ # before authentication.
18
+ devise_mapping = get_devise_mapping
19
+ redirect_route = get_redirect_route(devise_mapping)
20
+
21
+ # preserve omniauth info for success route. ignore 'extra' in twitter
22
+ # auth response to avoid CookieOverflow.
23
+ session['dta.omniauth.auth'] = request.env['omniauth.auth'].except('extra')
24
+ session['dta.omniauth.params'] = request.env['omniauth.params']
25
+
26
+ redirect_to redirect_route, {status: 307}.merge(redirect_options)
27
+ end
28
+
29
+ def get_redirect_route(devise_mapping)
30
+ path = "#{Devise.mappings[devise_mapping.to_sym].fullpath}/#{params[:provider]}/callback"
31
+ klass = request.scheme == 'https' ? URI::HTTPS : URI::HTTP
32
+ redirect_route = klass.build(host: request.host, port: request.port, path: path).to_s
33
+ end
34
+
35
+ def get_devise_mapping
36
+ # derive target redirect route from 'resource_class' param, which was set
37
+ # before authentication.
38
+ devise_mapping = [request.env['omniauth.params']['namespace_name'],
39
+ request.env['omniauth.params']['resource_class'].underscore.gsub('/', '_')].compact.join('_')
40
+ rescue NoMethodError => err
41
+ default_devise_mapping
42
+ end
43
+
44
+ # This method will only be called if `get_devise_mapping` cannot
45
+ # find the mapping in `omniauth.params`.
46
+ #
47
+ # One example use-case here is for IDP-initiated SAML login. In that
48
+ # case, there will have been no initial request in which to save
49
+ # the devise mapping. If you are in a situation like that, and
50
+ # your app allows for you to determine somehow what the devise
51
+ # mapping should be (because, for example, it is always the same),
52
+ # then you can handle it by overriding this method.
53
+ def default_devise_mapping
54
+ raise NotImplementedError.new('no default_devise_mapping set')
55
+ end
56
+
57
+ def omniauth_success
58
+ get_resource_from_auth_hash
59
+ set_token_on_resource
60
+ create_auth_params
61
+
62
+ if confirmable_enabled?
63
+ # don't send confirmation email!!!
64
+ @resource.skip_confirmation!
65
+ end
66
+
67
+ sign_in(:user, @resource, store: false, bypass: false)
68
+
69
+ @resource.save!
70
+
71
+ yield @resource if block_given?
72
+
73
+ if DeviseTokenAuth.cookie_enabled
74
+ set_token_in_cookie(@resource, @token)
75
+ end
76
+
77
+ render_data_or_redirect('deliverCredentials', @auth_params.as_json, @resource.as_json)
78
+ end
79
+
80
+ def omniauth_failure
81
+ @error = params[:message]
82
+ render_data_or_redirect('authFailure', error: @error)
83
+ end
84
+
85
+ def validate_auth_origin_url_param
86
+ return render_error_not_allowed_auth_origin_url if auth_origin_url && blacklisted_redirect_url?(auth_origin_url)
87
+ end
88
+
89
+
90
+ protected
91
+
92
+ # this will be determined differently depending on the action that calls
93
+ # it. redirect_callbacks is called upon returning from successful omniauth
94
+ # authentication, and the target params live in an omniauth-specific
95
+ # request.env variable. this variable is then persisted thru the redirect
96
+ # using our own dta.omniauth.params session var. the omniauth_success
97
+ # method will access that session var and then destroy it immediately
98
+ # after use. In the failure case, finally, the omniauth params
99
+ # are added as query params in our monkey patch to OmniAuth in engine.rb
100
+ def omniauth_params
101
+ unless defined?(@_omniauth_params)
102
+ if request.env['omniauth.params'] && request.env['omniauth.params'].any?
103
+ @_omniauth_params = request.env['omniauth.params']
104
+ elsif session['dta.omniauth.params'] && session['dta.omniauth.params'].any?
105
+ @_omniauth_params ||= session.delete('dta.omniauth.params')
106
+ @_omniauth_params
107
+ elsif params['omniauth_window_type']
108
+ @_omniauth_params = params.slice('omniauth_window_type', 'auth_origin_url', 'resource_class', 'origin')
109
+ else
110
+ @_omniauth_params = {}
111
+ end
112
+ end
113
+ @_omniauth_params
114
+ end
115
+
116
+ # break out provider attribute assignment for easy method extension
117
+ def assign_provider_attrs(user, auth_hash)
118
+ attrs = auth_hash['info'].to_hash
119
+ attrs = attrs.slice(*user.attribute_names)
120
+ user.assign_attributes(attrs)
121
+ end
122
+
123
+ # derive allowed params from the standard devise parameter sanitizer
124
+ def whitelisted_params
125
+ whitelist = params_for_resource(:sign_up)
126
+
127
+ whitelist.inject({}) do |coll, key|
128
+ param = omniauth_params[key.to_s]
129
+ coll[key] = param if param
130
+ coll
131
+ end
132
+ end
133
+
134
+ def resource_class(mapping = nil)
135
+ return @resource_class if defined?(@resource_class)
136
+
137
+ constant_name = omniauth_params['resource_class'].presence || params['resource_class'].presence
138
+ @resource_class = ObjectSpace.each_object(Class).detect { |cls| cls.to_s == constant_name && cls.pretty_print_inspect.starts_with?(constant_name) }
139
+ raise 'No resource_class found' if @resource_class.nil?
140
+
141
+ @resource_class
142
+ end
143
+
144
+ def resource_name
145
+ resource_class
146
+ end
147
+
148
+ def unsafe_auth_origin_url
149
+ omniauth_params['auth_origin_url'] || omniauth_params['origin']
150
+ end
151
+
152
+
153
+ def auth_origin_url
154
+ if unsafe_auth_origin_url && blacklisted_redirect_url?(unsafe_auth_origin_url)
155
+ return nil
156
+ end
157
+ return unsafe_auth_origin_url
158
+ end
159
+
160
+ # in the success case, omniauth_window_type is in the omniauth_params.
161
+ # in the failure case, it is in a query param. See monkey patch above
162
+ def omniauth_window_type
163
+ omniauth_params.nil? ? params['omniauth_window_type'] : omniauth_params['omniauth_window_type']
164
+ end
165
+
166
+ # this session value is set by the redirect_callbacks method. its purpose
167
+ # is to persist the omniauth auth hash value thru a redirect. the value
168
+ # must be destroyed immediately after it is accessed by omniauth_success
169
+ def auth_hash
170
+ @_auth_hash ||= session.delete('dta.omniauth.auth')
171
+ end
172
+
173
+ # ensure that this controller responds to :devise_controller? conditionals.
174
+ # this is used primarily for access to the parameter sanitizers.
175
+ def assert_is_devise_resource!
176
+ true
177
+ end
178
+
179
+ def set_random_password
180
+ # set crazy password for new oauth users. this is only used to prevent
181
+ # access via email sign-in.
182
+ p = SecureRandom.urlsafe_base64(nil, false)
183
+ @resource.password = p
184
+ @resource.password_confirmation = p
185
+ end
186
+
187
+ def create_auth_params
188
+ @auth_params = {
189
+ auth_token: @token.token,
190
+ client_id: @token.client,
191
+ uid: @resource.uid,
192
+ expiry: @token.expiry,
193
+ config: @config
194
+ }
195
+ @auth_params.merge!(oauth_registration: true) if @oauth_registration
196
+ @auth_params
197
+ end
198
+
199
+ def set_token_on_resource
200
+ @config = omniauth_params['config_name']
201
+ @token = @resource.create_token
202
+ end
203
+
204
+ def render_error_not_allowed_auth_origin_url
205
+ message = I18n.t('devise_token_auth.omniauth.not_allowed_redirect_url', redirect_url: unsafe_auth_origin_url)
206
+ render_data_or_redirect('authFailure', error: message)
207
+ end
208
+
209
+ def render_data(message, data)
210
+ @data = data.merge(message: ActionController::Base.helpers.sanitize(message))
211
+ render layout: nil, template: 'devise_token_auth/omniauth_external_window'
212
+ end
213
+
214
+ def render_data_or_redirect(message, data, user_data = {})
215
+
216
+ # We handle inAppBrowser and newWindow the same, but it is nice
217
+ # to support values in case people need custom implementations for each case
218
+ # (For example, nbrustein does not allow new users to be created if logging in with
219
+ # an inAppBrowser)
220
+ #
221
+ # See app/views/devise_token_auth/omniauth_external_window.html.erb to understand
222
+ # why we can handle these both the same. The view is setup to handle both cases
223
+ # at the same time.
224
+ if ['inAppBrowser', 'newWindow'].include?(omniauth_window_type)
225
+ render_data(message, user_data.merge(data))
226
+
227
+ elsif auth_origin_url # default to same-window implementation, which forwards back to auth_origin_url
228
+
229
+ # build and redirect to destination url
230
+ redirect_to DeviseTokenAuth::Url.generate(auth_origin_url, data.merge(blank: true).merge(redirect_options))
231
+ else
232
+
233
+ # there SHOULD always be an auth_origin_url, but if someone does something silly
234
+ # like coming straight to this url or refreshing the page at the wrong time, there may not be one.
235
+ # In that case, just render in plain text the error message if there is one or otherwise
236
+ # a generic message.
237
+ fallback_render data[:error] || 'An error occurred'
238
+ end
239
+ end
240
+
241
+ def fallback_render(text)
242
+ render inline: %Q(
243
+
244
+ <html>
245
+ <head></head>
246
+ <body>
247
+ #{ActionController::Base.helpers.sanitize(text)}
248
+ </body>
249
+ </html>)
250
+ end
251
+
252
+ def handle_new_resource
253
+ @oauth_registration = true
254
+ set_random_password
255
+ end
256
+
257
+ def assign_whitelisted_params?
258
+ true
259
+ end
260
+
261
+ def get_resource_from_auth_hash
262
+ # find or create user by provider and provider uid
263
+ @resource = resource_class.where(
264
+ uid: auth_hash['uid'],
265
+ provider: auth_hash['provider']
266
+ ).first_or_initialize
267
+
268
+ if @resource.new_record?
269
+ handle_new_resource
270
+ end
271
+
272
+ # sync user info with provider, update/generate auth token
273
+ assign_provider_attrs(@resource, auth_hash)
274
+
275
+ # assign any additional (whitelisted) attributes
276
+ if assign_whitelisted_params?
277
+ extra_params = whitelisted_params
278
+ @resource.assign_attributes(extra_params) if extra_params
279
+ end
280
+
281
+ @resource
282
+ end
283
+ end
284
+ end
@@ -0,0 +1,216 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DeviseTokenAuth
4
+ class PasswordsController < DeviseTokenAuth::ApplicationController
5
+ before_action :validate_redirect_url_param, only: [:create, :edit]
6
+ skip_after_action :update_auth_header, only: [:create, :edit]
7
+
8
+ # this action is responsible for generating password reset tokens and sending emails
9
+ def create
10
+ return render_create_error_missing_email unless resource_params[:email]
11
+
12
+ @email = get_case_insensitive_field_from_resource_params(:email)
13
+ @resource = find_resource(:uid, @email)
14
+
15
+ if @resource
16
+ yield @resource if block_given?
17
+ @resource.send_reset_password_instructions(
18
+ email: @email,
19
+ provider: 'email',
20
+ redirect_url: @redirect_url,
21
+ client_config: params[:config_name]
22
+ )
23
+
24
+ if @resource.errors.empty?
25
+ return render_create_success
26
+ else
27
+ render_create_error @resource.errors
28
+ end
29
+ else
30
+ render_not_found_error
31
+ end
32
+ end
33
+
34
+ # this is where users arrive after visiting the password reset confirmation link
35
+ def edit
36
+ # if a user is not found, return nil
37
+ @resource = resource_class.with_reset_password_token(resource_params[:reset_password_token])
38
+
39
+ if @resource && @resource.reset_password_period_valid?
40
+ token = @resource.create_token unless require_client_password_reset_token?
41
+
42
+ # ensure that user is confirmed
43
+ @resource.skip_confirmation! if confirmable_enabled? && !@resource.confirmed_at
44
+ # allow user to change password once without current_password
45
+ @resource.allow_password_change = true if recoverable_enabled?
46
+
47
+ @resource.save!
48
+
49
+ yield @resource if block_given?
50
+
51
+ if require_client_password_reset_token?
52
+ redirect_to DeviseTokenAuth::Url.generate(@redirect_url, reset_password_token: resource_params[:reset_password_token]),
53
+ redirect_options
54
+ else
55
+ if DeviseTokenAuth.cookie_enabled
56
+ set_token_in_cookie(@resource, token)
57
+ end
58
+
59
+ redirect_header_options = { reset_password: true, reset_password_token: resource_params[:reset_password_token] }
60
+ redirect_headers = build_redirect_headers(token.token,
61
+ token.client,
62
+ redirect_header_options)
63
+ redirect_to(@resource.build_auth_url(@redirect_url,
64
+ redirect_headers),
65
+ redirect_options)
66
+ end
67
+ else
68
+ render_edit_error
69
+ end
70
+ end
71
+
72
+ def update
73
+ # make sure user is authorized
74
+ if require_client_password_reset_token? && resource_params[:reset_password_token]
75
+ @resource = resource_class.with_reset_password_token(resource_params[:reset_password_token])
76
+ return render_update_error_unauthorized unless @resource && @resource.reset_password_period_valid?
77
+
78
+ @token = @resource.create_token
79
+ else
80
+ @resource = set_user_by_token
81
+ end
82
+
83
+ return render_update_error_unauthorized unless @resource
84
+
85
+ # make sure account doesn't use oauth2 provider
86
+ unless @resource.provider == 'email'
87
+ return render_update_error_password_not_required
88
+ end
89
+
90
+ # ensure that password params were sent
91
+ unless password_resource_params[:password] && password_resource_params[:password_confirmation]
92
+ return render_update_error_missing_password
93
+ end
94
+
95
+ if @resource.send(resource_update_method, password_resource_params)
96
+ @resource.allow_password_change = false if recoverable_enabled?
97
+ @resource.save!
98
+
99
+ yield @resource if block_given?
100
+ return render_update_success
101
+ else
102
+ return render_update_error
103
+ end
104
+ end
105
+
106
+ protected
107
+
108
+ def resource_update_method
109
+ allow_password_change = recoverable_enabled? && @resource.allow_password_change == true || require_client_password_reset_token?
110
+ if DeviseTokenAuth.check_current_password_before_update == false || allow_password_change
111
+ 'update'
112
+ else
113
+ 'update_with_password'
114
+ end
115
+ end
116
+
117
+ def render_create_error_missing_email
118
+ render_error(401, I18n.t('devise_token_auth.passwords.missing_email'))
119
+ end
120
+
121
+ def render_create_error_missing_redirect_url
122
+ render_error(401, I18n.t('devise_token_auth.passwords.missing_redirect_url'))
123
+ end
124
+
125
+ def render_error_not_allowed_redirect_url
126
+ response = {
127
+ status: 'error',
128
+ data: resource_data
129
+ }
130
+ message = I18n.t('devise_token_auth.passwords.not_allowed_redirect_url', redirect_url: @redirect_url)
131
+ render_error(422, message, response)
132
+ end
133
+
134
+ def render_create_success
135
+ render json: {
136
+ success: true,
137
+ message: success_message('passwords', @email)
138
+ }
139
+ end
140
+
141
+ def render_create_error(errors)
142
+ render json: {
143
+ success: false,
144
+ errors: errors
145
+ }, status: 400
146
+ end
147
+
148
+ def render_edit_error
149
+ raise ActionController::RoutingError, 'Not Found'
150
+ end
151
+
152
+ def render_update_error_unauthorized
153
+ render_error(401, 'Unauthorized')
154
+ end
155
+
156
+ def render_update_error_password_not_required
157
+ render_error(422, I18n.t('devise_token_auth.passwords.password_not_required', provider: @resource.provider.humanize))
158
+ end
159
+
160
+ def render_update_error_missing_password
161
+ render_error(422, I18n.t('devise_token_auth.passwords.missing_passwords'))
162
+ end
163
+
164
+ def render_update_success
165
+ render json: {
166
+ success: true,
167
+ data: resource_data,
168
+ message: I18n.t('devise_token_auth.passwords.successfully_updated')
169
+ }
170
+ end
171
+
172
+ def render_update_error
173
+ render json: {
174
+ success: false,
175
+ errors: resource_errors
176
+ }, status: 422
177
+ end
178
+
179
+ private
180
+
181
+ def resource_params
182
+ params.permit(:email, :reset_password_token)
183
+ end
184
+
185
+ def password_resource_params
186
+ params.permit(*params_for_resource(:account_update))
187
+ end
188
+
189
+ def render_not_found_error
190
+ if Devise.paranoid
191
+ render_create_success
192
+ else
193
+ render_error(404, I18n.t('devise_token_auth.passwords.user_not_found', email: @email))
194
+ end
195
+ end
196
+
197
+ def validate_redirect_url_param
198
+ # give redirect value from params priority
199
+ @redirect_url = params.fetch(
200
+ :redirect_url,
201
+ DeviseTokenAuth.default_password_reset_url
202
+ )
203
+
204
+ return render_create_error_missing_redirect_url unless @redirect_url
205
+ return render_error_not_allowed_redirect_url if blacklisted_redirect_url?(@redirect_url)
206
+ end
207
+
208
+ def reset_password_token_as_raw?(recoverable)
209
+ recoverable && recoverable.reset_password_token.present? && !require_client_password_reset_token?
210
+ end
211
+
212
+ def require_client_password_reset_token?
213
+ DeviseTokenAuth.require_client_password_reset_token
214
+ end
215
+ end
216
+ end
@@ -0,0 +1,205 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DeviseTokenAuth
4
+ class RegistrationsController < DeviseTokenAuth::ApplicationController
5
+ before_action :set_user_by_token, only: [:destroy, :update]
6
+ before_action :validate_sign_up_params, only: :create
7
+ before_action :validate_account_update_params, only: :update
8
+ skip_after_action :update_auth_header, only: [:create, :destroy]
9
+
10
+ def create
11
+ build_resource
12
+
13
+ unless @resource.present?
14
+ raise DeviseTokenAuth::Errors::NoResourceDefinedError,
15
+ "#{self.class.name} #build_resource does not define @resource,"\
16
+ ' execution stopped.'
17
+ end
18
+
19
+ # give redirect value from params priority
20
+ @redirect_url = params.fetch(
21
+ :confirm_success_url,
22
+ DeviseTokenAuth.default_confirm_success_url
23
+ )
24
+
25
+ # success redirect url is required
26
+ if confirmable_enabled? && !@redirect_url
27
+ return render_create_error_missing_confirm_success_url
28
+ end
29
+
30
+ # if whitelist is set, validate redirect_url against whitelist
31
+ return render_create_error_redirect_url_not_allowed if blacklisted_redirect_url?(@redirect_url)
32
+
33
+ # override email confirmation, must be sent manually from ctrl
34
+ callback_name = defined?(ActiveRecord) && resource_class < ActiveRecord::Base ? :commit : :create
35
+ resource_class.set_callback(callback_name, :after, :send_on_create_confirmation_instructions)
36
+ resource_class.skip_callback(callback_name, :after, :send_on_create_confirmation_instructions)
37
+
38
+ if @resource.respond_to? :skip_confirmation_notification!
39
+ # Fix duplicate e-mails by disabling Devise confirmation e-mail
40
+ @resource.skip_confirmation_notification!
41
+ end
42
+
43
+ if @resource.save
44
+ yield @resource if block_given?
45
+
46
+ unless @resource.confirmed?
47
+ # user will require email authentication
48
+ @resource.send_confirmation_instructions({
49
+ client_config: params[:config_name],
50
+ redirect_url: @redirect_url
51
+ })
52
+ end
53
+
54
+ if active_for_authentication?
55
+ # email auth has been bypassed, authenticate user
56
+ @token = @resource.create_token
57
+ @resource.save!
58
+ update_auth_header
59
+ end
60
+
61
+ render_create_success
62
+ else
63
+ clean_up_passwords @resource
64
+ render_create_error
65
+ end
66
+ end
67
+
68
+ def update
69
+ if @resource
70
+ if @resource.send(resource_update_method, account_update_params)
71
+ yield @resource if block_given?
72
+ render_update_success
73
+ else
74
+ render_update_error
75
+ end
76
+ else
77
+ render_update_error_user_not_found
78
+ end
79
+ end
80
+
81
+ def destroy
82
+ if @resource
83
+ @resource.destroy
84
+ yield @resource if block_given?
85
+ render_destroy_success
86
+ else
87
+ render_destroy_error
88
+ end
89
+ end
90
+
91
+ def sign_up_params
92
+ params.permit(*params_for_resource(:sign_up))
93
+ end
94
+
95
+ def account_update_params
96
+ params.permit(*params_for_resource(:account_update))
97
+ end
98
+
99
+ protected
100
+
101
+ def build_resource
102
+ @resource = resource_class.new(sign_up_params)
103
+ @resource.provider = provider
104
+
105
+ # honor devise configuration for case_insensitive_keys
106
+ if resource_class.case_insensitive_keys.include?(:email)
107
+ @resource.email = sign_up_params[:email].try(:downcase)
108
+ else
109
+ @resource.email = sign_up_params[:email]
110
+ end
111
+ end
112
+
113
+ def render_create_error_missing_confirm_success_url
114
+ response = {
115
+ status: 'error',
116
+ data: resource_data
117
+ }
118
+ message = I18n.t('devise_token_auth.registrations.missing_confirm_success_url')
119
+ render_error(422, message, response)
120
+ end
121
+
122
+ def render_create_error_redirect_url_not_allowed
123
+ response = {
124
+ status: 'error',
125
+ data: resource_data
126
+ }
127
+ message = I18n.t('devise_token_auth.registrations.redirect_url_not_allowed', redirect_url: @redirect_url)
128
+ render_error(422, message, response)
129
+ end
130
+
131
+ def render_create_success
132
+ render json: {
133
+ status: 'success',
134
+ data: resource_data
135
+ }
136
+ end
137
+
138
+ def render_create_error
139
+ render json: {
140
+ status: 'error',
141
+ data: resource_data,
142
+ errors: resource_errors
143
+ }, status: 422
144
+ end
145
+
146
+ def render_update_success
147
+ render json: {
148
+ status: 'success',
149
+ data: resource_data
150
+ }
151
+ end
152
+
153
+ def render_update_error
154
+ render json: {
155
+ status: 'error',
156
+ errors: resource_errors
157
+ }, status: 422
158
+ end
159
+
160
+ def render_update_error_user_not_found
161
+ render_error(404, I18n.t('devise_token_auth.registrations.user_not_found'), status: 'error')
162
+ end
163
+
164
+ def render_destroy_success
165
+ render json: {
166
+ status: 'success',
167
+ message: I18n.t('devise_token_auth.registrations.account_with_uid_destroyed', uid: @resource.uid)
168
+ }
169
+ end
170
+
171
+ def render_destroy_error
172
+ render_error(404, I18n.t('devise_token_auth.registrations.account_to_destroy_not_found'), status: 'error')
173
+ end
174
+
175
+ private
176
+
177
+ def resource_update_method
178
+ if DeviseTokenAuth.check_current_password_before_update == :attributes
179
+ 'update_with_password'
180
+ elsif DeviseTokenAuth.check_current_password_before_update == :password && account_update_params.key?(:password)
181
+ 'update_with_password'
182
+ elsif account_update_params.key?(:current_password)
183
+ 'update_with_password'
184
+ else
185
+ 'update'
186
+ end
187
+ end
188
+
189
+ def validate_sign_up_params
190
+ validate_post_data sign_up_params, I18n.t('errors.messages.validate_sign_up_params')
191
+ end
192
+
193
+ def validate_account_update_params
194
+ validate_post_data account_update_params, I18n.t('errors.messages.validate_account_update_params')
195
+ end
196
+
197
+ def validate_post_data which, message
198
+ render_error(:unprocessable_entity, message, status: 'error') if which.empty?
199
+ end
200
+
201
+ def active_for_authentication?
202
+ !@resource.respond_to?(:active_for_authentication?) || @resource.active_for_authentication?
203
+ end
204
+ end
205
+ end