Skip to content

Commit e8806a3

Browse files
authored
Merge cf6f58c into c8910ab
2 parents c8910ab + cf6f58c commit e8806a3

8 files changed

+106
-36
lines changed

package-lock.json

Lines changed: 6 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

spec/AuthenticationAdapters.spec.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -399,6 +399,33 @@ describe('AuthenticationProviders', function () {
399399
);
400400
});
401401

402+
it('should cache adapter', async () => {
403+
const adapter = {
404+
validateAppId() {
405+
return Promise.resolve();
406+
},
407+
validateAuthData() {
408+
return Promise.resolve();
409+
},
410+
validateOptions() {},
411+
};
412+
413+
const authDataSpy = spyOn(adapter, 'validateAuthData').and.callThrough();
414+
const optionsSpy = spyOn(adapter, 'validateOptions').and.callThrough();
415+
416+
await reconfigureServer({ auth: { customAuthentication: adapter } });
417+
418+
expect(optionsSpy).toHaveBeenCalled();
419+
await Parse.User.logInWith('customAuthentication', {
420+
authData: { id: 'user1', token: 'fakeToken1' },
421+
});
422+
await Parse.User.logInWith('customAuthentication', {
423+
authData: { id: 'user2', token: 'fakeToken2' },
424+
});
425+
expect(authDataSpy).toHaveBeenCalled();
426+
expect(optionsSpy).toHaveBeenCalledTimes(1);
427+
});
428+
402429
it('properly loads custom adapter module object', done => {
403430
const authenticationHandler = authenticationLoader({
404431
customAuthentication: path.resolve('./spec/support/CustomAuth.js'),

spec/AuthenticationAdaptersV2.spec.js

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -348,7 +348,9 @@ describe('Auth Adapter features', () => {
348348
it('should strip out authData if required', async () => {
349349
const spy = spyOn(modernAdapter3, 'validateOptions').and.callThrough();
350350
const afterSpy = spyOn(modernAdapter3, 'afterFind').and.callThrough();
351-
await reconfigureServer({ auth: { modernAdapter3 } });
351+
await reconfigureServer({ auth: { modernAdapter3 }, silent: false });
352+
expect(spy).toHaveBeenCalled();
353+
spy.calls.reset();
352354
const user = new Parse.User();
353355
await user.save({ authData: { modernAdapter3: { id: 'modernAdapter3Data' } } });
354356
await user.fetch({ sessionToken: user.getSessionToken() });
@@ -366,7 +368,7 @@ describe('Auth Adapter features', () => {
366368
{ id: 'modernAdapter3Data' },
367369
undefined
368370
);
369-
expect(spy).toHaveBeenCalled();
371+
expect(spy).not.toHaveBeenCalled();
370372
});
371373

372374
it('should throw if no triggers found', async () => {
@@ -1235,6 +1237,10 @@ describe('Auth Adapter features', () => {
12351237

12361238
spyOn(challengeAdapter, 'validateAuthData').and.rejectWith({});
12371239

1240+
await reconfigureServer({
1241+
auth: { challengeAdapter, soloAdapter },
1242+
});
1243+
12381244
await expectAsync(
12391245
requestWithExpectedError({
12401246
headers: headers,

spec/ParseUser.spec.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -489,6 +489,19 @@ describe('Parse.User testing', () => {
489489
);
490490
});
491491

492+
it('cannot connect to unconfigured adapter', async () => {
493+
await reconfigureServer({
494+
auth: {},
495+
});
496+
const provider = getMockFacebookProvider();
497+
Parse.User._registerAuthenticationProvider(provider);
498+
const user = new Parse.User();
499+
user.set('foo', 'bar');
500+
await expectAsync(user._linkWith('facebook', {})).toBeRejectedWith(
501+
new Parse.Error(Parse.Error.UNSUPPORTED_SERVICE, 'This authentication method is unsupported.')
502+
);
503+
});
504+
492505
it('should not call beforeLogin with become', async done => {
493506
const provider = getMockFacebookProvider();
494507
Parse.User._registerAuthenticationProvider(provider);

src/Adapters/Auth/AuthAdapter.js

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -100,16 +100,26 @@ export class AuthAdapter {
100100
* @param {Object} options additional adapter options
101101
* @returns {Promise<Object>} Any overrides required to authData
102102
*/
103-
afterFind(authData, options) {
104-
return Promise.resolve({});
105-
}
103+
afterFind(authData, options) {}
106104

107105
/**
108106
* Triggered when the adapter is first attached to Parse Server
109107
* @param {Object} options Adapter Options
110108
*/
111-
validateOptions(options) {
112-
/* */
109+
validateOptions(options) {}
110+
111+
_clearDefaultKeys(keys) {
112+
const defaultAdapter = new AuthAdapter();
113+
keys.forEach(key => {
114+
const existing = this[key];
115+
if (
116+
existing &&
117+
typeof existing === 'function' &&
118+
existing.toString() === defaultAdapter[key].toString()
119+
) {
120+
this[key] = null;
121+
}
122+
});
113123
}
114124
}
115125

src/Adapters/Auth/index.js

Lines changed: 30 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import loadAdapter from '../AdapterLoader';
22
import Parse from 'parse/node';
33
import AuthAdapter from './AuthAdapter';
4-
54
const apple = require('./apple');
65
const gcenter = require('./gcenter');
76
const gpgames = require('./gpgames');
@@ -136,7 +135,6 @@ function authDataValidator(provider, adapter, appIds, options) {
136135
}
137136

138137
function loadAuthAdapter(provider, authOptions) {
139-
// providers are auth providers implemented by default
140138
let defaultAdapter = providers[provider];
141139
// authOptions can contain complete custom auth adapters or
142140
// a default auth adapter like Facebook
@@ -154,8 +152,6 @@ function loadAuthAdapter(provider, authOptions) {
154152
return;
155153
}
156154

157-
const adapter =
158-
defaultAdapter instanceof AuthAdapter ? defaultAdapter : Object.assign({}, defaultAdapter);
159155
const keys = [
160156
'validateAuthData',
161157
'validateAppId',
@@ -167,20 +163,13 @@ function loadAuthAdapter(provider, authOptions) {
167163
'policy',
168164
'afterFind',
169165
];
170-
const defaultAuthAdapter = new AuthAdapter();
171-
keys.forEach(key => {
172-
const existing = adapter?.[key];
173-
if (
174-
existing &&
175-
typeof existing === 'function' &&
176-
existing.toString() === defaultAuthAdapter[key].toString()
177-
) {
178-
adapter[key] = null;
179-
}
180-
});
181-
const appIds = providerOptions ? providerOptions.appIds : undefined;
182166

183-
// Try the configuration methods
167+
let adapter = Object.assign({}, defaultAdapter);
168+
if (defaultAdapter instanceof AuthAdapter) {
169+
adapter = new defaultAdapter.constructor();
170+
defaultAdapter._clearDefaultKeys(keys);
171+
}
172+
184173
if (providerOptions) {
185174
const optionalAdapter = loadAdapter(providerOptions, undefined, providerOptions);
186175
if (optionalAdapter) {
@@ -191,25 +180,44 @@ function loadAuthAdapter(provider, authOptions) {
191180
});
192181
}
193182
}
194-
if (adapter.validateOptions) {
195-
adapter.validateOptions(providerOptions);
183+
184+
if (providerOptions?.enabled !== false) {
185+
if (adapter.validateOptions) {
186+
adapter.validateOptions(providerOptions);
187+
}
196188
}
197189

190+
const appIds = providerOptions ? providerOptions.appIds : undefined;
198191
return { adapter, appIds, providerOptions };
199192
}
200193

194+
function validateAuthConfig(auth) {
195+
const authCache = new Map();
196+
if (!auth.anonymous) {
197+
auth.anonymous = { enabled: true };
198+
}
199+
Object.keys(auth).forEach(key => {
200+
const authObject = loadAuthAdapter(key, auth);
201+
authCache.set(key, authObject);
202+
});
203+
return authCache;
204+
}
205+
201206
module.exports = function (authOptions = {}, enableAnonymousUsers = true) {
202207
let _enableAnonymousUsers = enableAnonymousUsers;
203208
const setEnableAnonymousUsers = function (enable) {
204209
_enableAnonymousUsers = enable;
205210
};
211+
const authCache = validateAuthConfig(authOptions);
206212
// To handle the test cases on configuration
207213
const getValidatorForProvider = function (provider) {
208214
if (provider === 'anonymous' && !_enableAnonymousUsers) {
209215
return { validator: undefined };
210216
}
211-
const authAdapter = loadAuthAdapter(provider, authOptions);
212-
if (!authAdapter) return;
217+
const authAdapter = authCache.get(provider);
218+
if (!authAdapter) {
219+
return { validator: undefined };
220+
}
213221
const { adapter, appIds, providerOptions } = authAdapter;
214222
return { validator: authDataValidator(provider, adapter, appIds, providerOptions), adapter };
215223
};
@@ -248,6 +256,7 @@ module.exports = function (authOptions = {}, enableAnonymousUsers = true) {
248256
getValidatorForProvider,
249257
setEnableAnonymousUsers,
250258
runAfterFind,
259+
authCache,
251260
});
252261
};
253262

src/Auth.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -488,18 +488,18 @@ const handleAuthDataValidation = async (authData, req, foundUser) => {
488488
}
489489
const { validator } = req.config.authDataManager.getValidatorForProvider(provider);
490490
const authProvider = (req.config.auth || {})[provider] || {};
491-
if (authProvider.enabled == null) {
492-
Deprecator.logRuntimeDeprecation({
493-
usage: `Using the authentication adapter "${provider}" without explicitly enabling it`,
494-
solution: `Enable the authentication adapter by setting the Parse Server option "auth.${provider}.enabled: true".`,
495-
});
496-
}
497491
if (!validator || authProvider.enabled === false) {
498492
throw new Parse.Error(
499493
Parse.Error.UNSUPPORTED_SERVICE,
500494
'This authentication method is unsupported.'
501495
);
502496
}
497+
if (authProvider.enabled == null) {
498+
Deprecator.logRuntimeDeprecation({
499+
usage: `Using the authentication adapter "${provider}" without explicitly enabling it`,
500+
solution: `Enable the authentication adapter by setting the Parse Server option "auth.${provider}.enabled: true".`,
501+
});
502+
}
503503
let validationResult = await validator(authData[provider], req, user, requestObject);
504504
method = validationResult && validationResult.method;
505505
requestObject.triggerName = method;

src/Routers/UsersRouter.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -566,7 +566,7 @@ export class UsersRouter extends ClassesRouter {
566566
for (const provider of Object.keys(challengeData).sort()) {
567567
try {
568568
const authAdapter = req.config.authDataManager.getValidatorForProvider(provider);
569-
if (!authAdapter) {
569+
if (!authAdapter?.validator) {
570570
continue;
571571
}
572572
const {

0 commit comments

Comments
 (0)