@@ -32,7 +32,7 @@ function RestWrite(config, auth, className, query, data, originalData) {
32
32
throw new Parse . Error ( Parse . Error . INVALID_KEY_NAME , 'objectId ' +
33
33
'is an invalid field name.' ) ;
34
34
}
35
-
35
+
36
36
// When the operation is complete, this.response may have several
37
37
// fields.
38
38
// response: the actual data to be returned
@@ -211,74 +211,117 @@ RestWrite.prototype.validateAuthData = function() {
211
211
212
212
var authData = this . data . authData ;
213
213
var providers = Object . keys ( authData ) ;
214
- if ( providers . length == 1 ) {
215
- var provider = providers [ 0 ] ;
214
+ if ( providers . length > 0 ) {
215
+ var provider = providers [ providers . length - 1 ] ;
216
216
var providerAuthData = authData [ provider ] ;
217
217
var hasToken = ( providerAuthData && providerAuthData . id ) ;
218
218
if ( providerAuthData === null || hasToken ) {
219
- return this . handleOAuthAuthData ( provider ) ;
219
+ return this . handleAuthData ( authData ) ;
220
220
}
221
221
}
222
222
throw new Parse . Error ( Parse . Error . UNSUPPORTED_SERVICE ,
223
223
'This authentication method is unsupported.' ) ;
224
224
} ;
225
225
226
- RestWrite . prototype . handleOAuthAuthData = function ( provider ) {
227
- var authData = this . data . authData [ provider ] ;
228
- if ( authData === null && this . query ) {
229
- // We are unlinking from the provider.
230
- this . data [ "_auth_data_" + provider ] = null ;
231
- return ;
232
- }
233
-
234
- let validateAuthData = this . config . authDataManager . getValidatorForProvider ( provider ) ;
226
+ RestWrite . prototype . handleAuthDataValidation = function ( authData ) {
227
+ let validations = Object . keys ( authData ) . map ( ( provider ) => {
228
+ if ( authData [ provider ] === null ) {
229
+ return Promise . resolve ( ) ;
230
+ }
231
+ let validateAuthData = this . config . authDataManager . getValidatorForProvider ( provider ) ;
232
+ if ( ! validateAuthData ) {
233
+ throw new Parse . Error ( Parse . Error . UNSUPPORTED_SERVICE ,
234
+ 'This authentication method is unsupported.' ) ;
235
+ } ;
236
+ return validateAuthData ( authData [ provider ] ) ;
237
+ } ) ;
238
+ return Promise . all ( validations ) ;
239
+ }
235
240
236
- if ( ! validateAuthData ) {
237
- throw new Parse . Error ( Parse . Error . UNSUPPORTED_SERVICE ,
238
- 'This authentication method is unsupported.' ) ;
239
- } ;
240
-
241
- return validateAuthData ( authData )
242
- . then ( ( ) => {
243
- // Check if this user already exists
244
- // TODO: does this handle re-linking correctly?
245
- var query = { } ;
246
- query [ 'authData.' + provider + '.id' ] = authData . id ;
247
- return this . config . database . find (
241
+ RestWrite . prototype . findUsersWithAuthData = function ( authData ) {
242
+ let providers = Object . keys ( authData ) ;
243
+ let query = providers . reduce ( ( memo , provider ) => {
244
+ if ( ! authData [ provider ] ) {
245
+ return memo ;
246
+ }
247
+ let queryKey = `authData.${ provider } .id` ;
248
+ let query = { } ;
249
+ query [ queryKey ] = authData [ provider ] . id ;
250
+ memo . push ( query ) ;
251
+ return memo ;
252
+ } , [ ] ) . filter ( ( q ) => {
253
+ return typeof q !== undefined ;
254
+ } ) ;
255
+
256
+ let findPromise = Promise . resolve ( [ ] ) ;
257
+ if ( query . length > 0 ) {
258
+ findPromise = this . config . database . find (
248
259
this . className ,
249
- query , { } ) ;
250
- } ) . then ( ( results ) => {
251
- this . storage [ 'authProvider' ] = provider ;
252
-
253
- // Put the data in the proper format
254
- this . data [ "_auth_data_" + provider ] = authData ;
255
-
256
- if ( results . length == 0 ) {
257
- // this a new user
258
- this . data . username = cryptoUtils . newToken ( ) ;
259
- } else if ( ! this . query ) {
260
- // Login with auth data
261
- // Short circuit
262
- delete results [ 0 ] . password ;
263
- this . response = {
264
- response : results [ 0 ] ,
265
- location : this . location ( )
266
- } ;
267
- this . data . objectId = results [ 0 ] . objectId ;
268
- } else if ( this . query && this . query . objectId ) {
269
- // Trying to update auth data but users
270
- // are different
271
- if ( results [ 0 ] . objectId !== this . query . objectId ) {
272
- delete this . data [ "_auth_data_" + provider ] ;
273
- throw new Parse . Error ( Parse . Error . ACCOUNT_ALREADY_LINKED ,
260
+ { '$or' : query } , { } )
261
+ }
262
+
263
+ return findPromise ;
264
+ }
265
+
266
+ RestWrite . prototype . handleAuthData = function ( authData ) {
267
+ let results ;
268
+ return this . findUsersWithAuthData ( authData ) . then ( ( r ) => {
269
+ results = r ;
270
+ if ( results . length > 1 ) {
271
+ // More than 1 user with the passed id's
272
+ throw new Parse . Error ( Parse . Error . ACCOUNT_ALREADY_LINKED ,
274
273
'this auth is already used' ) ;
274
+ } else if ( results . length == 1 ) {
275
+ // One user has this auth data registered
276
+ let knownProviders = Object . keys ( results [ 0 ] . authData ) ;
277
+ let providers = Object . keys ( authData ) ;
278
+ // Find the exising linked
279
+ // Keep only the new ones
280
+ let newAuthData = Object . assign ( { } , authData ) ;
281
+ newAuthData = providers . reduce ( ( memo , provider ) => {
282
+ if ( knownProviders . indexOf ( provider ) > - 1 ) {
283
+ delete memo [ provider ] ;
275
284
}
276
- } else {
277
-
278
- delete this . data [ "_auth_data_" + provider ] ;
279
- throw new Parse . Error ( Parse . Error . INTERNAL_SERVER_ERROR , 'THis should not be reached...' ) ;
285
+ return memo ;
286
+ } , newAuthData ) ;
287
+
288
+ if ( Object . keys ( newAuthData ) . length != 0 ) {
289
+ // the auth data was sent with more than 1 provider
290
+ // only validate the new ones
291
+ authData = newAuthData ;
280
292
}
293
+ }
294
+ return this . handleAuthDataValidation ( authData ) ;
295
+ } ) . then ( ( ) => {
296
+ // set the proper keys
297
+ Object . keys ( authData ) . forEach ( ( provider ) => {
298
+ this . data [ `_auth_data_${ provider } ` ] = authData [ provider ] ;
281
299
} ) ;
300
+
301
+ if ( results . length == 0 ) {
302
+ this . data . username = cryptoUtils . newToken ( ) ;
303
+ } else if ( ! this . query ) {
304
+ // Login with auth data
305
+ // Short circuit
306
+ delete results [ 0 ] . password ;
307
+ this . response = {
308
+ response : results [ 0 ] ,
309
+ location : this . location ( )
310
+ } ;
311
+ this . data . objectId = results [ 0 ] . objectId ;
312
+ } else if ( this . query && this . query . objectId ) {
313
+ // Trying to update auth data but users
314
+ // are different
315
+ if ( results [ 0 ] . objectId !== this . query . objectId ) {
316
+ Object . keys ( authData ) . forEach ( ( provider ) => {
317
+ delete this . data [ `_auth_data_${ provider } ` ] ;
318
+ } ) ;
319
+ throw new Parse . Error ( Parse . Error . ACCOUNT_ALREADY_LINKED ,
320
+ 'this auth is already used' ) ;
321
+ }
322
+ }
323
+ return Promise . resolve ( ) ;
324
+ } ) ;
282
325
}
283
326
284
327
// The non-third-party parts of User transformation
0 commit comments