diff --git a/spec/AuthenticationAdaptersV2.spec.js b/spec/AuthenticationAdaptersV2.spec.js index 244349a89ff..4c63b9a1ef0 100644 --- a/spec/AuthenticationAdaptersV2.spec.js +++ b/spec/AuthenticationAdaptersV2.spec.js @@ -59,6 +59,19 @@ describe('Auth Adapter features', () => { validateLogin: () => Promise.resolve(), }; + const modernAdapter3 = { + validateAppId: () => Promise.resolve(), + validateSetUp: () => Promise.resolve(), + validateUpdate: () => Promise.resolve(), + validateLogin: () => Promise.resolve(), + validateOptions: () => Promise.resolve(), + afterFind() { + return { + foo: 'bar', + }; + }, + }; + const wrongAdapter = { validateAppId: () => Promise.resolve(), }; @@ -332,6 +345,17 @@ describe('Auth Adapter features', () => { expect(user.getSessionToken()).toBeDefined(); }); + it('should strip out authData if required', async () => { + const spy = spyOn(modernAdapter3, 'validateOptions').and.callThrough(); + await reconfigureServer({ auth: { modernAdapter3 }, silent: false }); + const user = new Parse.User(); + await user.save({ authData: { modernAdapter3: { id: 'modernAdapter3Data' } } }); + await user.fetch({ sessionToken: user.getSessionToken() }); + const authData = user.get('authData').modernAdapter3; + expect(authData).toEqual({ foo: 'bar' }); + expect(spy).toHaveBeenCalled(); + }); + it('should throw if no triggers found', async () => { await reconfigureServer({ auth: { wrongAdapter } }); const user = new Parse.User(); diff --git a/spec/MongoStorageAdapter.spec.js b/spec/MongoStorageAdapter.spec.js index 06fdab4fcaa..1b5cc0c5e90 100644 --- a/spec/MongoStorageAdapter.spec.js +++ b/spec/MongoStorageAdapter.spec.js @@ -248,7 +248,7 @@ describe_only_db('mongo')('MongoStorageAdapter', () => { expect(object.date[0] instanceof Date).toBeTrue(); expect(object.bar.date[0] instanceof Date).toBeTrue(); expect(object.foo.test.date[0] instanceof Date).toBeTrue(); - const obj = await new Parse.Query('MyClass').first({useMasterKey: true}); + const obj = await new Parse.Query('MyClass').first({ useMasterKey: true }); expect(obj.get('date')[0] instanceof Date).toBeTrue(); expect(obj.get('bar').date[0] instanceof Date).toBeTrue(); expect(obj.get('foo').test.date[0] instanceof Date).toBeTrue(); diff --git a/src/Adapters/Auth/AuthAdapter.js b/src/Adapters/Auth/AuthAdapter.js index 0e106014d50..5b18c751706 100644 --- a/src/Adapters/Auth/AuthAdapter.js +++ b/src/Adapters/Auth/AuthAdapter.js @@ -93,6 +93,24 @@ export class AuthAdapter { challenge(challengeData, authData, options, request) { return Promise.resolve({}); } + + /** + * Triggered when auth data is fetched + * @param {Object} authData authData + * @param {Object} options additional adapter options + * @returns {Promise} Any overrides required to authData + */ + afterFind(authData, options) { + return Promise.resolve({}); + } + + /** + * Triggered when the adapter is first attached to Parse Server + * @param {Object} options Adapter Options + */ + validateOptions(options) { + /* */ + } } export default AuthAdapter; diff --git a/src/Adapters/Auth/index.js b/src/Adapters/Auth/index.js index 79a9e9e0bfc..3440208ebd1 100755 --- a/src/Adapters/Auth/index.js +++ b/src/Adapters/Auth/index.js @@ -154,7 +154,8 @@ function loadAuthAdapter(provider, authOptions) { return; } - const adapter = defaultAdapter instanceof AuthAdapter ? defaultAdapter : Object.assign({}, defaultAdapter); + const adapter = + defaultAdapter instanceof AuthAdapter ? defaultAdapter : Object.assign({}, defaultAdapter); const keys = [ 'validateAuthData', 'validateAppId', @@ -162,12 +163,18 @@ function loadAuthAdapter(provider, authOptions) { 'validateLogin', 'validateUpdate', 'challenge', - 'policy' + 'validateOptions', + 'policy', + 'afterFind', ]; const defaultAuthAdapter = new AuthAdapter(); keys.forEach(key => { const existing = adapter?.[key]; - if (existing && typeof existing === 'function' && existing.toString() === defaultAuthAdapter[key].toString()) { + if ( + existing && + typeof existing === 'function' && + existing.toString() === defaultAuthAdapter[key].toString() + ) { adapter[key] = null; } }); @@ -184,6 +191,9 @@ function loadAuthAdapter(provider, authOptions) { }); } } + if (adapter.validateOptions) { + adapter.validateOptions(providerOptions); + } return { adapter, appIds, providerOptions }; } @@ -204,9 +214,35 @@ module.exports = function (authOptions = {}, enableAnonymousUsers = true) { return { validator: authDataValidator(provider, adapter, appIds, providerOptions), adapter }; }; + const runAfterFind = async authData => { + if (!authData) { + return; + } + const adapters = Object.keys(authData); + await Promise.all( + adapters.map(async provider => { + const authAdapter = getValidatorForProvider(provider); + if (!authAdapter) { + return; + } + const { + adapter: { afterFind }, + providerOptions, + } = authAdapter; + if (afterFind && typeof afterFind === 'function') { + const result = afterFind(authData[provider], providerOptions); + if (result) { + authData[provider] = result; + } + } + }) + ); + }; + return Object.freeze({ getValidatorForProvider, setEnableAnonymousUsers, + runAfterFind, }); }; diff --git a/src/RestQuery.js b/src/RestQuery.js index f936a5a7a81..1f4304e78ad 100644 --- a/src/RestQuery.js +++ b/src/RestQuery.js @@ -223,6 +223,9 @@ RestQuery.prototype.execute = function (executeOptions) { .then(() => { return this.runAfterFindTrigger(); }) + .then(() => { + return this.handleAuthAdapters(); + }) .then(() => { return this.response; }); @@ -842,6 +845,15 @@ RestQuery.prototype.runAfterFindTrigger = function () { }); }; +RestQuery.prototype.handleAuthAdapters = async function () { + if (this.className !== '_User' || this.findOptions.explain) { + return; + } + await Promise.all( + this.response.results.map(result => this.config.authDataManager.runAfterFind(result.authData)) + ); +}; + // Adds included values to the response. // Path is a list of field names. // Returns a promise for an augmented response. diff --git a/src/Routers/UsersRouter.js b/src/Routers/UsersRouter.js index 4a72fdd73bf..e26d2ef141f 100644 --- a/src/Routers/UsersRouter.js +++ b/src/Routers/UsersRouter.js @@ -292,6 +292,7 @@ export class UsersRouter extends ClassesRouter { if (authDataResponse) { user.authDataResponse = authDataResponse; } + await req.config.authDataManager.runAfterFind(user.authData); return { response: user }; }