Verify User and AccessToken relations at startup
Modify `app.enableAuth()` to verify that (custom) User and AccessToken models have correctly configured their hasMany/belongsTo relations and print a warning otherwise.
This commit is contained in:
parent
f0c9700e1d
commit
79f441b9c4
|
@ -398,9 +398,88 @@ app.enableAuth = function(options) {
|
|||
}
|
||||
};
|
||||
|
||||
this._verifyAuthModelRelations();
|
||||
|
||||
this.isAuthEnabled = true;
|
||||
};
|
||||
|
||||
app._verifyAuthModelRelations = function() {
|
||||
// Allow unit-tests (but also LoopBack users) to disable the warnings
|
||||
if (this.get('_verifyAuthModelRelations') === false) return;
|
||||
|
||||
const AccessToken = this.registry.findModel('AccessToken');
|
||||
const User = this.registry.findModel('User');
|
||||
this.models().forEach(Model => {
|
||||
if (Model === AccessToken || Model.prototype instanceof AccessToken) {
|
||||
scheduleVerification(Model, verifyAccessTokenRelations);
|
||||
}
|
||||
|
||||
if (Model === User || Model.prototype instanceof User) {
|
||||
scheduleVerification(Model, verifyUserRelations);
|
||||
}
|
||||
});
|
||||
|
||||
function scheduleVerification(Model, verifyFn) {
|
||||
if (Model.dataSource) {
|
||||
verifyFn(Model);
|
||||
} else {
|
||||
Model.on('attached', () => verifyFn(Model));
|
||||
}
|
||||
}
|
||||
|
||||
function verifyAccessTokenRelations(Model) {
|
||||
const belongsToUser = Model.relations && Model.relations.user;
|
||||
if (belongsToUser) return;
|
||||
|
||||
const relationsConfig = Model.settings.relations || {};
|
||||
const userName = (relationsConfig.user || {}).model;
|
||||
if (userName) {
|
||||
console.warn(
|
||||
'The model %j configures "belongsTo User-like models" relation ' +
|
||||
'with target model %j. However, the model %j is not attached to ' +
|
||||
'the application and therefore cannot be used by this relation. ' +
|
||||
'This typically happens when the application has a custom ' +
|
||||
'custom User subclass, but does not fix AccessToken relations ' +
|
||||
'to use this new model.\n' +
|
||||
'Learn more at http://ibm.biz/setup-loopback-auth',
|
||||
Model.modelName, userName, userName);
|
||||
return;
|
||||
}
|
||||
|
||||
console.warn(
|
||||
'The model %j does not have "belongsTo User-like model" relation ' +
|
||||
'configured.\n' +
|
||||
'Learn more at http://ibm.biz/setup-loopback-auth',
|
||||
Model.modelName);
|
||||
}
|
||||
|
||||
function verifyUserRelations(Model) {
|
||||
const hasManyTokens = Model.relations && Model.relations.accessTokens;
|
||||
if (hasManyTokens) return;
|
||||
|
||||
const relationsConfig = Model.settings.relations || {};
|
||||
const accessTokenName = (relationsConfig.accessTokens || {}).model;
|
||||
if (accessTokenName) {
|
||||
console.warn(
|
||||
'The model %j configures "hasMany AccessToken-like models" relation ' +
|
||||
'with target model %j. However, the model %j is not attached to ' +
|
||||
'the application and therefore cannot be used by this relation. ' +
|
||||
'This typically happens when the application has a custom ' +
|
||||
'AccessToken subclass, but does not fix User relations to use this ' +
|
||||
'new model.\n' +
|
||||
'Learn more at http://ibm.biz/setup-loopback-auth',
|
||||
Model.modelName, accessTokenName, accessTokenName);
|
||||
return;
|
||||
}
|
||||
|
||||
console.warn(
|
||||
'The model %j does not have "hasMany AccessToken-like models" relation ' +
|
||||
'configured.\n' +
|
||||
'Learn more at http://ibm.biz/setup-loopback-auth',
|
||||
Model.modelName);
|
||||
}
|
||||
};
|
||||
|
||||
app.boot = function(options) {
|
||||
throw new Error(
|
||||
g.f('{{`app.boot`}} was removed, use the new module {{loopback-boot}} instead'));
|
||||
|
|
|
@ -454,6 +454,10 @@ describe('app.enableAuth()', function() {
|
|||
|
||||
ACL = app.registry.getModel('ACL');
|
||||
|
||||
// Fix User's "hasMany accessTokens" relation to use our new MyToken model
|
||||
const User = app.registry.getModel('User');
|
||||
User.settings.relations.accessTokens.model = 'MyToken';
|
||||
|
||||
app.enableAuth({dataSource: 'db'});
|
||||
});
|
||||
beforeEach(createTestingToken);
|
||||
|
|
|
@ -888,6 +888,10 @@ describe('app', function() {
|
|||
var Customer = app.registry.createModel('Customer', {}, {base: 'User'});
|
||||
app.model(Customer, {dataSource: 'db'});
|
||||
|
||||
// Fix AccessToken's "belongsTo user" relation to use our new Customer model
|
||||
const AccessToken = app.registry.getModel('AccessToken');
|
||||
AccessToken.settings.relations.user.model = 'Customer';
|
||||
|
||||
app.enableAuth({dataSource: 'db'});
|
||||
|
||||
expect(Object.keys(app.models)).to.not.include('User');
|
||||
|
|
|
@ -15,5 +15,12 @@
|
|||
"principalId": "$everyone",
|
||||
"property": "create"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"relations": {
|
||||
"user": {
|
||||
"type": "belongsTo",
|
||||
"model": "user",
|
||||
"foreignKey": "userId"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2374,6 +2374,7 @@ describe('User', function() {
|
|||
it('handles subclassed user with no accessToken relation', () => {
|
||||
// setup a new LoopBack app, we don't want to use shared models
|
||||
app = loopback({localRegistry: true, loadBuiltinModels: true});
|
||||
app.set('_verifyAuthModelRelations', false);
|
||||
app.set('remoting', {errorHandler: {debug: true, log: false}});
|
||||
app.dataSource('db', {connector: 'memory'});
|
||||
const User = app.registry.createModel({
|
||||
|
|
Loading…
Reference in New Issue