Skip to content

Commit 781026c

Browse files
acinaderdplewis
authored andcommitted
Add options to _linkWith(provider) for masterKey (#767)
* Don't add the session token to the save options. Remove test that verifies that session token is added to save options * Allow master key to be used with ParseUser._link needed to link in cloud code. * 1. add saveOptions to recursive _link call 2. add a fixme to add options to unlink too * attempt to cover changes. unusccessfully * add integration tests * unit tests * test for custom auth
1 parent b5e51d3 commit 781026c

File tree

6 files changed

+182
-41
lines changed

6 files changed

+182
-41
lines changed

integration/server.js

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,27 @@
11
const express = require('express');
22
const ParseServer = require('parse-server').ParseServer;
33
const app = express();
4+
const CustomAuth = require('./test/CustomAuth');
45

5-
// Specify the connection string for your mongodb database
6-
// and the location to your Parse cloud code
76
const api = new ParseServer({
87
databaseURI: 'mongodb://localhost:27017/integration',
98
appId: 'integration',
109
masterKey: 'notsosecret',
11-
serverURL: 'http://localhost:1337/parse', // Don't forget to change to https if needed
10+
serverURL: 'http://localhost:1337/parse',
1211
cloud: `${__dirname}/cloud/main.js`,
1312
liveQuery: {
1413
classNames: ['TestObject', 'DiffObject'],
1514
},
1615
startLiveQueryServer: true,
16+
auth: {
17+
myAuth: {
18+
module: CustomAuth,
19+
option1: 'hello',
20+
option2: 'world',
21+
}
22+
}
1723
});
1824

19-
// Serve the Parse API on the /parse URL prefix
2025
app.use('/parse', api);
2126

2227
const TestUtils = require('parse-server').TestUtils;

integration/test/CustomAuth.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/* eslint-disable */
2+
function validateAuthData(authData, options) {
3+
return Promise.resolve({})
4+
}
5+
6+
function validateAppId(appIds, authData, options) {
7+
return Promise.resolve({});
8+
}
9+
10+
module.exports = {
11+
validateAppId,
12+
validateAuthData,
13+
};

integration/test/ParseUserTest.js

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,20 @@ class CustomUser extends Parse.User {
1717
}
1818
Parse.Object.registerSubclass('CustomUser', CustomUser);
1919

20+
const provider = {
21+
authenticate: () => Promise.resolve(),
22+
restoreAuthentication: () => true,
23+
getAuthType: () => 'anonymous',
24+
getAuthData() {
25+
return {
26+
authData: {
27+
id: '1234',
28+
},
29+
};
30+
},
31+
};
32+
Parse.User._registerAuthenticationProvider(provider);
33+
2034
describe('Parse User', () => {
2135
beforeAll(() => {
2236
Parse.initialize('integration', null, 'notsosecret');
@@ -562,4 +576,77 @@ describe('Parse User', () => {
562576
expect(user instanceof CustomUser).toBe(true);
563577
expect(user.doSomething()).toBe(5);
564578
});
579+
580+
it('can link without master key', async () => {
581+
Parse.User.enableUnsafeCurrentUser();
582+
583+
const user = new Parse.User();
584+
user.setUsername('Alice');
585+
user.setPassword('sekrit');
586+
await user.signUp();
587+
await user._linkWith(provider.getAuthType(), provider.getAuthData());
588+
expect(user._isLinked(provider)).toBe(true);
589+
await user._unlinkFrom(provider);
590+
expect(user._isLinked(provider)).toBe(false);
591+
});
592+
593+
it('can link with master key', async () => {
594+
Parse.User.disableUnsafeCurrentUser();
595+
596+
const user = new Parse.User();
597+
user.setUsername('Alice');
598+
user.setPassword('sekrit');
599+
await user.save(null, { useMasterKey: true });
600+
await user._linkWith(provider.getAuthType(), provider.getAuthData(), { useMasterKey: true });
601+
expect(user._isLinked(provider)).toBe(true);
602+
await user._unlinkFrom(provider, { useMasterKey: true });
603+
expect(user._isLinked(provider)).toBe(false);
604+
});
605+
606+
it('can link with session token', async () => {
607+
Parse.User.disableUnsafeCurrentUser();
608+
609+
const user = new Parse.User();
610+
user.setUsername('Alice');
611+
user.setPassword('sekrit');
612+
await user.signUp();
613+
expect(user.isCurrent()).toBe(false);
614+
615+
const sessionToken = user.getSessionToken();
616+
await user._linkWith(provider.getAuthType(), provider.getAuthData(), { sessionToken });
617+
expect(user._isLinked(provider)).toBe(true);
618+
await user._unlinkFrom(provider, { sessionToken });
619+
expect(user._isLinked(provider)).toBe(false);
620+
});
621+
622+
it('can link with custom auth', async () => {
623+
Parse.User.enableUnsafeCurrentUser();
624+
const provider = {
625+
authenticate: () => Promise.resolve(),
626+
restoreAuthentication() {
627+
return true;
628+
},
629+
630+
getAuthType() {
631+
return 'myAuth';
632+
},
633+
634+
getAuthData() {
635+
return {
636+
authData: {
637+
id: 1234,
638+
},
639+
};
640+
},
641+
};
642+
Parse.User._registerAuthenticationProvider(provider);
643+
const user = new Parse.User();
644+
user.setUsername('Alice');
645+
user.setPassword('sekrit');
646+
await user.signUp();
647+
await user._linkWith(provider.getAuthType(), provider.getAuthData());
648+
expect(user._isLinked(provider)).toBe(true);
649+
await user._unlinkFrom(provider);
650+
expect(user._isLinked(provider)).toBe(false);
651+
});
565652
});

src/ParseObject.js

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1158,10 +1158,6 @@ class ParseObject {
11581158
if (options.hasOwnProperty('sessionToken') && typeof options.sessionToken === 'string') {
11591159
saveOptions.sessionToken = options.sessionToken;
11601160
}
1161-
// Pass sessionToken if saving currentUser
1162-
if (typeof this.getSessionToken === 'function' && this.getSessionToken()) {
1163-
saveOptions.sessionToken = this.getSessionToken();
1164-
}
11651161
const controller = CoreManager.getObjectController();
11661162
const unsaved = unsavedChildren(this);
11671163
return controller.save(unsaved, saveOptions).then(() => {

src/ParseUser.js

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ class ParseUser extends ParseObject {
7777
* Unlike in the Android/iOS SDKs, logInWith is unnecessary, since you can
7878
* call linkWith on the user (even if it doesn't exist yet on the server).
7979
*/
80-
_linkWith(provider: any, options: { authData?: AuthData }): Promise {
80+
_linkWith(provider: any, options: { authData?: AuthData }, saveOpts?: FullOptions): Promise {
8181
let authType;
8282
if (typeof provider === 'string') {
8383
authType = provider;
@@ -95,15 +95,16 @@ class ParseUser extends ParseObject {
9595
const controller = CoreManager.getUserController();
9696
return controller.linkWith(
9797
this,
98-
authData
98+
authData,
99+
saveOpts
99100
);
100101
} else {
101102
return new Promise((resolve, reject) => {
102103
provider.authenticate({
103104
success: (provider, result) => {
104105
const opts = {};
105106
opts.authData = result;
106-
this._linkWith(provider, opts).then(() => {
107+
this._linkWith(provider, opts, saveOpts).then(() => {
107108
resolve(this);
108109
}, (error) => {
109110
reject(error);
@@ -180,13 +181,12 @@ class ParseUser extends ParseObject {
180181

181182
/**
182183
* Unlinks a user from a service.
183-
184184
*/
185-
_unlinkFrom(provider: any) {
185+
_unlinkFrom(provider: any, options?: FullOptions) {
186186
if (typeof provider === 'string') {
187187
provider = authProviders[provider];
188188
}
189-
return this._linkWith(provider, { authData: null }).then(() => {
189+
return this._linkWith(provider, { authData: null }, options).then(() => {
190190
this._synchronizeAuthData(provider);
191191
return Promise.resolve(this);
192192
});
@@ -1054,8 +1054,8 @@ const DefaultController = {
10541054
});
10551055
},
10561056

1057-
linkWith(user: ParseUser, authData: AuthData) {
1058-
return user.save({ authData }).then(() => {
1057+
linkWith(user: ParseUser, authData: AuthData, options: FullOptions) {
1058+
return user.save({ authData }, options).then(() => {
10591059
if (canUseCurrentUser) {
10601060
return DefaultController.setCurrentUser(user);
10611061
}

src/__tests__/ParseUser-test.js

Lines changed: 65 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -742,31 +742,6 @@ describe('ParseUser', () => {
742742
spy.mockRestore();
743743
});
744744

745-
it('can pass sessionToken on save', async () => {
746-
ParseUser.enableUnsafeCurrentUser();
747-
ParseUser._clearCache();
748-
CoreManager.setRESTController({
749-
request() {
750-
return Promise.resolve({
751-
objectId: 'uid5',
752-
sessionToken: 'r:123abc',
753-
authData: {
754-
anonymous: {
755-
id: 'anonymousId',
756-
}
757-
}
758-
}, 200);
759-
},
760-
ajax() {}
761-
});
762-
const user = await AnonymousUtils.logIn();
763-
user.set('field', 'hello');
764-
jest.spyOn(user, 'getSessionToken');
765-
766-
await user.save();
767-
expect(user.getSessionToken).toHaveBeenCalledTimes(2);
768-
});
769-
770745
it('can destroy anonymous user on logout', async () => {
771746
ParseUser.enableUnsafeCurrentUser();
772747
ParseUser._clearCache();
@@ -792,6 +767,31 @@ describe('ParseUser', () => {
792767
expect(user.destroy).toHaveBeenCalledTimes(1);
793768
});
794769

770+
it('can unlink', async () => {
771+
const provider = AnonymousUtils._getAuthProvider();
772+
ParseUser._registerAuthenticationProvider(provider);
773+
const user = new ParseUser();
774+
jest.spyOn(user, '_linkWith');
775+
user._unlinkFrom(provider);
776+
expect(user._linkWith).toHaveBeenCalledTimes(1);
777+
expect(user._linkWith).toHaveBeenCalledWith(provider, { authData: null }, undefined);
778+
});
779+
780+
it('can unlink with options', async () => {
781+
const provider = AnonymousUtils._getAuthProvider();
782+
ParseUser._registerAuthenticationProvider(provider);
783+
const user = new ParseUser();
784+
jest.spyOn(user, '_linkWith')
785+
.mockImplementationOnce((authProvider, authData, saveOptions) => {
786+
expect(authProvider).toEqual(provider);
787+
expect(authData).toEqual({ authData: null});
788+
expect(saveOptions).toEqual({ useMasterKey: true });
789+
return Promise.resolve();
790+
});
791+
user._unlinkFrom(provider.getAuthType(), { useMasterKey: true });
792+
expect(user._linkWith).toHaveBeenCalledTimes(1);
793+
});
794+
795795
it('can destroy anonymous user when login new user', async () => {
796796
ParseUser.enableUnsafeCurrentUser();
797797
ParseUser._clearCache();
@@ -876,4 +876,44 @@ describe('ParseUser', () => {
876876
done();
877877
});
878878
});
879+
880+
it('can linkWith options', async () => {
881+
ParseUser._clearCache();
882+
CoreManager.setRESTController({
883+
request(method, path, body, options) {
884+
expect(options).toEqual({ useMasterKey: true });
885+
return Promise.resolve({
886+
objectId: 'uid5',
887+
sessionToken: 'r:123abc',
888+
authData: {
889+
test: {
890+
id: 'id',
891+
access_token: 'access_token'
892+
}
893+
}
894+
}, 200);
895+
},
896+
ajax() {}
897+
});
898+
const provider = {
899+
authenticate(options) {
900+
if (options.success) {
901+
options.success(this, {
902+
id: 'id',
903+
access_token: 'access_token'
904+
});
905+
}
906+
},
907+
restoreAuthentication() {},
908+
getAuthType() {
909+
return 'test';
910+
},
911+
deauthenticate() {}
912+
};
913+
914+
const user = new ParseUser();
915+
await user._linkWith(provider, null, { useMasterKey: true });
916+
917+
expect(user.get('authData')).toEqual({ test: { id: 'id', access_token: 'access_token' } });
918+
});
879919
});

0 commit comments

Comments
 (0)