Add app setting logoutSessionsOnSensitiveChanges
Disable invalidation of access tokens by default to restore backwards compatibility with older 2.x versions. Add a new application-wide flag logoutSessionsOnSensitiveChanges that can be used to explicitly turn on/off the token invalidation. When the flag is not set, a verbose warning is printed to nudge the user to make a decision how they want to handle token invalidation.
This commit is contained in:
parent
f355f66114
commit
f1e31ca50c
|
@ -807,6 +807,31 @@ module.exports = function(User) {
|
|||
UserModel.validatesUniquenessOf('username', {message: 'User already exists'});
|
||||
}
|
||||
|
||||
UserModel.once('attached', function() {
|
||||
if (UserModel.app.get('logoutSessionsOnSensitiveChanges') !== undefined)
|
||||
return;
|
||||
|
||||
g.warn([
|
||||
'',
|
||||
'The user model %j is attached to an application that does not specify',
|
||||
'whether other sessions should be invalidated when a password or',
|
||||
'an email has changed. Session invalidation is important for security',
|
||||
'reasons as it allows users to recover from various account breach',
|
||||
'situations.',
|
||||
'',
|
||||
'We recommend turning this feature on by setting',
|
||||
'"{{logoutSessionsOnSensitiveChanges}}" to {{true}} in',
|
||||
'{{server/config.json}} (unless you have implemented your own solution',
|
||||
'for token invalidation).',
|
||||
'',
|
||||
'We also recommend enabling "{{injectOptionsFromRemoteContext}}" in',
|
||||
'%s\'s settings (typically via common/models/*.json file).',
|
||||
'This setting is required for the invalidation algorithm to keep ',
|
||||
'the current session valid.',
|
||||
''
|
||||
].join('\n'), UserModel.modelName, UserModel.modelName);
|
||||
});
|
||||
|
||||
return UserModel;
|
||||
};
|
||||
|
||||
|
@ -832,6 +857,8 @@ module.exports = function(User) {
|
|||
|
||||
// Delete old sessions once email is updated
|
||||
User.observe('before save', function beforeEmailUpdate(ctx, next) {
|
||||
if (!ctx.Model.app.get('logoutSessionsOnSensitiveChanges')) return next();
|
||||
|
||||
var emailChanged;
|
||||
if (ctx.isNewInstance) return next();
|
||||
if (!ctx.where && !ctx.instance) return next();
|
||||
|
@ -872,6 +899,8 @@ module.exports = function(User) {
|
|||
});
|
||||
|
||||
User.observe('after save', function afterEmailUpdate(ctx, next) {
|
||||
if (!ctx.Model.app.get('logoutSessionsOnSensitiveChanges')) return next();
|
||||
|
||||
if (!ctx.instance && !ctx.data) return next();
|
||||
if (!ctx.hookState.originalUserData) return next();
|
||||
|
||||
|
|
|
@ -590,6 +590,7 @@ function createTestApp(testToken, settings, done) {
|
|||
}, settings.token);
|
||||
|
||||
var app = loopback();
|
||||
app.set('logoutSessionsOnSensitiveChanges', true);
|
||||
|
||||
app.use(cookieParser('secret'));
|
||||
app.use(loopback.token(tokenSettings));
|
||||
|
@ -652,6 +653,7 @@ function createTestApp(testToken, settings, done) {
|
|||
|
||||
function givenLocalTokenModel() {
|
||||
var app = loopback({ localRegistry: true, loadBuiltinModels: true });
|
||||
app.set('logoutSessionsOnSensitiveChanges', true);
|
||||
app.dataSource('db', { connector: 'memory' });
|
||||
|
||||
var User = app.registry.getModel('User');
|
||||
|
|
|
@ -718,6 +718,7 @@ describe('app', function() {
|
|||
|
||||
beforeEach(function() {
|
||||
app = loopback();
|
||||
app.set('logoutSessionsOnSensitiveChanges', true);
|
||||
app.dataSource('db', {
|
||||
connector: 'memory'
|
||||
});
|
||||
|
@ -922,6 +923,7 @@ describe('app', function() {
|
|||
var AUTH_MODELS = ['User', 'ACL', 'AccessToken', 'Role', 'RoleMapping'];
|
||||
var app = loopback({ localRegistry: true, loadBuiltinModels: true });
|
||||
require('../lib/builtin-models')(app.registry);
|
||||
app.set('logoutSessionsOnSensitiveChanges', true);
|
||||
var db = app.dataSource('db', { connector: 'memory' });
|
||||
|
||||
app.enableAuth({ dataSource: 'db' });
|
||||
|
@ -937,6 +939,7 @@ describe('app', function() {
|
|||
|
||||
it('detects already configured subclass of a required model', function() {
|
||||
var app = loopback({ localRegistry: true, loadBuiltinModels: true });
|
||||
app.set('logoutSessionsOnSensitiveChanges', true);
|
||||
var db = app.dataSource('db', { connector: 'memory' });
|
||||
var Customer = app.registry.createModel('Customer', {}, { base: 'User' });
|
||||
app.model(Customer, { dataSource: 'db' });
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
{
|
||||
"port": 3000,
|
||||
"host": "0.0.0.0",
|
||||
"logoutSessionsOnSensitiveChanges": true,
|
||||
"legacyExplorer": false
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
"disableStackTrace": false
|
||||
}
|
||||
},
|
||||
"logoutSessionsOnSensitiveChanges": true,
|
||||
"legacyExplorer": false
|
||||
}
|
||||
|
||||
|
|
|
@ -11,5 +11,6 @@
|
|||
"limit": "8kb"
|
||||
}
|
||||
},
|
||||
"logoutSessionsOnSensitiveChanges": true,
|
||||
"legacyExplorer": false
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,5 +11,6 @@
|
|||
"limit": "8kb"
|
||||
}
|
||||
},
|
||||
"logoutSessionsOnSensitiveChanges": true,
|
||||
"legacyExplorer": false
|
||||
}
|
||||
}
|
||||
|
|
|
@ -856,6 +856,7 @@ describe.onServer('Remote Methods', function() {
|
|||
|
||||
function setupAppAndRequest() {
|
||||
app = loopback({localRegistry: true, loadBuiltinModels: true});
|
||||
app.set('logoutSessionsOnSensitiveChanges', true);
|
||||
|
||||
app.dataSource('db', {connector: 'memory'});
|
||||
|
||||
|
|
|
@ -462,6 +462,7 @@ describe('Replication over REST', function() {
|
|||
|
||||
function setupServer(done) {
|
||||
serverApp = loopback();
|
||||
serverApp.set('logoutSessionsOnSensitiveChanges', true);
|
||||
serverApp.enableAuth();
|
||||
|
||||
serverApp.dataSource('db', { connector: 'memory' });
|
||||
|
@ -514,6 +515,7 @@ describe('Replication over REST', function() {
|
|||
|
||||
function setupClient() {
|
||||
clientApp = loopback();
|
||||
clientApp.set('logoutSessionsOnSensitiveChanges', true);
|
||||
clientApp.dataSource('db', { connector: 'memory' });
|
||||
clientApp.dataSource('remote', {
|
||||
connector: 'remote',
|
||||
|
|
|
@ -13,6 +13,7 @@ describe('loopback.rest', function() {
|
|||
// override the global app object provided by test/support.js
|
||||
// and create a local one that does not share state with other tests
|
||||
app = loopback({ localRegistry: true, loadBuiltinModels: true });
|
||||
app.set('logoutSessionsOnSensitiveChanges', true);
|
||||
var db = app.dataSource('db', { connector: 'memory' });
|
||||
MyModel = app.registry.createModel('MyModel');
|
||||
MyModel.attachTo(db);
|
||||
|
|
|
@ -23,6 +23,7 @@ describe('role model', function() {
|
|||
// Use local app registry to ensure models are isolated to avoid
|
||||
// pollutions from other tests
|
||||
app = loopback({ localRegistry: true, loadBuiltinModels: true });
|
||||
app.set('logoutSessionsOnSensitiveChanges', true);
|
||||
app.dataSource('db', { connector: 'memory' });
|
||||
|
||||
ACL = app.registry.getModel('ACL');
|
||||
|
@ -735,6 +736,7 @@ describe('role model', function() {
|
|||
describe('isOwner', function() {
|
||||
it('supports app-local model registry', function(done) {
|
||||
var app = loopback({ localRegistry: true, loadBuiltinModels: true });
|
||||
app.set('logoutSessionsOnSensitiveChanges', true);
|
||||
app.dataSource('db', { connector: 'memory' });
|
||||
// attach all auth-related models to 'db' datasource
|
||||
app.enableAuth({ dataSource: 'db' });
|
||||
|
|
|
@ -23,6 +23,7 @@ loopback.User.settings.saltWorkFactor = 4;
|
|||
|
||||
beforeEach(function() {
|
||||
this.app = app = loopback();
|
||||
app.set('logoutSessionsOnSensitiveChanges', true);
|
||||
|
||||
// setup default data sources
|
||||
loopback.setDefaultDataSourceForType('db', {
|
||||
|
|
|
@ -31,6 +31,7 @@ describe('User', function() {
|
|||
// override the global app object provided by test/support.js
|
||||
// and create a local one that does not share state with other tests
|
||||
app = loopback({ localRegistry: true, loadBuiltinModels: true });
|
||||
app.set('logoutSessionsOnSensitiveChanges', true);
|
||||
app.dataSource('db', { connector: 'memory' });
|
||||
|
||||
// setup Email model, it's needed by User tests
|
||||
|
@ -2326,6 +2327,17 @@ describe('User', function() {
|
|||
});
|
||||
});
|
||||
|
||||
it('preserves all sessions when logoutSessionsOnSensitiveChanges is disabled',
|
||||
function(done) {
|
||||
app.set('logoutSessionsOnSensitiveChanges', false);
|
||||
user.updateAttributes(
|
||||
{email: updatedEmailCredentials.email},
|
||||
function(err, userInstance) {
|
||||
if (err) return done(err);
|
||||
assertPreservedTokens(done);
|
||||
});
|
||||
});
|
||||
|
||||
function assertPreservedTokens(done) {
|
||||
AccessToken.find({where: {userId: user.id}}, function(err, tokens) {
|
||||
if (err) return done(err);
|
||||
|
|
Loading…
Reference in New Issue