From 148fe24c424e0846f89743d1966e2d13214ca9e5 Mon Sep 17 00:00:00 2001 From: dblythy Date: Sat, 25 Feb 2023 13:19:50 +1100 Subject: [PATCH 1/4] feat: add afterFind to auth adapters --- spec/AuthenticationAdaptersV2.spec.js | 21 +++++++++++++++++++++ src/Adapters/Auth/AuthAdapter.js | 10 ++++++++++ src/Adapters/Auth/index.js | 26 ++++++++++++++++++++++++++ src/RestQuery.js | 12 ++++++++++++ src/Routers/UsersRouter.js | 1 + 5 files changed, 70 insertions(+) diff --git a/spec/AuthenticationAdaptersV2.spec.js b/spec/AuthenticationAdaptersV2.spec.js index 244349a89f..aa62ba8da5 100644 --- a/spec/AuthenticationAdaptersV2.spec.js +++ b/spec/AuthenticationAdaptersV2.spec.js @@ -59,6 +59,18 @@ describe('Auth Adapter features', () => { validateLogin: () => Promise.resolve(), }; + const modernAdapter3 = { + validateAppId: () => Promise.resolve(), + validateSetUp: () => Promise.resolve(), + validateUpdate: () => Promise.resolve(), + validateLogin: () => Promise.resolve(), + afterFind() { + return { + foo: 'bar', + }; + }, + }; + const wrongAdapter = { validateAppId: () => Promise.resolve(), }; @@ -332,6 +344,15 @@ describe('Auth Adapter features', () => { expect(user.getSessionToken()).toBeDefined(); }); + it('should strip out authData if required', async () => { + 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' }); + }); + it('should throw if no triggers found', async () => { await reconfigureServer({ auth: { wrongAdapter } }); const user = new Parse.User(); diff --git a/src/Adapters/Auth/AuthAdapter.js b/src/Adapters/Auth/AuthAdapter.js index 0e106014d5..58ede1a428 100644 --- a/src/Adapters/Auth/AuthAdapter.js +++ b/src/Adapters/Auth/AuthAdapter.js @@ -93,6 +93,16 @@ 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({}); + } } export default AuthAdapter; diff --git a/src/Adapters/Auth/index.js b/src/Adapters/Auth/index.js index 0338bfdcea..6c21bf6415 100755 --- a/src/Adapters/Auth/index.js +++ b/src/Adapters/Auth/index.js @@ -168,6 +168,7 @@ function loadAuthAdapter(provider, authOptions) { 'validateUpdate', 'challenge', 'policy', + 'afterFind' ].forEach(key => { if (optionalAdapter[key]) { adapter[key] = optionalAdapter[key]; @@ -195,9 +196,34 @@ 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 f936a5a7a8..c07a7c3a24 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') { + 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 4a72fdd73b..e26d2ef141 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 }; } From a09f412fdf54071683b02ade2ff2cb31d50fde80 Mon Sep 17 00:00:00 2001 From: dblythy Date: Sat, 25 Feb 2023 13:36:00 +1100 Subject: [PATCH 2/4] Update RestQuery.js --- src/RestQuery.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/RestQuery.js b/src/RestQuery.js index c07a7c3a24..1f4304e78a 100644 --- a/src/RestQuery.js +++ b/src/RestQuery.js @@ -846,7 +846,7 @@ RestQuery.prototype.runAfterFindTrigger = function () { }; RestQuery.prototype.handleAuthAdapters = async function () { - if (this.className !== '_User') { + if (this.className !== '_User' || this.findOptions.explain) { return; } await Promise.all( From 87d549ac4ea1f5ad14bd6fd6ba7ca321b7fb0920 Mon Sep 17 00:00:00 2001 From: dblythy Date: Sat, 25 Feb 2023 16:18:15 +1100 Subject: [PATCH 3/4] add options --- spec/AuthenticationAdaptersV2.spec.js | 3 +++ src/Adapters/Auth/AuthAdapter.js | 8 ++++++++ src/Adapters/Auth/index.js | 5 +++++ 3 files changed, 16 insertions(+) diff --git a/spec/AuthenticationAdaptersV2.spec.js b/spec/AuthenticationAdaptersV2.spec.js index aa62ba8da5..612dbbab44 100644 --- a/spec/AuthenticationAdaptersV2.spec.js +++ b/spec/AuthenticationAdaptersV2.spec.js @@ -64,6 +64,7 @@ describe('Auth Adapter features', () => { validateSetUp: () => Promise.resolve(), validateUpdate: () => Promise.resolve(), validateLogin: () => Promise.resolve(), + validateOptions: () => Promise.resolve(), afterFind() { return { foo: 'bar', @@ -345,7 +346,9 @@ describe('Auth Adapter features', () => { }); it('should strip out authData if required', async () => { + const spy = spyOn(modernAdapter3, 'validateOptions').and.callThrough(); await reconfigureServer({ auth: { modernAdapter3 }, silent: false }); + expect(spy).toHaveBeenCalled(); const user = new Parse.User(); await user.save({ authData: { modernAdapter3: { id: 'modernAdapter3Data' } } }); await user.fetch({ sessionToken: user.getSessionToken() }); diff --git a/src/Adapters/Auth/AuthAdapter.js b/src/Adapters/Auth/AuthAdapter.js index 58ede1a428..5b18c75170 100644 --- a/src/Adapters/Auth/AuthAdapter.js +++ b/src/Adapters/Auth/AuthAdapter.js @@ -103,6 +103,14 @@ export class AuthAdapter { 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 6c21bf6415..ad1a3bf015 100755 --- a/src/Adapters/Auth/index.js +++ b/src/Adapters/Auth/index.js @@ -167,6 +167,7 @@ function loadAuthAdapter(provider, authOptions) { 'validateLogin', 'validateUpdate', 'challenge', + 'validateOptions', 'policy', 'afterFind' ].forEach(key => { @@ -177,6 +178,10 @@ function loadAuthAdapter(provider, authOptions) { } } + if (adapter.validateOptions) { + adapter.validateOptions(); + } + return { adapter, appIds, providerOptions }; } From 74cab45549579e974d6777c871491dc8cfb7b1b6 Mon Sep 17 00:00:00 2001 From: dblythy Date: Sun, 5 Mar 2023 18:10:08 +1100 Subject: [PATCH 4/4] add validateOptions --- spec/AuthenticationAdaptersV2.spec.js | 2 +- src/Adapters/Auth/index.js | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/spec/AuthenticationAdaptersV2.spec.js b/spec/AuthenticationAdaptersV2.spec.js index 612dbbab44..4c63b9a1ef 100644 --- a/spec/AuthenticationAdaptersV2.spec.js +++ b/spec/AuthenticationAdaptersV2.spec.js @@ -348,12 +348,12 @@ describe('Auth Adapter features', () => { it('should strip out authData if required', async () => { const spy = spyOn(modernAdapter3, 'validateOptions').and.callThrough(); await reconfigureServer({ auth: { modernAdapter3 }, silent: false }); - expect(spy).toHaveBeenCalled(); 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 () => { diff --git a/src/Adapters/Auth/index.js b/src/Adapters/Auth/index.js index e20b7ac4e7..3440208ebd 100755 --- a/src/Adapters/Auth/index.js +++ b/src/Adapters/Auth/index.js @@ -191,9 +191,8 @@ function loadAuthAdapter(provider, authOptions) { }); } } - if (adapter.validateOptions) { - adapter.validateOptions(); + adapter.validateOptions(providerOptions); } return { adapter, appIds, providerOptions };