|
|
@@ -66,7 +66,7 @@ module.exports = function(crowi, app) {
|
|
|
* @param {*} res
|
|
|
* @param {*} next
|
|
|
*/
|
|
|
- const loginWithLdap = (req, res, next) => {
|
|
|
+ const loginWithLdap = async(req, res, next) => {
|
|
|
if (!passportService.isLdapStrategySetup) {
|
|
|
debug('LdapStrategy has not been set up');
|
|
|
return next();
|
|
|
@@ -78,77 +78,43 @@ module.exports = function(crowi, app) {
|
|
|
});
|
|
|
}
|
|
|
|
|
|
- passport.authenticate('ldapauth', (err, ldapAccountInfo, info) => {
|
|
|
- if (res.headersSent) { // dirty hack -- 2017.09.25
|
|
|
- return; // cz: somehow passport.authenticate called twice when ECONNREFUSED error occurred
|
|
|
- }
|
|
|
-
|
|
|
- debug('--- authenticate with LdapStrategy ---');
|
|
|
- debug('ldapAccountInfo', ldapAccountInfo);
|
|
|
- debug('info', info);
|
|
|
+ const providerId = 'ldap';
|
|
|
+ const strategyName = 'ldapauth';
|
|
|
+ const ldapAccountInfo = await promisifiedPassportAuthentication(req, res, next, strategyName);
|
|
|
|
|
|
- if (err) { // DB Error
|
|
|
- logger.error('LDAP Server Error: ', err);
|
|
|
- req.flash('warningMessage', 'LDAP Server Error occured.');
|
|
|
- return next(); // pass and the flash message is displayed when all of authentications are failed.
|
|
|
- }
|
|
|
-
|
|
|
- // authentication failure
|
|
|
- if (!ldapAccountInfo) { return next() }
|
|
|
- // check groups
|
|
|
- if (!isValidLdapUserByGroupFilter(ldapAccountInfo)) {
|
|
|
- return loginFailure(req, res, next);
|
|
|
- }
|
|
|
+ // check groups for LDAP
|
|
|
+ if (!isValidLdapUserByGroupFilter(ldapAccountInfo)) {
|
|
|
+ return loginFailure(req, res, next);
|
|
|
+ }
|
|
|
|
|
|
- /*
|
|
|
- * authentication success
|
|
|
- */
|
|
|
- // it is guaranteed that username that is input from form can be acquired
|
|
|
- // because this processes after authentication
|
|
|
- const ldapAccountId = passportService.getLdapAccountIdFromReq(req);
|
|
|
+ /*
|
|
|
+ * authentication success
|
|
|
+ */
|
|
|
+ // it is guaranteed that username that is input from form can be acquired
|
|
|
+ // because this processes after authentication
|
|
|
+ const ldapAccountId = passportService.getLdapAccountIdFromReq(req);
|
|
|
+ const attrMapUsername = passportService.getLdapAttrNameMappedToUsername();
|
|
|
+ const attrMapName = passportService.getLdapAttrNameMappedToName();
|
|
|
+ const usernameToBeRegistered = ldapAccountInfo[attrMapUsername];
|
|
|
+ const nameToBeRegistered = ldapAccountInfo[attrMapName];
|
|
|
+ const userInfo = {
|
|
|
+ 'id': ldapAccountId,
|
|
|
+ 'username': usernameToBeRegistered,
|
|
|
+ 'name': nameToBeRegistered
|
|
|
+ }
|
|
|
|
|
|
- const attrMapUsername = passportService.getLdapAttrNameMappedToUsername();
|
|
|
- const attrMapName = passportService.getLdapAttrNameMappedToName();
|
|
|
- const usernameToBeRegistered = ldapAccountInfo[attrMapUsername];
|
|
|
- const nameToBeRegistered = ldapAccountInfo[attrMapName];
|
|
|
+ const externalAccount = await getOrCreateUser(req, res, next, userInfo, providerId);
|
|
|
+ if (!externalAccount) {
|
|
|
+ return loginFailure(req, res, next);
|
|
|
+ }
|
|
|
|
|
|
- // find or register(create) user
|
|
|
- ExternalAccount.findOrRegister('ldap', ldapAccountId, usernameToBeRegistered, nameToBeRegistered)
|
|
|
- .catch((err) => {
|
|
|
- if (err.name === 'DuplicatedUsernameException') {
|
|
|
- // get option
|
|
|
- const isSameUsernameTreatedAsIdenticalUser = Config.isSameUsernameTreatedAsIdenticalUser(config, 'ldap');
|
|
|
- if (isSameUsernameTreatedAsIdenticalUser) {
|
|
|
- // associate to existing user
|
|
|
- debug(`ExternalAccount '${ldapAccountId}' will be created and bound to the exisiting User account`);
|
|
|
- return ExternalAccount.associate('ldap', ldapAccountId, err.user);
|
|
|
- }
|
|
|
- }
|
|
|
- throw err; // throw again
|
|
|
- })
|
|
|
- .then((externalAccount) => {
|
|
|
- return externalAccount.getPopulatedUser();
|
|
|
- })
|
|
|
- .then((user) => {
|
|
|
- // login
|
|
|
- req.logIn(user, (err) => {
|
|
|
- if (err) { return next() }
|
|
|
- else {
|
|
|
- return loginSuccess(req, res, user);
|
|
|
- }
|
|
|
- });
|
|
|
- })
|
|
|
- .catch((err) => {
|
|
|
- if (err.name === 'DuplicatedUsernameException') {
|
|
|
- req.flash('isDuplicatedUsernameExceptionOccured', true);
|
|
|
- return next();
|
|
|
- }
|
|
|
- else {
|
|
|
- return next(err);
|
|
|
- }
|
|
|
- });
|
|
|
+ const user = await externalAccount.getPopulatedUser();
|
|
|
|
|
|
- })(req, res, next);
|
|
|
+ // login
|
|
|
+ await req.logIn(user, err => {
|
|
|
+ if (err) { return next(err) };
|
|
|
+ return loginSuccess(req, res, user);
|
|
|
+ });
|
|
|
};
|
|
|
|
|
|
/**
|
|
|
@@ -239,10 +205,132 @@ module.exports = function(crowi, app) {
|
|
|
})(req, res, next);
|
|
|
};
|
|
|
|
|
|
+ const loginPassportGoogle = function(req, res) {
|
|
|
+ if (!passportService.isGoogleStrategySetup) {
|
|
|
+ debug('GoogleStrategy has not been set up');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ passport.authenticate('google', {
|
|
|
+ scope: ['profile', 'email'],
|
|
|
+ })(req, res);
|
|
|
+ };
|
|
|
+
|
|
|
+ const loginPassportGoogleCallback = async(req, res, next) => {
|
|
|
+ const providerId = 'google';
|
|
|
+ const strategyName = 'google';
|
|
|
+ const response = await promisifiedPassportAuthentication(req, res, next, strategyName);
|
|
|
+ const userInfo = {
|
|
|
+ 'id': response.id,
|
|
|
+ 'username': response.displayName,
|
|
|
+ 'name': `${response.name.givenName} ${response.name.familyName}`
|
|
|
+ }
|
|
|
+ const externalAccount = await getOrCreateUser(req, res, next, userInfo, providerId);
|
|
|
+ if (!externalAccount) {
|
|
|
+ return loginFailure(req, res, next);
|
|
|
+ }
|
|
|
+
|
|
|
+ const user = await externalAccount.getPopulatedUser();
|
|
|
+
|
|
|
+ // login
|
|
|
+ req.logIn(user, err => {
|
|
|
+ if (err) { return next(err) };
|
|
|
+ return loginSuccess(req, res, user);
|
|
|
+ });
|
|
|
+ };
|
|
|
+
|
|
|
+ const loginPassportGitHub = function(req, res) {
|
|
|
+ if (!passportService.isGitHubStrategySetup) {
|
|
|
+ debug('GitHubStrategy has not been set up');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ passport.authenticate('github')(req, res);
|
|
|
+ };
|
|
|
+
|
|
|
+ const loginPassportGitHubCallback = async(req, res, next) => {
|
|
|
+ const providerId = 'github';
|
|
|
+ const strategyName = 'github';
|
|
|
+ const response = await promisifiedPassportAuthentication(req, res, next, strategyName);
|
|
|
+ const userInfo = {
|
|
|
+ 'id': response.id,
|
|
|
+ 'username': response.username,
|
|
|
+ 'name': response.displayName
|
|
|
+ }
|
|
|
+
|
|
|
+ const externalAccount = await getOrCreateUser(req, res, next, userInfo, providerId);
|
|
|
+ if (!externalAccount) {
|
|
|
+ return loginFailure(req, res, next);
|
|
|
+ }
|
|
|
+
|
|
|
+ const user = await externalAccount.getPopulatedUser();
|
|
|
+
|
|
|
+ // login
|
|
|
+ req.logIn(user, err => {
|
|
|
+ if (err) { return next(err) };
|
|
|
+ return loginSuccess(req, res, user);
|
|
|
+ });
|
|
|
+ };
|
|
|
+
|
|
|
+ const promisifiedPassportAuthentication = (req, res, next, strategyName) => {
|
|
|
+ return new Promise((resolve, reject) => {
|
|
|
+ passport.authenticate(strategyName, (err, response, info) => {
|
|
|
+ if (res.headersSent) { // dirty hack -- 2017.09.25
|
|
|
+ return; // cz: somehow passport.authenticate called twice when ECONNREFUSED error occurred
|
|
|
+ }
|
|
|
+
|
|
|
+ if (err) {
|
|
|
+ logger.error(`'${strategyName}' passport authentication error: `, err);
|
|
|
+ req.flash('warningMessage', `Error occured in '${strategyName}' passport authentication`);
|
|
|
+ return next(); // pass and the flash message is displayed when all of authentications are failed.
|
|
|
+ }
|
|
|
+
|
|
|
+ // authentication failure
|
|
|
+ if (!response) {
|
|
|
+ return next();
|
|
|
+ }
|
|
|
+
|
|
|
+ resolve(response)
|
|
|
+ })(req, res, next);
|
|
|
+ });
|
|
|
+ };
|
|
|
+
|
|
|
+ const getOrCreateUser = async(req, res, next, userInfo, providerId) => {
|
|
|
+ try {
|
|
|
+ // find or register(create) user
|
|
|
+ const externalAccount = await ExternalAccount.findOrRegister(
|
|
|
+ providerId,
|
|
|
+ userInfo.id,
|
|
|
+ userInfo.username,
|
|
|
+ userInfo.name
|
|
|
+ );
|
|
|
+ return externalAccount;
|
|
|
+ }
|
|
|
+ catch (err) {
|
|
|
+ if (err.name === 'DuplicatedUsernameException') {
|
|
|
+ // get option
|
|
|
+ const isSameUsernameTreatedAsIdenticalUser = Config.isSameUsernameTreatedAsIdenticalUser(config, providerId);
|
|
|
+ if (isSameUsernameTreatedAsIdenticalUser) {
|
|
|
+ // associate to existing user
|
|
|
+ debug(`ExternalAccount '${userInfo.username}' will be created and bound to the exisiting User account`);
|
|
|
+ return ExternalAccount.associate(providerId, userInfo.id, err.user);
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ req.flash('provider-DuplicatedUsernameException', providerId);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
return {
|
|
|
loginFailure,
|
|
|
loginWithLdap,
|
|
|
testLdapCredentials,
|
|
|
loginWithLocal,
|
|
|
+ loginPassportGoogle,
|
|
|
+ loginPassportGitHub,
|
|
|
+ loginPassportGoogleCallback,
|
|
|
+ loginPassportGitHubCallback,
|
|
|
};
|
|
|
};
|