devise_token_auth_multitenancy 1.1.3.alpha1

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 (175) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +13 -0
  3. data/README.md +103 -0
  4. data/Rakefile +42 -0
  5. data/app/controllers/devise_token_auth/application_controller.rb +79 -0
  6. data/app/controllers/devise_token_auth/concerns/resource_finder.rb +44 -0
  7. data/app/controllers/devise_token_auth/concerns/set_user_by_token.rb +162 -0
  8. data/app/controllers/devise_token_auth/confirmations_controller.rb +82 -0
  9. data/app/controllers/devise_token_auth/omniauth_callbacks_controller.rb +287 -0
  10. data/app/controllers/devise_token_auth/passwords_controller.rb +206 -0
  11. data/app/controllers/devise_token_auth/registrations_controller.rb +205 -0
  12. data/app/controllers/devise_token_auth/sessions_controller.rb +131 -0
  13. data/app/controllers/devise_token_auth/token_validations_controller.rb +31 -0
  14. data/app/controllers/devise_token_auth/unlocks_controller.rb +89 -0
  15. data/app/models/devise_token_auth/concerns/active_record_support.rb +16 -0
  16. data/app/models/devise_token_auth/concerns/confirmable_support.rb +27 -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 +19 -0
  19. data/app/models/devise_token_auth/concerns/user.rb +257 -0
  20. data/app/models/devise_token_auth/concerns/user_omniauth_callbacks.rb +28 -0
  21. data/app/validators/devise_token_auth_email_validator.rb +23 -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 +57 -0
  29. data/config/locales/es.yml +51 -0
  30. data/config/locales/fr.yml +51 -0
  31. data/config/locales/he.yml +52 -0
  32. data/config/locales/it.yml +48 -0
  33. data/config/locales/ja.yml +48 -0
  34. data/config/locales/ko.yml +51 -0
  35. data/config/locales/nl.yml +32 -0
  36. data/config/locales/pl.yml +51 -0
  37. data/config/locales/pt-BR.yml +48 -0
  38. data/config/locales/pt.yml +51 -0
  39. data/config/locales/ro.yml +48 -0
  40. data/config/locales/ru.yml +52 -0
  41. data/config/locales/sq.yml +48 -0
  42. data/config/locales/sv.yml +52 -0
  43. data/config/locales/uk.yml +61 -0
  44. data/config/locales/vi.yml +52 -0
  45. data/config/locales/zh-CN.yml +48 -0
  46. data/config/locales/zh-HK.yml +50 -0
  47. data/config/locales/zh-TW.yml +50 -0
  48. data/lib/devise_token_auth/blacklist.rb +2 -0
  49. data/lib/devise_token_auth/controllers/helpers.rb +161 -0
  50. data/lib/devise_token_auth/controllers/url_helpers.rb +10 -0
  51. data/lib/devise_token_auth/engine.rb +96 -0
  52. data/lib/devise_token_auth/errors.rb +8 -0
  53. data/lib/devise_token_auth/rails/routes.rb +116 -0
  54. data/lib/devise_token_auth/token_factory.rb +126 -0
  55. data/lib/devise_token_auth/url.rb +44 -0
  56. data/lib/devise_token_auth/version.rb +5 -0
  57. data/lib/devise_token_auth.rb +14 -0
  58. data/lib/generators/devise_token_auth/USAGE +31 -0
  59. data/lib/generators/devise_token_auth/install_generator.rb +91 -0
  60. data/lib/generators/devise_token_auth/install_generator_helpers.rb +98 -0
  61. data/lib/generators/devise_token_auth/install_mongoid_generator.rb +46 -0
  62. data/lib/generators/devise_token_auth/install_views_generator.rb +18 -0
  63. data/lib/generators/devise_token_auth/templates/devise_token_auth.rb +60 -0
  64. data/lib/generators/devise_token_auth/templates/devise_token_auth_create_users.rb.erb +49 -0
  65. data/lib/generators/devise_token_auth/templates/user.rb.erb +9 -0
  66. data/lib/generators/devise_token_auth/templates/user_mongoid.rb.erb +56 -0
  67. data/lib/tasks/devise_token_auth_tasks.rake +6 -0
  68. data/test/controllers/custom/custom_confirmations_controller_test.rb +25 -0
  69. data/test/controllers/custom/custom_omniauth_callbacks_controller_test.rb +33 -0
  70. data/test/controllers/custom/custom_passwords_controller_test.rb +79 -0
  71. data/test/controllers/custom/custom_registrations_controller_test.rb +63 -0
  72. data/test/controllers/custom/custom_sessions_controller_test.rb +39 -0
  73. data/test/controllers/custom/custom_token_validations_controller_test.rb +42 -0
  74. data/test/controllers/demo_group_controller_test.rb +151 -0
  75. data/test/controllers/demo_mang_controller_test.rb +284 -0
  76. data/test/controllers/demo_user_controller_test.rb +629 -0
  77. data/test/controllers/devise_token_auth/confirmations_controller_test.rb +191 -0
  78. data/test/controllers/devise_token_auth/omniauth_callbacks_controller_test.rb +441 -0
  79. data/test/controllers/devise_token_auth/passwords_controller_test.rb +780 -0
  80. data/test/controllers/devise_token_auth/registrations_controller_test.rb +907 -0
  81. data/test/controllers/devise_token_auth/sessions_controller_test.rb +503 -0
  82. data/test/controllers/devise_token_auth/token_validations_controller_test.rb +102 -0
  83. data/test/controllers/devise_token_auth/unlocks_controller_test.rb +196 -0
  84. data/test/controllers/overrides/confirmations_controller_test.rb +47 -0
  85. data/test/controllers/overrides/omniauth_callbacks_controller_test.rb +53 -0
  86. data/test/controllers/overrides/passwords_controller_test.rb +64 -0
  87. data/test/controllers/overrides/registrations_controller_test.rb +46 -0
  88. data/test/controllers/overrides/sessions_controller_test.rb +35 -0
  89. data/test/controllers/overrides/token_validations_controller_test.rb +43 -0
  90. data/test/dummy/README.rdoc +28 -0
  91. data/test/dummy/app/active_record/confirmable_user.rb +11 -0
  92. data/test/dummy/app/active_record/lockable_user.rb +7 -0
  93. data/test/dummy/app/active_record/mang.rb +5 -0
  94. data/test/dummy/app/active_record/only_email_user.rb +7 -0
  95. data/test/dummy/app/active_record/scoped_user.rb +9 -0
  96. data/test/dummy/app/active_record/unconfirmable_user.rb +9 -0
  97. data/test/dummy/app/active_record/unregisterable_user.rb +9 -0
  98. data/test/dummy/app/active_record/user.rb +6 -0
  99. data/test/dummy/app/controllers/application_controller.rb +18 -0
  100. data/test/dummy/app/controllers/auth_origin_controller.rb +7 -0
  101. data/test/dummy/app/controllers/custom/confirmations_controller.rb +13 -0
  102. data/test/dummy/app/controllers/custom/omniauth_callbacks_controller.rb +13 -0
  103. data/test/dummy/app/controllers/custom/passwords_controller.rb +39 -0
  104. data/test/dummy/app/controllers/custom/registrations_controller.rb +39 -0
  105. data/test/dummy/app/controllers/custom/sessions_controller.rb +29 -0
  106. data/test/dummy/app/controllers/custom/token_validations_controller.rb +19 -0
  107. data/test/dummy/app/controllers/demo_group_controller.rb +15 -0
  108. data/test/dummy/app/controllers/demo_mang_controller.rb +14 -0
  109. data/test/dummy/app/controllers/demo_user_controller.rb +27 -0
  110. data/test/dummy/app/controllers/overrides/confirmations_controller.rb +28 -0
  111. data/test/dummy/app/controllers/overrides/omniauth_callbacks_controller.rb +16 -0
  112. data/test/dummy/app/controllers/overrides/passwords_controller.rb +35 -0
  113. data/test/dummy/app/controllers/overrides/registrations_controller.rb +29 -0
  114. data/test/dummy/app/controllers/overrides/sessions_controller.rb +36 -0
  115. data/test/dummy/app/controllers/overrides/token_validations_controller.rb +23 -0
  116. data/test/dummy/app/helpers/application_helper.rb +1058 -0
  117. data/test/dummy/app/models/concerns/favorite_color.rb +19 -0
  118. data/test/dummy/app/mongoid/confirmable_user.rb +52 -0
  119. data/test/dummy/app/mongoid/lockable_user.rb +38 -0
  120. data/test/dummy/app/mongoid/mang.rb +46 -0
  121. data/test/dummy/app/mongoid/only_email_user.rb +33 -0
  122. data/test/dummy/app/mongoid/scoped_user.rb +50 -0
  123. data/test/dummy/app/mongoid/unconfirmable_user.rb +44 -0
  124. data/test/dummy/app/mongoid/unregisterable_user.rb +47 -0
  125. data/test/dummy/app/mongoid/user.rb +49 -0
  126. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  127. data/test/dummy/config/application.rb +48 -0
  128. data/test/dummy/config/application.yml.bk +0 -0
  129. data/test/dummy/config/boot.rb +11 -0
  130. data/test/dummy/config/environment.rb +7 -0
  131. data/test/dummy/config/environments/development.rb +46 -0
  132. data/test/dummy/config/environments/production.rb +84 -0
  133. data/test/dummy/config/environments/test.rb +50 -0
  134. data/test/dummy/config/initializers/assets.rb +10 -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/factories/users.rb +41 -0
  162. data/test/lib/devise_token_auth/blacklist_test.rb +11 -0
  163. data/test/lib/devise_token_auth/token_factory_test.rb +191 -0
  164. data/test/lib/devise_token_auth/url_test.rb +26 -0
  165. data/test/lib/generators/devise_token_auth/install_generator_test.rb +217 -0
  166. data/test/lib/generators/devise_token_auth/install_generator_with_namespace_test.rb +222 -0
  167. data/test/lib/generators/devise_token_auth/install_views_generator_test.rb +25 -0
  168. data/test/models/concerns/mongoid_support_test.rb +31 -0
  169. data/test/models/concerns/tokens_serialization_test.rb +70 -0
  170. data/test/models/confirmable_user_test.rb +35 -0
  171. data/test/models/only_email_user_test.rb +29 -0
  172. data/test/models/user_test.rb +108 -0
  173. data/test/support/controllers/routes.rb +43 -0
  174. data/test/test_helper.rb +103 -0
  175. metadata +483 -0
@@ -0,0 +1,287 @@
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
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
+ render_data_or_redirect('deliverCredentials', @auth_params.as_json, @resource.as_json)
74
+ end
75
+
76
+ def omniauth_failure
77
+ @error = params[:message]
78
+ render_data_or_redirect('authFailure', error: @error)
79
+ end
80
+
81
+ def validate_auth_origin_url_param
82
+ return render_error_not_allowed_auth_origin_url if auth_origin_url && blacklisted_redirect_url?(auth_origin_url)
83
+ end
84
+
85
+
86
+ protected
87
+
88
+ # this will be determined differently depending on the action that calls
89
+ # it. redirect_callbacks is called upon returning from successful omniauth
90
+ # authentication, and the target params live in an omniauth-specific
91
+ # request.env variable. this variable is then persisted thru the redirect
92
+ # using our own dta.omniauth.params session var. the omniauth_success
93
+ # method will access that session var and then destroy it immediately
94
+ # after use. In the failure case, finally, the omniauth params
95
+ # are added as query params in our monkey patch to OmniAuth in engine.rb
96
+ def omniauth_params
97
+ unless defined?(@_omniauth_params)
98
+ if request.env['omniauth.params'] && request.env['omniauth.params'].any?
99
+ @_omniauth_params = request.env['omniauth.params']
100
+ elsif session['dta.omniauth.params'] && session['dta.omniauth.params'].any?
101
+ @_omniauth_params ||= session.delete('dta.omniauth.params')
102
+ @_omniauth_params
103
+ elsif params['omniauth_window_type']
104
+ @_omniauth_params = params.slice('omniauth_window_type', 'auth_origin_url', 'resource_class', 'origin')
105
+ else
106
+ @_omniauth_params = {}
107
+ end
108
+ end
109
+ @_omniauth_params
110
+
111
+ end
112
+
113
+ # break out provider attribute assignment for easy method extension
114
+ def assign_provider_attrs(user, auth_hash)
115
+ attrs = auth_hash['info'].to_hash
116
+ attrs = attrs.slice(*user.attribute_names)
117
+ user.assign_attributes(attrs)
118
+ end
119
+
120
+ # derive allowed params from the standard devise parameter sanitizer
121
+ def whitelisted_params
122
+ whitelist = params_for_resource(:sign_up)
123
+
124
+ whitelist.inject({}) do |coll, key|
125
+ param = omniauth_params[key.to_s]
126
+ coll[key] = param if param
127
+ coll
128
+ end
129
+ end
130
+
131
+ def resource_class(mapping = nil)
132
+ if omniauth_params['resource_class']
133
+ omniauth_params['resource_class'].constantize
134
+ elsif params['resource_class']
135
+ params['resource_class'].constantize
136
+ else
137
+ raise 'No resource_class found'
138
+ end
139
+ end
140
+
141
+ def resource_name
142
+ resource_class
143
+ end
144
+
145
+ def omniauth_window_type
146
+ omniauth_params['omniauth_window_type']
147
+ end
148
+
149
+ def unsafe_auth_origin_url
150
+ omniauth_params['auth_origin_url'] || omniauth_params['origin']
151
+ end
152
+
153
+
154
+ def auth_origin_url
155
+ if unsafe_auth_origin_url && blacklisted_redirect_url?(unsafe_auth_origin_url)
156
+ return nil
157
+ end
158
+ return unsafe_auth_origin_url
159
+ end
160
+
161
+ # in the success case, omniauth_window_type is in the omniauth_params.
162
+ # in the failure case, it is in a query param. See monkey patch above
163
+ def omniauth_window_type
164
+ omniauth_params.nil? ? params['omniauth_window_type'] : omniauth_params['omniauth_window_type']
165
+ end
166
+
167
+ # this sesison value is set by the redirect_callbacks method. its purpose
168
+ # is to persist the omniauth auth hash value thru a redirect. the value
169
+ # must be destroyed immediatly after it is accessed by omniauth_success
170
+ def auth_hash
171
+ @_auth_hash ||= session.delete('dta.omniauth.auth')
172
+ @_auth_hash
173
+ end
174
+
175
+ # ensure that this controller responds to :devise_controller? conditionals.
176
+ # this is used primarily for access to the parameter sanitizers.
177
+ def assert_is_devise_resource!
178
+ true
179
+ end
180
+
181
+ def set_random_password
182
+ # set crazy password for new oauth users. this is only used to prevent
183
+ # access via email sign-in.
184
+ p = SecureRandom.urlsafe_base64(nil, false)
185
+ @resource.password = p
186
+ @resource.password_confirmation = p
187
+ end
188
+
189
+ def create_auth_params
190
+ @auth_params = {
191
+ auth_token: @token.token,
192
+ client_id: @token.client,
193
+ uid: @resource.uid,
194
+ expiry: @token.expiry,
195
+ config: @config
196
+ }
197
+ @auth_params.merge!(oauth_registration: true) if @oauth_registration
198
+ @auth_params
199
+ end
200
+
201
+ def set_token_on_resource
202
+ @config = omniauth_params['config_name']
203
+ @token = @resource.create_token
204
+ end
205
+
206
+ def render_error_not_allowed_auth_origin_url
207
+ message = I18n.t('devise_token_auth.omniauth.not_allowed_redirect_url', redirect_url: unsafe_auth_origin_url)
208
+ render_data_or_redirect('authFailure', error: message)
209
+ end
210
+
211
+ def render_data(message, data)
212
+ @data = data.merge(message: ActionController::Base.helpers.sanitize(message))
213
+ render layout: nil, template: 'devise_token_auth/omniauth_external_window'
214
+ end
215
+
216
+ def render_data_or_redirect(message, data, user_data = {})
217
+
218
+ # We handle inAppBrowser and newWindow the same, but it is nice
219
+ # to support values in case people need custom implementations for each case
220
+ # (For example, nbrustein does not allow new users to be created if logging in with
221
+ # an inAppBrowser)
222
+ #
223
+ # See app/views/devise_token_auth/omniauth_external_window.html.erb to understand
224
+ # why we can handle these both the same. The view is setup to handle both cases
225
+ # at the same time.
226
+ if ['inAppBrowser', 'newWindow'].include?(omniauth_window_type)
227
+ render_data(message, user_data.merge(data))
228
+
229
+ elsif auth_origin_url # default to same-window implementation, which forwards back to auth_origin_url
230
+
231
+ # build and redirect to destination url
232
+ redirect_to DeviseTokenAuth::Url.generate(auth_origin_url, data.merge(blank: true))
233
+ else
234
+
235
+ # there SHOULD always be an auth_origin_url, but if someone does something silly
236
+ # like coming straight to this url or refreshing the page at the wrong time, there may not be one.
237
+ # In that case, just render in plain text the error message if there is one or otherwise
238
+ # a generic message.
239
+ fallback_render data[:error] || 'An error occurred'
240
+ end
241
+ end
242
+
243
+ def fallback_render(text)
244
+ render inline: %Q(
245
+
246
+ <html>
247
+ <head></head>
248
+ <body>
249
+ #{ActionController::Base.helpers.sanitize(text)}
250
+ </body>
251
+ </html>)
252
+ end
253
+
254
+ def handle_new_resource
255
+ @oauth_registration = true
256
+ set_random_password
257
+ end
258
+
259
+ def assign_whitelisted_params?
260
+ true
261
+ end
262
+
263
+ def get_resource_from_auth_hash
264
+ # find or create user by provider and provider uid
265
+ @resource = resource_class.where(
266
+ uid: auth_hash['uid'],
267
+ provider: auth_hash['provider']
268
+ ).first_or_initialize
269
+
270
+ if @resource.new_record?
271
+ handle_new_resource
272
+ end
273
+
274
+ # sync user info with provider, update/generate auth token
275
+ assign_provider_attrs(@resource, auth_hash)
276
+
277
+ # assign any additional (whitelisted) attributes
278
+ if assign_whitelisted_params?
279
+ extra_params = whitelisted_params
280
+ @resource.assign_attributes(extra_params) if extra_params
281
+ end
282
+
283
+ @resource
284
+ end
285
+ end
286
+
287
+ end
@@ -0,0 +1,206 @@
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
+ else
54
+ redirect_header_options = { reset_password: true }
55
+ redirect_headers = build_redirect_headers(token.token,
56
+ token.client,
57
+ redirect_header_options)
58
+ redirect_to(@resource.build_auth_url(@redirect_url,
59
+ redirect_headers))
60
+ end
61
+ else
62
+ render_edit_error
63
+ end
64
+ end
65
+
66
+ def update
67
+ # make sure user is authorized
68
+ if require_client_password_reset_token? && resource_params[:reset_password_token]
69
+ @resource = resource_class.with_reset_password_token(resource_params[:reset_password_token])
70
+ return render_update_error_unauthorized unless @resource
71
+
72
+ @token = @resource.create_token
73
+ else
74
+ @resource = set_user_by_token
75
+ end
76
+
77
+ return render_update_error_unauthorized unless @resource
78
+
79
+ # make sure account doesn't use oauth2 provider
80
+ unless @resource.provider == 'email'
81
+ return render_update_error_password_not_required
82
+ end
83
+
84
+ # ensure that password params were sent
85
+ unless password_resource_params[:password] && password_resource_params[:password_confirmation]
86
+ return render_update_error_missing_password
87
+ end
88
+
89
+ if @resource.send(resource_update_method, password_resource_params)
90
+ @resource.allow_password_change = false if recoverable_enabled?
91
+ @resource.save!
92
+
93
+ yield @resource if block_given?
94
+ return render_update_success
95
+ else
96
+ return render_update_error
97
+ end
98
+ end
99
+
100
+ protected
101
+
102
+ def resource_update_method
103
+ allow_password_change = recoverable_enabled? && @resource.allow_password_change == true || require_client_password_reset_token?
104
+ if DeviseTokenAuth.check_current_password_before_update == false || allow_password_change
105
+ 'update'
106
+ else
107
+ 'update_with_password'
108
+ end
109
+ end
110
+
111
+ def render_create_error_missing_email
112
+ render_error(401, I18n.t('devise_token_auth.passwords.missing_email'))
113
+ end
114
+
115
+ def render_create_error_missing_redirect_url
116
+ render_error(401, I18n.t('devise_token_auth.passwords.missing_redirect_url'))
117
+ end
118
+
119
+ def render_error_not_allowed_redirect_url
120
+ response = {
121
+ status: 'error',
122
+ data: resource_data
123
+ }
124
+ message = I18n.t('devise_token_auth.passwords.not_allowed_redirect_url', redirect_url: @redirect_url)
125
+ render_error(422, message, response)
126
+ end
127
+
128
+ def render_create_success
129
+ render json: {
130
+ success: true,
131
+ message: I18n.t('devise_token_auth.passwords.sended', email: @email)
132
+ }
133
+ end
134
+
135
+ def render_create_error(errors)
136
+ render json: {
137
+ success: false,
138
+ errors: errors
139
+ }, status: 400
140
+ end
141
+
142
+ def render_edit_error
143
+ raise ActionController::RoutingError, 'Not Found'
144
+ end
145
+
146
+ def render_update_error_unauthorized
147
+ render_error(401, 'Unauthorized')
148
+ end
149
+
150
+ def render_update_error_password_not_required
151
+ render_error(422, I18n.t('devise_token_auth.passwords.password_not_required', provider: @resource.provider.humanize))
152
+ end
153
+
154
+ def render_update_error_missing_password
155
+ render_error(422, I18n.t('devise_token_auth.passwords.missing_passwords'))
156
+ end
157
+
158
+ def render_update_success
159
+ render json: {
160
+ success: true,
161
+ data: resource_data,
162
+ message: I18n.t('devise_token_auth.passwords.successfully_updated')
163
+ }
164
+ end
165
+
166
+ def render_update_error
167
+ render json: {
168
+ success: false,
169
+ errors: resource_errors
170
+ }, status: 422
171
+ end
172
+
173
+ private
174
+
175
+ def resource_params
176
+ params.permit(:email, :reset_password_token)
177
+ end
178
+
179
+ def password_resource_params
180
+ params.permit(*params_for_resource(:account_update))
181
+ end
182
+
183
+ def render_not_found_error
184
+ render_error(404, I18n.t('devise_token_auth.passwords.user_not_found', email: @email))
185
+ end
186
+
187
+ def validate_redirect_url_param
188
+ # give redirect value from params priority
189
+ @redirect_url = params.fetch(
190
+ :redirect_url,
191
+ DeviseTokenAuth.default_password_reset_url
192
+ )
193
+
194
+ return render_create_error_missing_redirect_url unless @redirect_url
195
+ return render_error_not_allowed_redirect_url if blacklisted_redirect_url?(@redirect_url)
196
+ end
197
+
198
+ def reset_password_token_as_raw?(recoverable)
199
+ recoverable && recoverable.reset_password_token.present? && !require_client_password_reset_token?
200
+ end
201
+
202
+ def require_client_password_reset_token?
203
+ DeviseTokenAuth.require_client_password_reset_token
204
+ end
205
+ end
206
+ 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