@@ -32,7 +32,7 @@ function RestWrite(config, auth, className, query, data, originalData) {
3232 throw new Parse . Error ( Parse . Error . INVALID_KEY_NAME , 'objectId ' +
3333 'is an invalid field name.' ) ;
3434 }
35-
35+
3636 // When the operation is complete, this.response may have several
3737 // fields.
3838 // response: the actual data to be returned
@@ -211,74 +211,117 @@ RestWrite.prototype.validateAuthData = function() {
211211
212212 var authData = this . data . authData ;
213213 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 ] ;
216216 var providerAuthData = authData [ provider ] ;
217217 var hasToken = ( providerAuthData && providerAuthData . id ) ;
218218 if ( providerAuthData === null || hasToken ) {
219- return this . handleOAuthAuthData ( provider ) ;
219+ return this . handleAuthData ( authData ) ;
220220 }
221221 }
222222 throw new Parse . Error ( Parse . Error . UNSUPPORTED_SERVICE ,
223223 'This authentication method is unsupported.' ) ;
224224} ;
225225
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+ }
235240
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 (
248259 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 ,
274273 '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 ] ;
275284 }
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 ;
280292 }
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 ] ;
281299 } ) ;
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+ } ) ;
282325}
283326
284327// The non-third-party parts of User transformation
0 commit comments