devise_token_auth_multitenancy 1.1.3.alpha1

Sign up to get free protection for your applications and to get access to all the features.
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