feat: Add afterFind trigger to authentication adapters (#8444)
This commit is contained in:
@@ -59,6 +59,19 @@ describe('Auth Adapter features', () => {
|
|||||||
validateLogin: () => Promise.resolve(),
|
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 = {
|
const wrongAdapter = {
|
||||||
validateAppId: () => Promise.resolve(),
|
validateAppId: () => Promise.resolve(),
|
||||||
};
|
};
|
||||||
@@ -332,6 +345,17 @@ describe('Auth Adapter features', () => {
|
|||||||
expect(user.getSessionToken()).toBeDefined();
|
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 () => {
|
it('should throw if no triggers found', async () => {
|
||||||
await reconfigureServer({ auth: { wrongAdapter } });
|
await reconfigureServer({ auth: { wrongAdapter } });
|
||||||
const user = new Parse.User();
|
const user = new Parse.User();
|
||||||
|
|||||||
@@ -248,7 +248,7 @@ describe_only_db('mongo')('MongoStorageAdapter', () => {
|
|||||||
expect(object.date[0] instanceof Date).toBeTrue();
|
expect(object.date[0] instanceof Date).toBeTrue();
|
||||||
expect(object.bar.date[0] instanceof Date).toBeTrue();
|
expect(object.bar.date[0] instanceof Date).toBeTrue();
|
||||||
expect(object.foo.test.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('date')[0] instanceof Date).toBeTrue();
|
||||||
expect(obj.get('bar').date[0] instanceof Date).toBeTrue();
|
expect(obj.get('bar').date[0] instanceof Date).toBeTrue();
|
||||||
expect(obj.get('foo').test.date[0] instanceof Date).toBeTrue();
|
expect(obj.get('foo').test.date[0] instanceof Date).toBeTrue();
|
||||||
|
|||||||
@@ -93,6 +93,24 @@ export class AuthAdapter {
|
|||||||
challenge(challengeData, authData, options, request) {
|
challenge(challengeData, authData, options, request) {
|
||||||
return Promise.resolve({});
|
return Promise.resolve({});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Triggered when auth data is fetched
|
||||||
|
* @param {Object} authData authData
|
||||||
|
* @param {Object} options additional adapter options
|
||||||
|
* @returns {Promise<Object>} 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;
|
export default AuthAdapter;
|
||||||
|
|||||||
@@ -154,7 +154,8 @@ function loadAuthAdapter(provider, authOptions) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const adapter = defaultAdapter instanceof AuthAdapter ? defaultAdapter : Object.assign({}, defaultAdapter);
|
const adapter =
|
||||||
|
defaultAdapter instanceof AuthAdapter ? defaultAdapter : Object.assign({}, defaultAdapter);
|
||||||
const keys = [
|
const keys = [
|
||||||
'validateAuthData',
|
'validateAuthData',
|
||||||
'validateAppId',
|
'validateAppId',
|
||||||
@@ -162,12 +163,18 @@ function loadAuthAdapter(provider, authOptions) {
|
|||||||
'validateLogin',
|
'validateLogin',
|
||||||
'validateUpdate',
|
'validateUpdate',
|
||||||
'challenge',
|
'challenge',
|
||||||
'policy'
|
'validateOptions',
|
||||||
|
'policy',
|
||||||
|
'afterFind',
|
||||||
];
|
];
|
||||||
const defaultAuthAdapter = new AuthAdapter();
|
const defaultAuthAdapter = new AuthAdapter();
|
||||||
keys.forEach(key => {
|
keys.forEach(key => {
|
||||||
const existing = adapter?.[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;
|
adapter[key] = null;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -184,6 +191,9 @@ function loadAuthAdapter(provider, authOptions) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (adapter.validateOptions) {
|
||||||
|
adapter.validateOptions(providerOptions);
|
||||||
|
}
|
||||||
|
|
||||||
return { adapter, appIds, providerOptions };
|
return { adapter, appIds, providerOptions };
|
||||||
}
|
}
|
||||||
@@ -204,9 +214,35 @@ module.exports = function (authOptions = {}, enableAnonymousUsers = true) {
|
|||||||
return { validator: authDataValidator(provider, adapter, appIds, providerOptions), adapter };
|
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({
|
return Object.freeze({
|
||||||
getValidatorForProvider,
|
getValidatorForProvider,
|
||||||
setEnableAnonymousUsers,
|
setEnableAnonymousUsers,
|
||||||
|
runAfterFind,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -223,6 +223,9 @@ RestQuery.prototype.execute = function (executeOptions) {
|
|||||||
.then(() => {
|
.then(() => {
|
||||||
return this.runAfterFindTrigger();
|
return this.runAfterFindTrigger();
|
||||||
})
|
})
|
||||||
|
.then(() => {
|
||||||
|
return this.handleAuthAdapters();
|
||||||
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
return this.response;
|
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.
|
// Adds included values to the response.
|
||||||
// Path is a list of field names.
|
// Path is a list of field names.
|
||||||
// Returns a promise for an augmented response.
|
// Returns a promise for an augmented response.
|
||||||
|
|||||||
@@ -292,6 +292,7 @@ export class UsersRouter extends ClassesRouter {
|
|||||||
if (authDataResponse) {
|
if (authDataResponse) {
|
||||||
user.authDataResponse = authDataResponse;
|
user.authDataResponse = authDataResponse;
|
||||||
}
|
}
|
||||||
|
await req.config.authDataManager.runAfterFind(user.authData);
|
||||||
|
|
||||||
return { response: user };
|
return { response: user };
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user