又一个 Promise 问题——嵌套的 Sequelize 调用?
Yet another Promise question - nested Sequelize calls?
我已经阅读了数十篇关于 Promises 的文章,并且在大多数情况下,我得到了它们并且已经使用它们几年了。但是我有一个案例,我正在努力解决并且在我所有的阅读中都没有找到完全匹配 - 除非我没有认出匹配。我的情况是这样的:
我有客户端和服务器应用程序。服务器应用程序是客户端的 API。用户发出登录请求,并在成功调用 Active Directory 并在那里找到用户后,进行 Sequelize 'Users.findOne' 调用并将用户数据 returns 发送到 .then 中的客户端,随后.任何错误都有一个 .catch。
我想在 findOne 成功后添加一个检查和另一个 Sequelize 调用,其中如果来自 Active Directory(办公室、公司、部门)的三个数据点中的任何一个与用户中存储的不匹配 table,我用 Users.update 调用更新它。我不需要 return 此调用的结果给客户端,也不需要通知它任何失败。将两者都记录到服务器就足够了。我使用 winston 进行日志记录。没有和添加的代码如下。两者都有效,但我不确定较长的那个是否正确。
为了完整起见,我包括了对 AD 的 Passport 调用,以及通过 AD 组的替代登录,但您可以忽略这些部分。我在原始代码中添加了一行星号以指示我插入附加代码的位置,并且在第二个列表中,我用几行星号包围了代码,以便您可以轻松找到它。
那么我这样做对吗?
注意:为简洁起见,您可以忽略星号后 return 语句之后的所有内容。这还不到代码的一半。如果您认为可能相关,我只是想让您看看其余部分。
api.post('/login', passport.authenticate('ldapauth', {session: true, failureFlash: 'Invalid username or password.', failWithError: true}),
(req, res, next) => {
// this is only called if authentication was successful. this is called after the serializer.
// the authenticated user is found in req.user.
let adGroups = req.user.memberOf;
let office = req.user.physicalDeliveryOfficeName;
let company = req.user.company;
let department = req.user.department;
// console.log('Member Of', adGroups);
if (typeof req.user.sessionID !== 'undefined') {
winston.info(`\"Login Successful for \"${req.user.displayName}\" (${req.user.sAMAccountName})\" - SessionID: ${req.user.sessionID} ${req.ip} \"${req.method} ${req.originalUrl} HTTP/${req.httpVersion}\" \"${req.headers['referer']}\" \"${req.headers['user-agent']}\" \"${req.headers['content-length']}\"`,
{username: req.user.sAMAccountName, sessionID: req.user.sessionID, ip: req.ip, referrer: req.headers['referer'], url: req.originalUrl, query: req.method, route: 'Authentication'});
} else {
winston.info(`\"Login Successful for \"${req.user.displayName}\" (${req.user.sAMAccountName})\" - SessionID: ${req.user.dataValues.sid} ${req.ip} \"${req.method} ${req.originalUrl} HTTP/${req.httpVersion}\" \"${req.headers['referer']}\" \"${req.headers['user-agent']}\" \"${req.headers['content-length']}\"`,
{username: req.user.sAMAccountName, sessionID: req.user.dataValues.sid, ip: req.ip, referrer: req.headers['referer'], url: req.originalUrl, query: req.method, route: 'Authentication'});
}
// console.log('Req user: ', req.user);
// console.log('right here 1')
let tempAbilities = [];
let userAbilities = [];
let allAbilities = displayAbilities();
let include = [{
model: Roles,
where: {
active: 1
},
include: [ RolesPerms ]
}];
let options = {
where: {
username: req.user.sAMAccountName
},
include: include
};
let userID = null;
// TODO check if found user is 'active === 0' and if so, fail the authentication; use where clause "active != 0"
// console.log('right here 2')
Users.findOne(options).then(userResult => {
// console.log('user result', userResult);
if (userResult === null) {
if (typeof req.user.sessionID !== 'undefined') {
winston.info(`\"Login user not found - looking for group for \"${req.user.displayName}\" (${req.user.sAMAccountName})\" - SessionID: ${req.user.sessionID} ${req.ip} \"${req.method} ${req.originalUrl} HTTP/${req.httpVersion}\" \"${req.headers['referer']}\" \"${req.headers['user-agent']}\" \"${req.headers['content-length']}\"`,
{username: req.user.sAMAccountName, sessionID: req.user.sessionID, ip: req.ip, referrer: req.headers['referer'], url: req.originalUrl, query: req.method, route: 'Authentication'});
} else {
winston.info(`\"Login user not found - looking for group for \"${req.user.displayName}\" (${req.user.sAMAccountName})\" - SessionID: ${req.user.dataValues.sid} ${req.ip} \"${req.method} ${req.originalUrl} HTTP/${req.httpVersion}\" \"${req.headers['referer']}\" \"${req.headers['user-agent']}\" \"${req.headers['content-length']}\"`,
{username: req.user.sAMAccountName, sessionID: req.user.dataValues.sid, ip: req.ip, referrer: req.headers['referer'], url: req.originalUrl, query: req.method, route: 'Authentication'});
}
throw new Error('User not in SutterNow DB.')
}
userID = userResult.dataValues.id;
userResult.roles.forEach(rElement => {
rElement.roles_perms.forEach(rpElement => {
// console.log('rpElement', rpElement);
// if (abilities.findIndex(x => x.prop=="propVal")) --> an example of using an object property to find index
if (tempAbilities.indexOf(rpElement.dataValues.permission_name) === -1) {
tempAbilities.push(rpElement.dataValues.permission_name);
userAbilities.push(allAbilities[allAbilities.findIndex(x => x.name === rpElement.dataValues.permission_name)]);
}
})
});
req.session.rules = userAbilities;
let location = {
office: office,
company: company,
department: department
}
req.session.location = location
/****************************************************************************************************/
return res.json({id: userID, rules: userAbilities, location: location, "Message": "Login Successful"});
}).catch(err => {
// console.log('ah crap')
if (typeof req.user.sessionID !== 'undefined') {
winston.info(`\"Looking for group for \"${req.user.displayName}\" (${req.user.sAMAccountName})\" - SessionID: ${req.user.sessionID} ${req.ip} \"${req.method} ${req.originalUrl} HTTP/${req.httpVersion}\" \"${req.headers['referer']}\" \"${req.headers['user-agent']}\" \"${req.headers['content-length']}\"`,
{username: req.user.sAMAccountName, sessionID: req.user.sessionID, ip: req.ip, referrer: req.headers['referer'], url: req.originalUrl, query: req.method, route: 'Authentication'});
} else {
winston.info(`\"Looking for group for \"${req.user.displayName}\" (${req.user.sAMAccountName})\" - SessionID: ${req.user.dataValues.sid} ${req.ip} \"${req.method} ${req.originalUrl} HTTP/${req.httpVersion}\" \"${req.headers['referer']}\" \"${req.headers['user-agent']}\" \"${req.headers['content-length']}\"`,
{username: req.user.sAMAccountName, sessionID: req.user.dataValues.sid, ip: req.ip, referrer: req.headers['referer'], url: req.originalUrl, query: req.method, route: 'Authentication'});
}
const promA = Groups.findAll({
where: {
active: 1
},
include: [{
model: Roles,
where: {
active: 1
},
include: [ RolesPerms ]
}],
order: ['displayName']
});
const promB = GroupsRoles.findAll();
// console.error(err);
// user not found in our DB, but they're in AD; let's check if they belong to an approved group
Promise.all([promA, promB]).then(responses => {
// console.log('Response 1', responses[0]);
// console.log('Response 2', responses[1]);
if (typeof req.user.sessionID !== 'undefined') {
winston.info(`\"Found group results for \"${req.user.displayName}\" (${req.user.sAMAccountName})\" - SessionID: ${req.user.sessionID} ${req.ip} \"${req.method} ${req.originalUrl} HTTP/${req.httpVersion}\" \"${req.headers['referer']}\" \"${req.headers['user-agent']}\" \"${req.headers['content-length']}\"`,
{username: req.user.sAMAccountName, sessionID: req.user.sessionID, ip: req.ip, referrer: req.headers['referer'], url: req.originalUrl, query: req.method, route: 'Authentication'});
} else {
winston.info(`\"Found group results for \"${req.user.displayName}\" (${req.user.sAMAccountName})\" - SessionID: ${req.user.dataValues.sid} ${req.ip} \"${req.method} ${req.originalUrl} HTTP/${req.httpVersion}\" \"${req.headers['referer']}\" \"${req.headers['user-agent']}\" \"${req.headers['content-length']}\"`,
{username: req.user.sAMAccountName, sessionID: req.user.dataValues.sid, ip: req.ip, referrer: req.headers['referer'], url: req.originalUrl, query: req.method, route: 'Authentication'});
}
let foundGroup = ''
let foundRoles = []
let foundPerms = []
responses[0].forEach(el => {
// console.log('our el', el)
// console.log('our group', el.dataValues)
for (let j = 0; j < adGroups.length; j++) {
// console.log('adGroups j', adGroups[j])
if (adGroups[j].match(el.dataValues.username)) {
// console.log('found it', el.dataValues)
userID = el.id
foundGroup = el.dataValues.username;
foundRoles = el.dataValues;
break // TODO allow for membership in multiple groups, like I do multiple roles, below
}
}
});
foundRoles.roles.forEach(role => {
// console.log('roles_perms things', role.roles_perms);
role.roles_perms.forEach(roleP => {
if (foundPerms.indexOf(roleP.dataValues.permission_name) === -1) {
foundPerms.push(roleP.dataValues.permission_name);
userAbilities.push(allAbilities[allAbilities.findIndex(x => x.name === roleP.dataValues.permission_name)]);
}
})
});
req.session.rules = userAbilities;
let location = {
office: office,
company: company,
department: department
}
req.session.location = location
return res.json({id: 0, rules: userAbilities, location: location, "Message": "Login Successful via group membership"});
}).catch(err => {
console.error('the error ' + err);
return res.json({"Message": "Login failed. You neither had a user account in SutterNow, nor did you belong to a valid AD Group."})
});
});
return null;
// res.json({"Message":"Login successful"});
},
(err, req, res, next) => {
/* failWithErrors: true, above, makes this section get called to handle the error.
We don't handle the logging nor the json return here; instead, we setup the error object and pass it on
to the error handler which does those things. */
console.log('Authentication failed; passing error on to error handler...');
err.route = 'Authentication';
err.statusCode = 401;
err.status = 401;
err.shouldRedirect = req.headers['user-agent'].indexOf('Postman') > -1;
if (typeof req.flash === 'function' && typeof req.flash('error') !== 'undefined' && req.flash('error').length !== 0) {
err.message = req.flash('error').slice(-1)[0];
console.log('Flash error ' + req.flash('error').slice(-1)[0]);
res.statusMessage = req.flash('error').slice(-1)[0];
}
if (app.get('env') === 'production') {
console.log('stack redacted');
err.stack = '';// We want to obscure any data the user shouldn't see.
}
next(err);
// return res.json({"Message": "Login failed. " + err.message});
// return null;
}
);
api.post('/login', passport.authenticate('ldapauth', {session: true, failureFlash: 'Invalid username or password.', failWithError: true}),
(req, res, next) => {
// this is only called if authentication was successful. this is called after the serializer.
// the authenticated user is found in req.user.
let adGroups = req.user.memberOf;
let office = req.user.physicalDeliveryOfficeName;
let company = req.user.company;
let department = req.user.department;
// console.log('Member Of', adGroups);
if (typeof req.user.sessionID !== 'undefined') {
winston.info(`\"Login Successful for \"${req.user.displayName}\" (${req.user.sAMAccountName})\" - SessionID: ${req.user.sessionID} ${req.ip} \"${req.method} ${req.originalUrl} HTTP/${req.httpVersion}\" \"${req.headers['referer']}\" \"${req.headers['user-agent']}\" \"${req.headers['content-length']}\"`,
{username: req.user.sAMAccountName, sessionID: req.user.sessionID, ip: req.ip, referrer: req.headers['referer'], url: req.originalUrl, query: req.method, route: 'Authentication'});
} else {
winston.info(`\"Login Successful for \"${req.user.displayName}\" (${req.user.sAMAccountName})\" - SessionID: ${req.user.dataValues.sid} ${req.ip} \"${req.method} ${req.originalUrl} HTTP/${req.httpVersion}\" \"${req.headers['referer']}\" \"${req.headers['user-agent']}\" \"${req.headers['content-length']}\"`,
{username: req.user.sAMAccountName, sessionID: req.user.dataValues.sid, ip: req.ip, referrer: req.headers['referer'], url: req.originalUrl, query: req.method, route: 'Authentication'});
}
// console.log('Req user: ', req.user);
// console.log('right here 1')
let tempAbilities = [];
let userAbilities = [];
let allAbilities = displayAbilities();
let include = [{
model: Roles,
where: {
active: 1
},
include: [ RolesPerms ]
}];
let options = {
where: {
username: req.user.sAMAccountName
},
include: include
};
let userID = null;
// TODO check if found user is 'active === 0' and if so, fail the authentication; use where clause "active != 0"
// console.log('right here 2')
Users.findOne(options).then(userResult => {
// console.log('user result', userResult);
if (userResult === null) {
if (typeof req.user.sessionID !== 'undefined') {
winston.info(`\"Login user not found - looking for group for \"${req.user.displayName}\" (${req.user.sAMAccountName})\" - SessionID: ${req.user.sessionID} ${req.ip} \"${req.method} ${req.originalUrl} HTTP/${req.httpVersion}\" \"${req.headers['referer']}\" \"${req.headers['user-agent']}\" \"${req.headers['content-length']}\"`,
{username: req.user.sAMAccountName, sessionID: req.user.sessionID, ip: req.ip, referrer: req.headers['referer'], url: req.originalUrl, query: req.method, route: 'Authentication'});
} else {
winston.info(`\"Login user not found - looking for group for \"${req.user.displayName}\" (${req.user.sAMAccountName})\" - SessionID: ${req.user.dataValues.sid} ${req.ip} \"${req.method} ${req.originalUrl} HTTP/${req.httpVersion}\" \"${req.headers['referer']}\" \"${req.headers['user-agent']}\" \"${req.headers['content-length']}\"`,
{username: req.user.sAMAccountName, sessionID: req.user.dataValues.sid, ip: req.ip, referrer: req.headers['referer'], url: req.originalUrl, query: req.method, route: 'Authentication'});
}
throw new Error('User not in SutterNow DB.')
}
userID = userResult.dataValues.id;
userResult.roles.forEach(rElement => {
rElement.roles_perms.forEach(rpElement => {
// console.log('rpElement', rpElement);
// if (abilities.findIndex(x => x.prop=="propVal")) --> an example of using an object property to find index
if (tempAbilities.indexOf(rpElement.dataValues.permission_name) === -1) {
tempAbilities.push(rpElement.dataValues.permission_name);
userAbilities.push(allAbilities[allAbilities.findIndex(x => x.name === rpElement.dataValues.permission_name)]);
}
})
});
req.session.rules = userAbilities;
let location = {
office: office,
company: company,
department: department
}
req.session.location = location
/****************************************************************************************************/
// TODO is this supposed to be another .then chained between this one and the catch (or before this one)? if so, combine the catch?
if (userResult.dataValues.office !== office || userResult.dataValues.department !== department || userResult.dataValues.company !== company) {
// update the database with the new AD values
console.log('updating user')
Users.update({
// values
office: office,
department: department,
company: company
}, {
// options
where: {
id: userID
}
}).then(numAffected => {
winston.info(`\"Updated ${numAffected} user for ${req.user.dataValues.username}\" - SessionID: ${req.sessionID} ${req.ip} \"${req.method} ${req.originalUrl} HTTP/${req.httpVersion}\" \"${req.headers['referer']}\" \"${req.headers['user-agent']}\" \"${req.headers['content-length']}\"`,
{username: req.user.dataValues.username, sessionID: req.sessionID, ip: req.ip, referrer: req.headers['referer'], url: req.originalUrl, query: req.method, route: 'User Admin'});
return;
}).catch((err) => {
if (err) {
// Not sure how to get an error here. ensureAuthenticated handles invalid users attempting this PUT.
// console.log(err);
err.route = 'User Admin';
err.statusCode = 'UPDATE_USER_AT_LOGIN_BY_ID_ERROR';
err.status = 'UPDATE USER AT LOGIN BY ID ERROR';
err.shouldRedirect = req.headers['user-agent'].indexOf('Postman') > -1;
if (app.get('env') === 'production') {
console.log('stack redacted');
err.stack = '';// We want to obscure any data the user shouldn't see.
}
next(err);
}
});
}
/****************************************************************************************************/
return res.json({id: userID, rules: userAbilities, location: location, "Message": "Login Successful"});
}).catch(err => {
// console.log('ah crap')
if (typeof req.user.sessionID !== 'undefined') {
winston.info(`\"Looking for group for \"${req.user.displayName}\" (${req.user.sAMAccountName})\" - SessionID: ${req.user.sessionID} ${req.ip} \"${req.method} ${req.originalUrl} HTTP/${req.httpVersion}\" \"${req.headers['referer']}\" \"${req.headers['user-agent']}\" \"${req.headers['content-length']}\"`,
{username: req.user.sAMAccountName, sessionID: req.user.sessionID, ip: req.ip, referrer: req.headers['referer'], url: req.originalUrl, query: req.method, route: 'Authentication'});
} else {
winston.info(`\"Looking for group for \"${req.user.displayName}\" (${req.user.sAMAccountName})\" - SessionID: ${req.user.dataValues.sid} ${req.ip} \"${req.method} ${req.originalUrl} HTTP/${req.httpVersion}\" \"${req.headers['referer']}\" \"${req.headers['user-agent']}\" \"${req.headers['content-length']}\"`,
{username: req.user.sAMAccountName, sessionID: req.user.dataValues.sid, ip: req.ip, referrer: req.headers['referer'], url: req.originalUrl, query: req.method, route: 'Authentication'});
}
const promA = Groups.findAll({
where: {
active: 1
},
include: [{
model: Roles,
where: {
active: 1
},
include: [ RolesPerms ]
}],
order: ['displayName']
});
const promB = GroupsRoles.findAll();
// console.error(err);
// user not found in our DB, but they're in AD; let's check if they belong to an approved group
Promise.all([promA, promB]).then(responses => {
// console.log('Response 1', responses[0]);
// console.log('Response 2', responses[1]);
if (typeof req.user.sessionID !== 'undefined') {
winston.info(`\"Found group results for \"${req.user.displayName}\" (${req.user.sAMAccountName})\" - SessionID: ${req.user.sessionID} ${req.ip} \"${req.method} ${req.originalUrl} HTTP/${req.httpVersion}\" \"${req.headers['referer']}\" \"${req.headers['user-agent']}\" \"${req.headers['content-length']}\"`,
{username: req.user.sAMAccountName, sessionID: req.user.sessionID, ip: req.ip, referrer: req.headers['referer'], url: req.originalUrl, query: req.method, route: 'Authentication'});
} else {
winston.info(`\"Found group results for \"${req.user.displayName}\" (${req.user.sAMAccountName})\" - SessionID: ${req.user.dataValues.sid} ${req.ip} \"${req.method} ${req.originalUrl} HTTP/${req.httpVersion}\" \"${req.headers['referer']}\" \"${req.headers['user-agent']}\" \"${req.headers['content-length']}\"`,
{username: req.user.sAMAccountName, sessionID: req.user.dataValues.sid, ip: req.ip, referrer: req.headers['referer'], url: req.originalUrl, query: req.method, route: 'Authentication'});
}
let foundGroup = ''
let foundRoles = []
let foundPerms = []
responses[0].forEach(el => {
// console.log('our el', el)
// console.log('our group', el.dataValues)
for (let j = 0; j < adGroups.length; j++) {
// console.log('adGroups j', adGroups[j])
if (adGroups[j].match(el.dataValues.username)) {
// console.log('found it', el.dataValues)
userID = el.id
foundGroup = el.dataValues.username;
foundRoles = el.dataValues;
break // TODO allow for membership in multiple groups, like I do multiple roles, below
}
}
});
foundRoles.roles.forEach(role => {
// console.log('roles_perms things', role.roles_perms);
role.roles_perms.forEach(roleP => {
if (foundPerms.indexOf(roleP.dataValues.permission_name) === -1) {
foundPerms.push(roleP.dataValues.permission_name);
userAbilities.push(allAbilities[allAbilities.findIndex(x => x.name === roleP.dataValues.permission_name)]);
}
})
});
req.session.rules = userAbilities;
let location = {
office: office,
company: company,
department: department
}
req.session.location = location
return res.json({id: 0, rules: userAbilities, location: location, "Message": "Login Successful via group membership"});
}).catch(err => {
console.error('the error ' + err);
return res.json({"Message": "Login failed. You neither had a user account in SutterNow, nor did you belong to a valid AD Group."})
});
});
return null;
// res.json({"Message":"Login successful"});
},
(err, req, res, next) => {
/* failWithErrors: true, above, makes this section get called to handle the error.
We don't handle the logging nor the json return here; instead, we setup the error object and pass it on
to the error handler which does those things. */
console.log('Authentication failed; passing error on to error handler...');
err.route = 'Authentication';
err.statusCode = 401;
err.status = 401;
err.shouldRedirect = req.headers['user-agent'].indexOf('Postman') > -1;
if (typeof req.flash === 'function' && typeof req.flash('error') !== 'undefined' && req.flash('error').length !== 0) {
err.message = req.flash('error').slice(-1)[0];
console.log('Flash error ' + req.flash('error').slice(-1)[0]);
res.statusMessage = req.flash('error').slice(-1)[0];
}
if (app.get('env') === 'production') {
console.log('stack redacted');
err.stack = '';// We want to obscure any data the user shouldn't see.
}
next(err);
// return res.json({"Message": "Login failed. " + err.message});
// return null;
}
);
我想我明白了。我将我添加的代码移动到一个单独的 .then,并确保 return 之前的代码不使用 res.json,而只是 return 对象。然后我 return 新对象中的对象。然后使用 res.json 无论用户 table 更新成功还是失败,因此:
api.post('/login', passport.authenticate('ldapauth', {session: true, failureFlash: 'Invalid username or password.', failWithError: true}),
(req, res, next) => {
// this is only called if authentication was successful. this is called after the serializer.
// the authenticated user is found in req.user.
let adGroups = req.user.memberOf;
let office = req.user.physicalDeliveryOfficeName;
let company = req.user.company;
let department = req.user.department;
// console.log('Member Of', adGroups);
if (typeof req.user.sessionID !== 'undefined') {
winston.info(`\"Login Successful for \"${req.user.displayName}\" (${req.user.sAMAccountName})\" - SessionID: ${req.user.sessionID} ${req.ip} \"${req.method} ${req.originalUrl} HTTP/${req.httpVersion}\" \"${req.headers['referer']}\" \"${req.headers['user-agent']}\" \"${req.headers['content-length']}\"`,
{username: req.user.sAMAccountName, sessionID: req.user.sessionID, ip: req.ip, referrer: req.headers['referer'], url: req.originalUrl, query: req.method, route: 'Authentication'});
} else {
winston.info(`\"Login Successful for \"${req.user.displayName}\" (${req.user.sAMAccountName})\" - SessionID: ${req.user.dataValues.sid} ${req.ip} \"${req.method} ${req.originalUrl} HTTP/${req.httpVersion}\" \"${req.headers['referer']}\" \"${req.headers['user-agent']}\" \"${req.headers['content-length']}\"`,
{username: req.user.sAMAccountName, sessionID: req.user.dataValues.sid, ip: req.ip, referrer: req.headers['referer'], url: req.originalUrl, query: req.method, route: 'Authentication'});
}
// console.log('Req user: ', req.user);
// console.log('right here 1')
let tempAbilities = [];
let userAbilities = [];
let allAbilities = displayAbilities();
let include = [{
model: Roles,
where: {
active: 1
},
include: [ RolesPerms ]
}];
let options = {
where: {
username: req.user.sAMAccountName
},
include: include
};
let userID = null;
// TODO check if found user is 'active === 0' and if so, fail the authentication; use where clause "active != 0"
// console.log('right here 2')
Users.findOne(options).then(userResult => {
// console.log('user result', userResult);
if (userResult === null) {
if (typeof req.user.sessionID !== 'undefined') {
winston.info(`\"Login user not found - looking for group for \"${req.user.displayName}\" (${req.user.sAMAccountName})\" - SessionID: ${req.user.sessionID} ${req.ip} \"${req.method} ${req.originalUrl} HTTP/${req.httpVersion}\" \"${req.headers['referer']}\" \"${req.headers['user-agent']}\" \"${req.headers['content-length']}\"`,
{username: req.user.sAMAccountName, sessionID: req.user.sessionID, ip: req.ip, referrer: req.headers['referer'], url: req.originalUrl, query: req.method, route: 'Authentication'});
} else {
winston.info(`\"Login user not found - looking for group for \"${req.user.displayName}\" (${req.user.sAMAccountName})\" - SessionID: ${req.user.dataValues.sid} ${req.ip} \"${req.method} ${req.originalUrl} HTTP/${req.httpVersion}\" \"${req.headers['referer']}\" \"${req.headers['user-agent']}\" \"${req.headers['content-length']}\"`,
{username: req.user.sAMAccountName, sessionID: req.user.dataValues.sid, ip: req.ip, referrer: req.headers['referer'], url: req.originalUrl, query: req.method, route: 'Authentication'});
}
throw new Error('User not in SutterNow DB.')
}
userID = userResult.dataValues.id;
userResult.roles.forEach(rElement => {
rElement.roles_perms.forEach(rpElement => {
// console.log('rpElement', rpElement);
// if (abilities.findIndex(x => x.prop=="propVal")) --> an example of using an object property to find index
if (tempAbilities.indexOf(rpElement.dataValues.permission_name) === -1) {
tempAbilities.push(rpElement.dataValues.permission_name);
userAbilities.push(allAbilities[allAbilities.findIndex(x => x.name === rpElement.dataValues.permission_name)]);
}
})
});
req.session.rules = userAbilities;
let location = {
office: office,
company: company,
department: department
}
req.session.location = location
let adLocation = {
office: userResult.dataValues.office,
company: userResult.dataValues.company,
department: userResult.dataValues.department
}
return {id: userID, rules: userAbilities, location: location, adLocation: adLocation, "Message": "Login Successful"};
}).then(result => {
if (result.adLocation.office !== result.location.office || result.adLocation.department !== result.location.department || result.adLocation.company !== result.location.company) {
// update the database with the new AD values
Users.update({
// values
office: office,
department: department,
company: company
}, {
// options
where: {
id: userID
}
}).then(numAffected => {
winston.info(`\"Updated ${numAffected} user for ${req.user.dataValues.username}\" - SessionID: ${req.sessionID} ${req.ip} \"${req.method} ${req.originalUrl} HTTP/${req.httpVersion}\" \"${req.headers['referer']}\" \"${req.headers['user-agent']}\" \"${req.headers['content-length']}\"`,
{username: req.user.dataValues.username, sessionID: req.sessionID, ip: req.ip, referrer: req.headers['referer'], url: req.originalUrl, query: req.method, route: 'User Admin'});
return res.json(result);
}).catch((err) => {
if (err) {
// Not sure how to get an error here. ensureAuthenticated handles invalid users attempting this PUT.
// console.log(err);
err.route = 'User Admin';
err.statusCode = 'UPDATE_USER_AT_LOGIN_BY_ID_ERROR';
err.status = 'UPDATE USER AT LOGIN BY ID ERROR';
err.shouldRedirect = req.headers['user-agent'].indexOf('Postman') > -1;
if (app.get('env') === 'production') {
console.log('stack redacted');
err.stack = '';// We want to obscure any data the user shouldn't see.
}
next(err);
}
});
} else {
return res.json(result);
}
}).catch(err => {
// console.log('ah crap')
if (typeof req.user.sessionID !== 'undefined') {
winston.info(`\"Looking for group for \"${req.user.displayName}\" (${req.user.sAMAccountName})\" - SessionID: ${req.user.sessionID} ${req.ip} \"${req.method} ${req.originalUrl} HTTP/${req.httpVersion}\" \"${req.headers['referer']}\" \"${req.headers['user-agent']}\" \"${req.headers['content-length']}\"`,
{username: req.user.sAMAccountName, sessionID: req.user.sessionID, ip: req.ip, referrer: req.headers['referer'], url: req.originalUrl, query: req.method, route: 'Authentication'});
} else {
winston.info(`\"Looking for group for \"${req.user.displayName}\" (${req.user.sAMAccountName})\" - SessionID: ${req.user.dataValues.sid} ${req.ip} \"${req.method} ${req.originalUrl} HTTP/${req.httpVersion}\" \"${req.headers['referer']}\" \"${req.headers['user-agent']}\" \"${req.headers['content-length']}\"`,
{username: req.user.sAMAccountName, sessionID: req.user.dataValues.sid, ip: req.ip, referrer: req.headers['referer'], url: req.originalUrl, query: req.method, route: 'Authentication'});
}
const promA = Groups.findAll({
where: {
active: 1
},
include: [{
model: Roles,
where: {
active: 1
},
include: [ RolesPerms ]
}],
order: ['displayName']
});
const promB = GroupsRoles.findAll();
// console.error(err);
// user not found in our DB, but they're in AD; let's check if they belong to an approved group
Promise.all([promA, promB]).then(responses => {
// console.log('Response 1', responses[0]);
// console.log('Response 2', responses[1]);
if (typeof req.user.sessionID !== 'undefined') {
winston.info(`\"Found group results for \"${req.user.displayName}\" (${req.user.sAMAccountName})\" - SessionID: ${req.user.sessionID} ${req.ip} \"${req.method} ${req.originalUrl} HTTP/${req.httpVersion}\" \"${req.headers['referer']}\" \"${req.headers['user-agent']}\" \"${req.headers['content-length']}\"`,
{username: req.user.sAMAccountName, sessionID: req.user.sessionID, ip: req.ip, referrer: req.headers['referer'], url: req.originalUrl, query: req.method, route: 'Authentication'});
} else {
winston.info(`\"Found group results for \"${req.user.displayName}\" (${req.user.sAMAccountName})\" - SessionID: ${req.user.dataValues.sid} ${req.ip} \"${req.method} ${req.originalUrl} HTTP/${req.httpVersion}\" \"${req.headers['referer']}\" \"${req.headers['user-agent']}\" \"${req.headers['content-length']}\"`,
{username: req.user.sAMAccountName, sessionID: req.user.dataValues.sid, ip: req.ip, referrer: req.headers['referer'], url: req.originalUrl, query: req.method, route: 'Authentication'});
}
let foundGroup = ''
let foundRoles = []
let foundPerms = []
responses[0].forEach(el => {
// console.log('our el', el)
// console.log('our group', el.dataValues)
for (let j = 0; j < adGroups.length; j++) {
// console.log('adGroups j', adGroups[j])
if (adGroups[j].match(el.dataValues.username)) {
// console.log('found it', el.dataValues)
userID = el.id
foundGroup = el.dataValues.username;
foundRoles = el.dataValues;
break // TODO allow for membership in multiple groups, like I do multiple roles, below
}
}
});
foundRoles.roles.forEach(role => {
// console.log('roles_perms things', role.roles_perms);
role.roles_perms.forEach(roleP => {
if (foundPerms.indexOf(roleP.dataValues.permission_name) === -1) {
foundPerms.push(roleP.dataValues.permission_name);
userAbilities.push(allAbilities[allAbilities.findIndex(x => x.name === roleP.dataValues.permission_name)]);
}
})
});
req.session.rules = userAbilities;
let location = {
office: office,
company: company,
department: department
}
req.session.location = location
return res.json({id: 0, rules: userAbilities, location: location, "Message": "Login Successful via group membership"});
}).catch(err => {
console.error('the error ' + err);
return res.json({"Message": "Login failed. You neither had a user account in SutterNow, nor did you belong to a valid AD Group."})
});
});
return null;
// res.json({"Message":"Login successful"});
},
(err, req, res, next) => {
/* failWithErrors: true, above, makes this section get called to handle the error.
We don't handle the logging nor the json return here; instead, we setup the error object and pass it on
to the error handler which does those things. */
console.log('Authentication failed; passing error on to error handler...');
err.route = 'Authentication';
err.statusCode = 401;
err.status = 401;
err.shouldRedirect = req.headers['user-agent'].indexOf('Postman') > -1;
if (typeof req.flash === 'function' && typeof req.flash('error') !== 'undefined' && req.flash('error').length !== 0) {
err.message = req.flash('error').slice(-1)[0];
console.log('Flash error ' + req.flash('error').slice(-1)[0]);
res.statusMessage = req.flash('error').slice(-1)[0];
}
if (app.get('env') === 'production') {
console.log('stack redacted');
err.stack = '';// We want to obscure any data the user shouldn't see.
}
next(err);
// return res.json({"Message": "Login failed. " + err.message});
// return null;
}
);
我已经阅读了数十篇关于 Promises 的文章,并且在大多数情况下,我得到了它们并且已经使用它们几年了。但是我有一个案例,我正在努力解决并且在我所有的阅读中都没有找到完全匹配 - 除非我没有认出匹配。我的情况是这样的:
我有客户端和服务器应用程序。服务器应用程序是客户端的 API。用户发出登录请求,并在成功调用 Active Directory 并在那里找到用户后,进行 Sequelize 'Users.findOne' 调用并将用户数据 returns 发送到 .then 中的客户端,随后.任何错误都有一个 .catch。
我想在 findOne 成功后添加一个检查和另一个 Sequelize 调用,其中如果来自 Active Directory(办公室、公司、部门)的三个数据点中的任何一个与用户中存储的不匹配 table,我用 Users.update 调用更新它。我不需要 return 此调用的结果给客户端,也不需要通知它任何失败。将两者都记录到服务器就足够了。我使用 winston 进行日志记录。没有和添加的代码如下。两者都有效,但我不确定较长的那个是否正确。
为了完整起见,我包括了对 AD 的 Passport 调用,以及通过 AD 组的替代登录,但您可以忽略这些部分。我在原始代码中添加了一行星号以指示我插入附加代码的位置,并且在第二个列表中,我用几行星号包围了代码,以便您可以轻松找到它。
那么我这样做对吗?
注意:为简洁起见,您可以忽略星号后 return 语句之后的所有内容。这还不到代码的一半。如果您认为可能相关,我只是想让您看看其余部分。
api.post('/login', passport.authenticate('ldapauth', {session: true, failureFlash: 'Invalid username or password.', failWithError: true}),
(req, res, next) => {
// this is only called if authentication was successful. this is called after the serializer.
// the authenticated user is found in req.user.
let adGroups = req.user.memberOf;
let office = req.user.physicalDeliveryOfficeName;
let company = req.user.company;
let department = req.user.department;
// console.log('Member Of', adGroups);
if (typeof req.user.sessionID !== 'undefined') {
winston.info(`\"Login Successful for \"${req.user.displayName}\" (${req.user.sAMAccountName})\" - SessionID: ${req.user.sessionID} ${req.ip} \"${req.method} ${req.originalUrl} HTTP/${req.httpVersion}\" \"${req.headers['referer']}\" \"${req.headers['user-agent']}\" \"${req.headers['content-length']}\"`,
{username: req.user.sAMAccountName, sessionID: req.user.sessionID, ip: req.ip, referrer: req.headers['referer'], url: req.originalUrl, query: req.method, route: 'Authentication'});
} else {
winston.info(`\"Login Successful for \"${req.user.displayName}\" (${req.user.sAMAccountName})\" - SessionID: ${req.user.dataValues.sid} ${req.ip} \"${req.method} ${req.originalUrl} HTTP/${req.httpVersion}\" \"${req.headers['referer']}\" \"${req.headers['user-agent']}\" \"${req.headers['content-length']}\"`,
{username: req.user.sAMAccountName, sessionID: req.user.dataValues.sid, ip: req.ip, referrer: req.headers['referer'], url: req.originalUrl, query: req.method, route: 'Authentication'});
}
// console.log('Req user: ', req.user);
// console.log('right here 1')
let tempAbilities = [];
let userAbilities = [];
let allAbilities = displayAbilities();
let include = [{
model: Roles,
where: {
active: 1
},
include: [ RolesPerms ]
}];
let options = {
where: {
username: req.user.sAMAccountName
},
include: include
};
let userID = null;
// TODO check if found user is 'active === 0' and if so, fail the authentication; use where clause "active != 0"
// console.log('right here 2')
Users.findOne(options).then(userResult => {
// console.log('user result', userResult);
if (userResult === null) {
if (typeof req.user.sessionID !== 'undefined') {
winston.info(`\"Login user not found - looking for group for \"${req.user.displayName}\" (${req.user.sAMAccountName})\" - SessionID: ${req.user.sessionID} ${req.ip} \"${req.method} ${req.originalUrl} HTTP/${req.httpVersion}\" \"${req.headers['referer']}\" \"${req.headers['user-agent']}\" \"${req.headers['content-length']}\"`,
{username: req.user.sAMAccountName, sessionID: req.user.sessionID, ip: req.ip, referrer: req.headers['referer'], url: req.originalUrl, query: req.method, route: 'Authentication'});
} else {
winston.info(`\"Login user not found - looking for group for \"${req.user.displayName}\" (${req.user.sAMAccountName})\" - SessionID: ${req.user.dataValues.sid} ${req.ip} \"${req.method} ${req.originalUrl} HTTP/${req.httpVersion}\" \"${req.headers['referer']}\" \"${req.headers['user-agent']}\" \"${req.headers['content-length']}\"`,
{username: req.user.sAMAccountName, sessionID: req.user.dataValues.sid, ip: req.ip, referrer: req.headers['referer'], url: req.originalUrl, query: req.method, route: 'Authentication'});
}
throw new Error('User not in SutterNow DB.')
}
userID = userResult.dataValues.id;
userResult.roles.forEach(rElement => {
rElement.roles_perms.forEach(rpElement => {
// console.log('rpElement', rpElement);
// if (abilities.findIndex(x => x.prop=="propVal")) --> an example of using an object property to find index
if (tempAbilities.indexOf(rpElement.dataValues.permission_name) === -1) {
tempAbilities.push(rpElement.dataValues.permission_name);
userAbilities.push(allAbilities[allAbilities.findIndex(x => x.name === rpElement.dataValues.permission_name)]);
}
})
});
req.session.rules = userAbilities;
let location = {
office: office,
company: company,
department: department
}
req.session.location = location
/****************************************************************************************************/
return res.json({id: userID, rules: userAbilities, location: location, "Message": "Login Successful"});
}).catch(err => {
// console.log('ah crap')
if (typeof req.user.sessionID !== 'undefined') {
winston.info(`\"Looking for group for \"${req.user.displayName}\" (${req.user.sAMAccountName})\" - SessionID: ${req.user.sessionID} ${req.ip} \"${req.method} ${req.originalUrl} HTTP/${req.httpVersion}\" \"${req.headers['referer']}\" \"${req.headers['user-agent']}\" \"${req.headers['content-length']}\"`,
{username: req.user.sAMAccountName, sessionID: req.user.sessionID, ip: req.ip, referrer: req.headers['referer'], url: req.originalUrl, query: req.method, route: 'Authentication'});
} else {
winston.info(`\"Looking for group for \"${req.user.displayName}\" (${req.user.sAMAccountName})\" - SessionID: ${req.user.dataValues.sid} ${req.ip} \"${req.method} ${req.originalUrl} HTTP/${req.httpVersion}\" \"${req.headers['referer']}\" \"${req.headers['user-agent']}\" \"${req.headers['content-length']}\"`,
{username: req.user.sAMAccountName, sessionID: req.user.dataValues.sid, ip: req.ip, referrer: req.headers['referer'], url: req.originalUrl, query: req.method, route: 'Authentication'});
}
const promA = Groups.findAll({
where: {
active: 1
},
include: [{
model: Roles,
where: {
active: 1
},
include: [ RolesPerms ]
}],
order: ['displayName']
});
const promB = GroupsRoles.findAll();
// console.error(err);
// user not found in our DB, but they're in AD; let's check if they belong to an approved group
Promise.all([promA, promB]).then(responses => {
// console.log('Response 1', responses[0]);
// console.log('Response 2', responses[1]);
if (typeof req.user.sessionID !== 'undefined') {
winston.info(`\"Found group results for \"${req.user.displayName}\" (${req.user.sAMAccountName})\" - SessionID: ${req.user.sessionID} ${req.ip} \"${req.method} ${req.originalUrl} HTTP/${req.httpVersion}\" \"${req.headers['referer']}\" \"${req.headers['user-agent']}\" \"${req.headers['content-length']}\"`,
{username: req.user.sAMAccountName, sessionID: req.user.sessionID, ip: req.ip, referrer: req.headers['referer'], url: req.originalUrl, query: req.method, route: 'Authentication'});
} else {
winston.info(`\"Found group results for \"${req.user.displayName}\" (${req.user.sAMAccountName})\" - SessionID: ${req.user.dataValues.sid} ${req.ip} \"${req.method} ${req.originalUrl} HTTP/${req.httpVersion}\" \"${req.headers['referer']}\" \"${req.headers['user-agent']}\" \"${req.headers['content-length']}\"`,
{username: req.user.sAMAccountName, sessionID: req.user.dataValues.sid, ip: req.ip, referrer: req.headers['referer'], url: req.originalUrl, query: req.method, route: 'Authentication'});
}
let foundGroup = ''
let foundRoles = []
let foundPerms = []
responses[0].forEach(el => {
// console.log('our el', el)
// console.log('our group', el.dataValues)
for (let j = 0; j < adGroups.length; j++) {
// console.log('adGroups j', adGroups[j])
if (adGroups[j].match(el.dataValues.username)) {
// console.log('found it', el.dataValues)
userID = el.id
foundGroup = el.dataValues.username;
foundRoles = el.dataValues;
break // TODO allow for membership in multiple groups, like I do multiple roles, below
}
}
});
foundRoles.roles.forEach(role => {
// console.log('roles_perms things', role.roles_perms);
role.roles_perms.forEach(roleP => {
if (foundPerms.indexOf(roleP.dataValues.permission_name) === -1) {
foundPerms.push(roleP.dataValues.permission_name);
userAbilities.push(allAbilities[allAbilities.findIndex(x => x.name === roleP.dataValues.permission_name)]);
}
})
});
req.session.rules = userAbilities;
let location = {
office: office,
company: company,
department: department
}
req.session.location = location
return res.json({id: 0, rules: userAbilities, location: location, "Message": "Login Successful via group membership"});
}).catch(err => {
console.error('the error ' + err);
return res.json({"Message": "Login failed. You neither had a user account in SutterNow, nor did you belong to a valid AD Group."})
});
});
return null;
// res.json({"Message":"Login successful"});
},
(err, req, res, next) => {
/* failWithErrors: true, above, makes this section get called to handle the error.
We don't handle the logging nor the json return here; instead, we setup the error object and pass it on
to the error handler which does those things. */
console.log('Authentication failed; passing error on to error handler...');
err.route = 'Authentication';
err.statusCode = 401;
err.status = 401;
err.shouldRedirect = req.headers['user-agent'].indexOf('Postman') > -1;
if (typeof req.flash === 'function' && typeof req.flash('error') !== 'undefined' && req.flash('error').length !== 0) {
err.message = req.flash('error').slice(-1)[0];
console.log('Flash error ' + req.flash('error').slice(-1)[0]);
res.statusMessage = req.flash('error').slice(-1)[0];
}
if (app.get('env') === 'production') {
console.log('stack redacted');
err.stack = '';// We want to obscure any data the user shouldn't see.
}
next(err);
// return res.json({"Message": "Login failed. " + err.message});
// return null;
}
);
api.post('/login', passport.authenticate('ldapauth', {session: true, failureFlash: 'Invalid username or password.', failWithError: true}),
(req, res, next) => {
// this is only called if authentication was successful. this is called after the serializer.
// the authenticated user is found in req.user.
let adGroups = req.user.memberOf;
let office = req.user.physicalDeliveryOfficeName;
let company = req.user.company;
let department = req.user.department;
// console.log('Member Of', adGroups);
if (typeof req.user.sessionID !== 'undefined') {
winston.info(`\"Login Successful for \"${req.user.displayName}\" (${req.user.sAMAccountName})\" - SessionID: ${req.user.sessionID} ${req.ip} \"${req.method} ${req.originalUrl} HTTP/${req.httpVersion}\" \"${req.headers['referer']}\" \"${req.headers['user-agent']}\" \"${req.headers['content-length']}\"`,
{username: req.user.sAMAccountName, sessionID: req.user.sessionID, ip: req.ip, referrer: req.headers['referer'], url: req.originalUrl, query: req.method, route: 'Authentication'});
} else {
winston.info(`\"Login Successful for \"${req.user.displayName}\" (${req.user.sAMAccountName})\" - SessionID: ${req.user.dataValues.sid} ${req.ip} \"${req.method} ${req.originalUrl} HTTP/${req.httpVersion}\" \"${req.headers['referer']}\" \"${req.headers['user-agent']}\" \"${req.headers['content-length']}\"`,
{username: req.user.sAMAccountName, sessionID: req.user.dataValues.sid, ip: req.ip, referrer: req.headers['referer'], url: req.originalUrl, query: req.method, route: 'Authentication'});
}
// console.log('Req user: ', req.user);
// console.log('right here 1')
let tempAbilities = [];
let userAbilities = [];
let allAbilities = displayAbilities();
let include = [{
model: Roles,
where: {
active: 1
},
include: [ RolesPerms ]
}];
let options = {
where: {
username: req.user.sAMAccountName
},
include: include
};
let userID = null;
// TODO check if found user is 'active === 0' and if so, fail the authentication; use where clause "active != 0"
// console.log('right here 2')
Users.findOne(options).then(userResult => {
// console.log('user result', userResult);
if (userResult === null) {
if (typeof req.user.sessionID !== 'undefined') {
winston.info(`\"Login user not found - looking for group for \"${req.user.displayName}\" (${req.user.sAMAccountName})\" - SessionID: ${req.user.sessionID} ${req.ip} \"${req.method} ${req.originalUrl} HTTP/${req.httpVersion}\" \"${req.headers['referer']}\" \"${req.headers['user-agent']}\" \"${req.headers['content-length']}\"`,
{username: req.user.sAMAccountName, sessionID: req.user.sessionID, ip: req.ip, referrer: req.headers['referer'], url: req.originalUrl, query: req.method, route: 'Authentication'});
} else {
winston.info(`\"Login user not found - looking for group for \"${req.user.displayName}\" (${req.user.sAMAccountName})\" - SessionID: ${req.user.dataValues.sid} ${req.ip} \"${req.method} ${req.originalUrl} HTTP/${req.httpVersion}\" \"${req.headers['referer']}\" \"${req.headers['user-agent']}\" \"${req.headers['content-length']}\"`,
{username: req.user.sAMAccountName, sessionID: req.user.dataValues.sid, ip: req.ip, referrer: req.headers['referer'], url: req.originalUrl, query: req.method, route: 'Authentication'});
}
throw new Error('User not in SutterNow DB.')
}
userID = userResult.dataValues.id;
userResult.roles.forEach(rElement => {
rElement.roles_perms.forEach(rpElement => {
// console.log('rpElement', rpElement);
// if (abilities.findIndex(x => x.prop=="propVal")) --> an example of using an object property to find index
if (tempAbilities.indexOf(rpElement.dataValues.permission_name) === -1) {
tempAbilities.push(rpElement.dataValues.permission_name);
userAbilities.push(allAbilities[allAbilities.findIndex(x => x.name === rpElement.dataValues.permission_name)]);
}
})
});
req.session.rules = userAbilities;
let location = {
office: office,
company: company,
department: department
}
req.session.location = location
/****************************************************************************************************/
// TODO is this supposed to be another .then chained between this one and the catch (or before this one)? if so, combine the catch?
if (userResult.dataValues.office !== office || userResult.dataValues.department !== department || userResult.dataValues.company !== company) {
// update the database with the new AD values
console.log('updating user')
Users.update({
// values
office: office,
department: department,
company: company
}, {
// options
where: {
id: userID
}
}).then(numAffected => {
winston.info(`\"Updated ${numAffected} user for ${req.user.dataValues.username}\" - SessionID: ${req.sessionID} ${req.ip} \"${req.method} ${req.originalUrl} HTTP/${req.httpVersion}\" \"${req.headers['referer']}\" \"${req.headers['user-agent']}\" \"${req.headers['content-length']}\"`,
{username: req.user.dataValues.username, sessionID: req.sessionID, ip: req.ip, referrer: req.headers['referer'], url: req.originalUrl, query: req.method, route: 'User Admin'});
return;
}).catch((err) => {
if (err) {
// Not sure how to get an error here. ensureAuthenticated handles invalid users attempting this PUT.
// console.log(err);
err.route = 'User Admin';
err.statusCode = 'UPDATE_USER_AT_LOGIN_BY_ID_ERROR';
err.status = 'UPDATE USER AT LOGIN BY ID ERROR';
err.shouldRedirect = req.headers['user-agent'].indexOf('Postman') > -1;
if (app.get('env') === 'production') {
console.log('stack redacted');
err.stack = '';// We want to obscure any data the user shouldn't see.
}
next(err);
}
});
}
/****************************************************************************************************/
return res.json({id: userID, rules: userAbilities, location: location, "Message": "Login Successful"});
}).catch(err => {
// console.log('ah crap')
if (typeof req.user.sessionID !== 'undefined') {
winston.info(`\"Looking for group for \"${req.user.displayName}\" (${req.user.sAMAccountName})\" - SessionID: ${req.user.sessionID} ${req.ip} \"${req.method} ${req.originalUrl} HTTP/${req.httpVersion}\" \"${req.headers['referer']}\" \"${req.headers['user-agent']}\" \"${req.headers['content-length']}\"`,
{username: req.user.sAMAccountName, sessionID: req.user.sessionID, ip: req.ip, referrer: req.headers['referer'], url: req.originalUrl, query: req.method, route: 'Authentication'});
} else {
winston.info(`\"Looking for group for \"${req.user.displayName}\" (${req.user.sAMAccountName})\" - SessionID: ${req.user.dataValues.sid} ${req.ip} \"${req.method} ${req.originalUrl} HTTP/${req.httpVersion}\" \"${req.headers['referer']}\" \"${req.headers['user-agent']}\" \"${req.headers['content-length']}\"`,
{username: req.user.sAMAccountName, sessionID: req.user.dataValues.sid, ip: req.ip, referrer: req.headers['referer'], url: req.originalUrl, query: req.method, route: 'Authentication'});
}
const promA = Groups.findAll({
where: {
active: 1
},
include: [{
model: Roles,
where: {
active: 1
},
include: [ RolesPerms ]
}],
order: ['displayName']
});
const promB = GroupsRoles.findAll();
// console.error(err);
// user not found in our DB, but they're in AD; let's check if they belong to an approved group
Promise.all([promA, promB]).then(responses => {
// console.log('Response 1', responses[0]);
// console.log('Response 2', responses[1]);
if (typeof req.user.sessionID !== 'undefined') {
winston.info(`\"Found group results for \"${req.user.displayName}\" (${req.user.sAMAccountName})\" - SessionID: ${req.user.sessionID} ${req.ip} \"${req.method} ${req.originalUrl} HTTP/${req.httpVersion}\" \"${req.headers['referer']}\" \"${req.headers['user-agent']}\" \"${req.headers['content-length']}\"`,
{username: req.user.sAMAccountName, sessionID: req.user.sessionID, ip: req.ip, referrer: req.headers['referer'], url: req.originalUrl, query: req.method, route: 'Authentication'});
} else {
winston.info(`\"Found group results for \"${req.user.displayName}\" (${req.user.sAMAccountName})\" - SessionID: ${req.user.dataValues.sid} ${req.ip} \"${req.method} ${req.originalUrl} HTTP/${req.httpVersion}\" \"${req.headers['referer']}\" \"${req.headers['user-agent']}\" \"${req.headers['content-length']}\"`,
{username: req.user.sAMAccountName, sessionID: req.user.dataValues.sid, ip: req.ip, referrer: req.headers['referer'], url: req.originalUrl, query: req.method, route: 'Authentication'});
}
let foundGroup = ''
let foundRoles = []
let foundPerms = []
responses[0].forEach(el => {
// console.log('our el', el)
// console.log('our group', el.dataValues)
for (let j = 0; j < adGroups.length; j++) {
// console.log('adGroups j', adGroups[j])
if (adGroups[j].match(el.dataValues.username)) {
// console.log('found it', el.dataValues)
userID = el.id
foundGroup = el.dataValues.username;
foundRoles = el.dataValues;
break // TODO allow for membership in multiple groups, like I do multiple roles, below
}
}
});
foundRoles.roles.forEach(role => {
// console.log('roles_perms things', role.roles_perms);
role.roles_perms.forEach(roleP => {
if (foundPerms.indexOf(roleP.dataValues.permission_name) === -1) {
foundPerms.push(roleP.dataValues.permission_name);
userAbilities.push(allAbilities[allAbilities.findIndex(x => x.name === roleP.dataValues.permission_name)]);
}
})
});
req.session.rules = userAbilities;
let location = {
office: office,
company: company,
department: department
}
req.session.location = location
return res.json({id: 0, rules: userAbilities, location: location, "Message": "Login Successful via group membership"});
}).catch(err => {
console.error('the error ' + err);
return res.json({"Message": "Login failed. You neither had a user account in SutterNow, nor did you belong to a valid AD Group."})
});
});
return null;
// res.json({"Message":"Login successful"});
},
(err, req, res, next) => {
/* failWithErrors: true, above, makes this section get called to handle the error.
We don't handle the logging nor the json return here; instead, we setup the error object and pass it on
to the error handler which does those things. */
console.log('Authentication failed; passing error on to error handler...');
err.route = 'Authentication';
err.statusCode = 401;
err.status = 401;
err.shouldRedirect = req.headers['user-agent'].indexOf('Postman') > -1;
if (typeof req.flash === 'function' && typeof req.flash('error') !== 'undefined' && req.flash('error').length !== 0) {
err.message = req.flash('error').slice(-1)[0];
console.log('Flash error ' + req.flash('error').slice(-1)[0]);
res.statusMessage = req.flash('error').slice(-1)[0];
}
if (app.get('env') === 'production') {
console.log('stack redacted');
err.stack = '';// We want to obscure any data the user shouldn't see.
}
next(err);
// return res.json({"Message": "Login failed. " + err.message});
// return null;
}
);
我想我明白了。我将我添加的代码移动到一个单独的 .then,并确保 return 之前的代码不使用 res.json,而只是 return 对象。然后我 return 新对象中的对象。然后使用 res.json 无论用户 table 更新成功还是失败,因此:
api.post('/login', passport.authenticate('ldapauth', {session: true, failureFlash: 'Invalid username or password.', failWithError: true}),
(req, res, next) => {
// this is only called if authentication was successful. this is called after the serializer.
// the authenticated user is found in req.user.
let adGroups = req.user.memberOf;
let office = req.user.physicalDeliveryOfficeName;
let company = req.user.company;
let department = req.user.department;
// console.log('Member Of', adGroups);
if (typeof req.user.sessionID !== 'undefined') {
winston.info(`\"Login Successful for \"${req.user.displayName}\" (${req.user.sAMAccountName})\" - SessionID: ${req.user.sessionID} ${req.ip} \"${req.method} ${req.originalUrl} HTTP/${req.httpVersion}\" \"${req.headers['referer']}\" \"${req.headers['user-agent']}\" \"${req.headers['content-length']}\"`,
{username: req.user.sAMAccountName, sessionID: req.user.sessionID, ip: req.ip, referrer: req.headers['referer'], url: req.originalUrl, query: req.method, route: 'Authentication'});
} else {
winston.info(`\"Login Successful for \"${req.user.displayName}\" (${req.user.sAMAccountName})\" - SessionID: ${req.user.dataValues.sid} ${req.ip} \"${req.method} ${req.originalUrl} HTTP/${req.httpVersion}\" \"${req.headers['referer']}\" \"${req.headers['user-agent']}\" \"${req.headers['content-length']}\"`,
{username: req.user.sAMAccountName, sessionID: req.user.dataValues.sid, ip: req.ip, referrer: req.headers['referer'], url: req.originalUrl, query: req.method, route: 'Authentication'});
}
// console.log('Req user: ', req.user);
// console.log('right here 1')
let tempAbilities = [];
let userAbilities = [];
let allAbilities = displayAbilities();
let include = [{
model: Roles,
where: {
active: 1
},
include: [ RolesPerms ]
}];
let options = {
where: {
username: req.user.sAMAccountName
},
include: include
};
let userID = null;
// TODO check if found user is 'active === 0' and if so, fail the authentication; use where clause "active != 0"
// console.log('right here 2')
Users.findOne(options).then(userResult => {
// console.log('user result', userResult);
if (userResult === null) {
if (typeof req.user.sessionID !== 'undefined') {
winston.info(`\"Login user not found - looking for group for \"${req.user.displayName}\" (${req.user.sAMAccountName})\" - SessionID: ${req.user.sessionID} ${req.ip} \"${req.method} ${req.originalUrl} HTTP/${req.httpVersion}\" \"${req.headers['referer']}\" \"${req.headers['user-agent']}\" \"${req.headers['content-length']}\"`,
{username: req.user.sAMAccountName, sessionID: req.user.sessionID, ip: req.ip, referrer: req.headers['referer'], url: req.originalUrl, query: req.method, route: 'Authentication'});
} else {
winston.info(`\"Login user not found - looking for group for \"${req.user.displayName}\" (${req.user.sAMAccountName})\" - SessionID: ${req.user.dataValues.sid} ${req.ip} \"${req.method} ${req.originalUrl} HTTP/${req.httpVersion}\" \"${req.headers['referer']}\" \"${req.headers['user-agent']}\" \"${req.headers['content-length']}\"`,
{username: req.user.sAMAccountName, sessionID: req.user.dataValues.sid, ip: req.ip, referrer: req.headers['referer'], url: req.originalUrl, query: req.method, route: 'Authentication'});
}
throw new Error('User not in SutterNow DB.')
}
userID = userResult.dataValues.id;
userResult.roles.forEach(rElement => {
rElement.roles_perms.forEach(rpElement => {
// console.log('rpElement', rpElement);
// if (abilities.findIndex(x => x.prop=="propVal")) --> an example of using an object property to find index
if (tempAbilities.indexOf(rpElement.dataValues.permission_name) === -1) {
tempAbilities.push(rpElement.dataValues.permission_name);
userAbilities.push(allAbilities[allAbilities.findIndex(x => x.name === rpElement.dataValues.permission_name)]);
}
})
});
req.session.rules = userAbilities;
let location = {
office: office,
company: company,
department: department
}
req.session.location = location
let adLocation = {
office: userResult.dataValues.office,
company: userResult.dataValues.company,
department: userResult.dataValues.department
}
return {id: userID, rules: userAbilities, location: location, adLocation: adLocation, "Message": "Login Successful"};
}).then(result => {
if (result.adLocation.office !== result.location.office || result.adLocation.department !== result.location.department || result.adLocation.company !== result.location.company) {
// update the database with the new AD values
Users.update({
// values
office: office,
department: department,
company: company
}, {
// options
where: {
id: userID
}
}).then(numAffected => {
winston.info(`\"Updated ${numAffected} user for ${req.user.dataValues.username}\" - SessionID: ${req.sessionID} ${req.ip} \"${req.method} ${req.originalUrl} HTTP/${req.httpVersion}\" \"${req.headers['referer']}\" \"${req.headers['user-agent']}\" \"${req.headers['content-length']}\"`,
{username: req.user.dataValues.username, sessionID: req.sessionID, ip: req.ip, referrer: req.headers['referer'], url: req.originalUrl, query: req.method, route: 'User Admin'});
return res.json(result);
}).catch((err) => {
if (err) {
// Not sure how to get an error here. ensureAuthenticated handles invalid users attempting this PUT.
// console.log(err);
err.route = 'User Admin';
err.statusCode = 'UPDATE_USER_AT_LOGIN_BY_ID_ERROR';
err.status = 'UPDATE USER AT LOGIN BY ID ERROR';
err.shouldRedirect = req.headers['user-agent'].indexOf('Postman') > -1;
if (app.get('env') === 'production') {
console.log('stack redacted');
err.stack = '';// We want to obscure any data the user shouldn't see.
}
next(err);
}
});
} else {
return res.json(result);
}
}).catch(err => {
// console.log('ah crap')
if (typeof req.user.sessionID !== 'undefined') {
winston.info(`\"Looking for group for \"${req.user.displayName}\" (${req.user.sAMAccountName})\" - SessionID: ${req.user.sessionID} ${req.ip} \"${req.method} ${req.originalUrl} HTTP/${req.httpVersion}\" \"${req.headers['referer']}\" \"${req.headers['user-agent']}\" \"${req.headers['content-length']}\"`,
{username: req.user.sAMAccountName, sessionID: req.user.sessionID, ip: req.ip, referrer: req.headers['referer'], url: req.originalUrl, query: req.method, route: 'Authentication'});
} else {
winston.info(`\"Looking for group for \"${req.user.displayName}\" (${req.user.sAMAccountName})\" - SessionID: ${req.user.dataValues.sid} ${req.ip} \"${req.method} ${req.originalUrl} HTTP/${req.httpVersion}\" \"${req.headers['referer']}\" \"${req.headers['user-agent']}\" \"${req.headers['content-length']}\"`,
{username: req.user.sAMAccountName, sessionID: req.user.dataValues.sid, ip: req.ip, referrer: req.headers['referer'], url: req.originalUrl, query: req.method, route: 'Authentication'});
}
const promA = Groups.findAll({
where: {
active: 1
},
include: [{
model: Roles,
where: {
active: 1
},
include: [ RolesPerms ]
}],
order: ['displayName']
});
const promB = GroupsRoles.findAll();
// console.error(err);
// user not found in our DB, but they're in AD; let's check if they belong to an approved group
Promise.all([promA, promB]).then(responses => {
// console.log('Response 1', responses[0]);
// console.log('Response 2', responses[1]);
if (typeof req.user.sessionID !== 'undefined') {
winston.info(`\"Found group results for \"${req.user.displayName}\" (${req.user.sAMAccountName})\" - SessionID: ${req.user.sessionID} ${req.ip} \"${req.method} ${req.originalUrl} HTTP/${req.httpVersion}\" \"${req.headers['referer']}\" \"${req.headers['user-agent']}\" \"${req.headers['content-length']}\"`,
{username: req.user.sAMAccountName, sessionID: req.user.sessionID, ip: req.ip, referrer: req.headers['referer'], url: req.originalUrl, query: req.method, route: 'Authentication'});
} else {
winston.info(`\"Found group results for \"${req.user.displayName}\" (${req.user.sAMAccountName})\" - SessionID: ${req.user.dataValues.sid} ${req.ip} \"${req.method} ${req.originalUrl} HTTP/${req.httpVersion}\" \"${req.headers['referer']}\" \"${req.headers['user-agent']}\" \"${req.headers['content-length']}\"`,
{username: req.user.sAMAccountName, sessionID: req.user.dataValues.sid, ip: req.ip, referrer: req.headers['referer'], url: req.originalUrl, query: req.method, route: 'Authentication'});
}
let foundGroup = ''
let foundRoles = []
let foundPerms = []
responses[0].forEach(el => {
// console.log('our el', el)
// console.log('our group', el.dataValues)
for (let j = 0; j < adGroups.length; j++) {
// console.log('adGroups j', adGroups[j])
if (adGroups[j].match(el.dataValues.username)) {
// console.log('found it', el.dataValues)
userID = el.id
foundGroup = el.dataValues.username;
foundRoles = el.dataValues;
break // TODO allow for membership in multiple groups, like I do multiple roles, below
}
}
});
foundRoles.roles.forEach(role => {
// console.log('roles_perms things', role.roles_perms);
role.roles_perms.forEach(roleP => {
if (foundPerms.indexOf(roleP.dataValues.permission_name) === -1) {
foundPerms.push(roleP.dataValues.permission_name);
userAbilities.push(allAbilities[allAbilities.findIndex(x => x.name === roleP.dataValues.permission_name)]);
}
})
});
req.session.rules = userAbilities;
let location = {
office: office,
company: company,
department: department
}
req.session.location = location
return res.json({id: 0, rules: userAbilities, location: location, "Message": "Login Successful via group membership"});
}).catch(err => {
console.error('the error ' + err);
return res.json({"Message": "Login failed. You neither had a user account in SutterNow, nor did you belong to a valid AD Group."})
});
});
return null;
// res.json({"Message":"Login successful"});
},
(err, req, res, next) => {
/* failWithErrors: true, above, makes this section get called to handle the error.
We don't handle the logging nor the json return here; instead, we setup the error object and pass it on
to the error handler which does those things. */
console.log('Authentication failed; passing error on to error handler...');
err.route = 'Authentication';
err.statusCode = 401;
err.status = 401;
err.shouldRedirect = req.headers['user-agent'].indexOf('Postman') > -1;
if (typeof req.flash === 'function' && typeof req.flash('error') !== 'undefined' && req.flash('error').length !== 0) {
err.message = req.flash('error').slice(-1)[0];
console.log('Flash error ' + req.flash('error').slice(-1)[0]);
res.statusMessage = req.flash('error').slice(-1)[0];
}
if (app.get('env') === 'production') {
console.log('stack redacted');
err.stack = '';// We want to obscure any data the user shouldn't see.
}
next(err);
// return res.json({"Message": "Login failed. " + err.message});
// return null;
}
);