674 lines
26 KiB
JavaScript
674 lines
26 KiB
JavaScript
var logger = require('morgan');
|
|
var moment = require('moment');
|
|
var md5 = require('md5');
|
|
var jwt = require('jsonwebtoken');
|
|
var express = require('express');
|
|
require('express-async-errors');
|
|
var bodyParser = require('body-parser');
|
|
var request = require('request');
|
|
var Pool = require('pg-pool');
|
|
var LDAP = require('ldapjs');
|
|
|
|
var app = express();
|
|
var env = process.env.NODE_ENV || 'development';
|
|
var config = require('../config/vnc-hybrid-authenticator.js')[env];
|
|
console.log(moment().format("LTS") + ' Using configuration', config);
|
|
|
|
|
|
var servicePort = config.servicePort;
|
|
|
|
var adBaseFilter = '(&(objectClass=user)';
|
|
|
|
if ((config.adBaseFilter) && (config.adBaseFilter != null)) {
|
|
adBaseFilter = config.adBaseFilter;
|
|
}
|
|
|
|
var ldap;
|
|
|
|
// these options are applied in search methods
|
|
var base = config.ldap.searchBase; // default base for all future searches
|
|
//filter: config.ldap.filter, // default filter for all future searches
|
|
var scope = 'sub'; // scope: LDAP.SUBTREE
|
|
|
|
if (!config.noLDAP) {
|
|
if (!config.useRedmineAuth) {
|
|
ldap = LDAP.createClient({
|
|
url: config.ldap.ldapUri, // string
|
|
// do not wait longer than 1 minute for ldap connect
|
|
connectTimeout: 60 * 1000, // Milliseconds client should wait before timing out on TCP connections (Default: OS default)
|
|
reconnect: {
|
|
initialDelay: 200,
|
|
maxDelay: 1000,
|
|
failAfter: 10
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
let dbpoolOpts = {
|
|
database: config.database.name,
|
|
user: config.database.user,
|
|
password: config.database.pass,
|
|
host: config.database.host,
|
|
port: config.database.port,
|
|
max: 20, // set pool max size to 20
|
|
min: 4, // set min pool size to 4
|
|
idleTimeoutMillis: config.database.idleTimeoutMillis, // close idle clients after 1 second
|
|
connectionTimeoutMillis: config.database.connectionTimeoutMillis, // return an error after 1 second if connection could not be established
|
|
}
|
|
|
|
if (!config.disableDBtls) {
|
|
dbpoolOpts['ssl'] = { rejectUnauthorized: false };
|
|
}
|
|
|
|
var dbpool = new Pool(dbpoolOpts);
|
|
|
|
|
|
let projectPoolOpts = {
|
|
database: config.vncproject.name,
|
|
user: config.vncproject.user,
|
|
password: config.vncproject.pass,
|
|
host: config.vncproject.host,
|
|
port: config.vncproject.port,
|
|
max: 20, // set pool max size to 20
|
|
min: 4, // set min pool size to 4
|
|
idleTimeoutMillis: config.database.idleTimeoutMillis, // close idle clients after 1 second
|
|
connectionTimeoutMillis: config.database.connectionTimeoutMillis, // return an error after 1 second if connection could not be established
|
|
}
|
|
|
|
if (!config.disableDBtls) {
|
|
projectPoolOpts['ssl'] = { rejectUnauthorized: false };
|
|
}
|
|
|
|
|
|
var projectPool;
|
|
if (config.vncproject) {
|
|
projectPool = new Pool(projectPoolOpts);
|
|
}
|
|
|
|
app.use(bodyParser.json()); // for parsing application/json
|
|
app.use(logger('dev'));
|
|
|
|
function IsValidLDAPUser(username) {
|
|
return new Promise( function (resolve, reject) {
|
|
console.log("[IsValidLDAPUser start] username: ", username);
|
|
var uid = username.split("@")[0];
|
|
var domain = config.domain;
|
|
var email = username;
|
|
if (username.indexOf("@") > -1) {
|
|
domain = username.split("@")[1];
|
|
} else {
|
|
email = username + "@" + domain;
|
|
}
|
|
var dcstring = "ou=people,dc=";
|
|
if (domain.indexOf(".") > -1 ){
|
|
var dcstrings = domain.split(".");
|
|
if (dcstrings.length > 0) {
|
|
dcstring += dcstrings.join(",dc=");
|
|
}
|
|
}
|
|
console.log("uid: ", uid);
|
|
console.log("domain: ", domain);
|
|
console.log("dc: ", dcstring);
|
|
|
|
if ((config.searchBase) && (config.searchBase != null)) {
|
|
dcstring = config.searchBase;
|
|
}
|
|
|
|
var search_options = { scope : 'sub' };
|
|
var _filter = '(&(objectClass=inetOrgPerson)';
|
|
if (config.ldap.ldapType === 'zimbra') {
|
|
_filter += '(!(zimbraIsSystemResource=TRUE))(!(zimbraIsSystemAccount=TRUE))';
|
|
_filter += '(!(objectClass=zimbraDistributionList))(!(objectClass=zimbraCalendarResource))';
|
|
_filter += '(&(zimbraAccountStatus=active))(&(zimbraMailStatus=enabled))';
|
|
_filter += '(|(uid=' + uid + ')(mail=' + email + ')))';
|
|
search_options.filter = _filter;
|
|
// search_options.attributes = ['uid', 'mail', 'givenName', 'sn', 'displayName', 'zimbraAccountStatus'];
|
|
search_options.attributes = ['uid', 'mail', 'zimbraAccountStatus', 'givenName', 'sn', 'displayName', 'title', 'telephoneNumber', 'l', 'street', 'postalCode', 'co'];
|
|
} else if (config.ldap.ldapType === 'MS-AD') {
|
|
|
|
_filter = adBaseFilter;
|
|
_filter += '(|(sAMAccountName=' + uid + ')(mail=' + username + ')))';
|
|
search_options.filter = _filter;
|
|
search_options.attributes = ['sAMAccountName', 'mail', 'givenName', 'sn', 'displayName' ];
|
|
} else {
|
|
console.error(moment().format("LTS") + " [profile ldap] unsupported config.ldap.ldapType : '"+config.ldap.ldapType+"'");
|
|
resolve(null);
|
|
}
|
|
console.log(moment().format("LTS") + ' ldap filter: ', _filter);
|
|
ldap.bind(
|
|
config.ldap.bindDn,
|
|
config.ldap.bindPassword,
|
|
err => {
|
|
if (err) {
|
|
console.error(moment().format("LTS") + " [profile ldap.bind]", err);
|
|
resolve(null);
|
|
} else {
|
|
var resultEntries = [];
|
|
console.log(moment().format("LTS") + ' [profile ldap.search] dcstring:', dcstring);
|
|
ldap.search(dcstring, search_options, (error, searchResult) => {
|
|
if (error) {
|
|
console.error(moment().format("LTS") + " [profile ldap.search error] ", error);
|
|
resolve(null);
|
|
}
|
|
searchResult.on('searchEntry' , e => {
|
|
var resultEntry = {};
|
|
for (e2 of e.attributes) {
|
|
resultEntry[e2.type == "sAMAccountName" ? "uid" : e2.type] = e2.vals;
|
|
}
|
|
console.log(moment().format("LTS") + ' [profile ldap.search] entry:', resultEntry);
|
|
resultEntries.push(resultEntry);
|
|
});
|
|
|
|
searchResult.on('searchReference' , ref => console.log(moment().format("LTS") + ' [profile ldap.search] referral: ' + ref));
|
|
searchResult.on('error' , err => { console.error(moment().format("LTS") + ' [profile ldap.search] on error: ' + err); resolve(false);})
|
|
searchResult.on('end' , status =>
|
|
{
|
|
console.log('[search-ldap.end] status=' + status.status + " ("+status.errorMessage+")", resultEntries.length+ " entries");
|
|
if (resultEntries.length === 1) {
|
|
console.log(moment().format("LTS") + " [valid LDAP user] resultEntries: ", resultEntries);
|
|
console.log(moment().format("LTS") + " [valid LDAP user] ", username);
|
|
ldap.unbind();
|
|
resolve(resultEntries[0]);
|
|
} else {
|
|
console.log(moment().format("LTS") + " [no valid LDAP user] - sending 401");
|
|
ldap.unbind();
|
|
resolve(null);
|
|
}
|
|
});
|
|
});
|
|
}
|
|
}
|
|
);
|
|
|
|
});
|
|
}
|
|
|
|
|
|
function IsValidLDAPAuth(username, password) {
|
|
return new Promise( function (resolve, reject) {
|
|
var uid = username.split("@")[0];
|
|
var domain = username.split("@")[1];
|
|
var dcstring = "ou=people,dc=";
|
|
var dcstrings = domain.split(".");
|
|
if (dcstrings.length > 0) {
|
|
dcstring += dcstrings.join(",dc=");
|
|
}
|
|
|
|
if ((config.searchBase) && (config.searchBase != null)) {
|
|
dcstring = config.searchBase;
|
|
}
|
|
|
|
var search_options = { scope : 'sub' };
|
|
var uiddn = "";
|
|
if (config.ldap.ldapType === 'zimbra') {
|
|
uiddn = "uid=" + uid + "," + dcstring;
|
|
} else if (config.ldap.ldapType === 'MS-AD') {
|
|
uiddn = "sAMAccountName=" + uid + "," + dcstring;
|
|
} else {
|
|
console.error(moment().format("LTS") + " [profile ldap] unsupported config.ldap.ldapType : '"+config.ldap.ldapType+"'");
|
|
resolve(false);
|
|
}
|
|
console.log("LDAP Auth - UID DN: ", uiddn);
|
|
ldap.bind( uiddn, password,
|
|
err => {
|
|
if (err) {
|
|
console.error(moment().format("LTS") + " [user ldap.bind] fail ", err);
|
|
resolve(false);
|
|
} else {
|
|
console.error(moment().format("LTS") + " [user ldap.bind]", err);
|
|
resolve(true);
|
|
}
|
|
}
|
|
);
|
|
|
|
});
|
|
}
|
|
|
|
function getProjectApiKey(username) {
|
|
return new Promise( function (resolve, reject) {
|
|
var pqueryparams = [];
|
|
pqueryparams[0] = username;
|
|
pqueryparams[1] = "api";
|
|
pquery = "select tokens.value AS RedmineAPIKey from tokens where tokens.action=$2 and tokens.user_id in ";
|
|
pquery += "(select id from users where users.status=1 and id in (select user_id from email_addresses where address=$1))"
|
|
projectPool.query(pquery, pqueryparams, function (perr, pres) {
|
|
if (perr != null) {
|
|
console.log("db connection error: ", perr);
|
|
resolve(null);
|
|
} else {
|
|
if (pres.rowCount > 0) {
|
|
console.log("pres: ", pres.rows);
|
|
resolve(pres.rows[0].redmineapikey);
|
|
} else {
|
|
resolve(null);
|
|
}
|
|
}
|
|
})
|
|
});
|
|
}
|
|
|
|
function IsValidRedmineUser(username) {
|
|
return new Promise( function (resolve, reject) {
|
|
var uid = username.split("@")[0];
|
|
var query = ""
|
|
var queryparams = [];
|
|
queryparams[0] = uid;
|
|
queryparams[1] = "api";
|
|
queryparams[2] = "@" + config.domain;
|
|
var query = "SELECT DISTINCT ON (uid) users.login AS uid, users.firstname AS givenName, users.lastname AS sn, ";
|
|
query += "users.login||$3 as email, tokens.value AS RedmineAPIKey FROM users, tokens ";
|
|
query += "WHERE users.status=1 AND lower(users.login)=$1 and tokens.action=$2 and tokens.user_id=users.id";
|
|
|
|
// var query = "select SELECT DISTINCT ON (uid) users.login AS uid, users.firstname AS givenName, users.lastname AS sn, users.login||\'@talk.vnc.de\' AS mail FROM users WHERE users.status=1 AND users.login=$1 AND substring(users.hashed_password, 0, length(users.hashed_password)+1) = encode(digest(users.salt||encode(digest(:password, \'sha1\'), \'hex\'), \'sha1\'), \'hex\')";
|
|
dbpool.query(query, queryparams, function (derr, dres) {
|
|
if (derr == null) {
|
|
if (dres.rowCount > 0) {
|
|
var redmineUser = {
|
|
id: dres.rows[0].uid,
|
|
firstname: dres.rows[0].givenname,
|
|
lastname: dres.rows[0].sn,
|
|
email: dres.rows[0].email,
|
|
displayName: dres.rows[0].givenname + " " + dres.rows[0].sn,
|
|
redmineapikey: dres.rows[0].redmineapikey
|
|
}
|
|
resolve(redmineUser);
|
|
} else {
|
|
resolve(false);
|
|
}
|
|
} else {
|
|
console.log("db connection error: ", derr);
|
|
resolve(false);
|
|
}
|
|
}
|
|
);
|
|
|
|
});
|
|
}
|
|
|
|
function IsValidRedmineUser2FA(username) {
|
|
return new Promise( function (resolve, reject) {
|
|
var uid = username.split("@")[0];
|
|
var query = ""
|
|
var queryparams = [];
|
|
queryparams[0] = uid;
|
|
queryparams[1] = "api";
|
|
//var query = "SELECT DISTINCT ON (uid) users.login AS uid, users.firstname AS givenname, users.lastname AS sn FROM users WHERE users.status=1 AND users.login=$1";
|
|
var query = "SELECT users.login AS uid, users.firstname AS givenname, users.lastname AS sn, ";
|
|
query += "users.login||\'@"+ config.domain +"\' AS email, organizations.name as memberOf, departments.name as deptName, ";
|
|
query += "replace(trim(replace(replace(departments.enabled_product_names, \'-\', \'\'), E\'\n\', \'\')), E\' \', \',\') as features, tokens.value as redmineapikey, cast(users.tfa_enabled as TEXT) as tfa_enabled ";
|
|
query += "FROM users JOIN organization_users ON organization_users.user_id=users.id ";
|
|
query += "JOIN organizations ON organizations.id=organization_users.organization_id JOIN departments ON departments.id=organization_users.department_id JOIN tokens ON tokens.user_id=users.id ";
|
|
query += "WHERE users.status=1 AND lower(users.login)=$1 AND tokens.action=$2 AND organization_users.status=1 AND organizations.active=true";
|
|
|
|
// var query = "select SELECT DISTINCT ON (uid) users.login AS uid, users.firstname AS givenName, users.lastname AS sn, users.login||\'@talk.vnc.de\' AS mail FROM users WHERE users.status=1 AND users.login=$1 AND substring(users.hashed_password, 0, length(users.hashed_password)+1) = encode(digest(users.salt||encode(digest(:password, \'sha1\'), \'hex\'), \'sha1\'), \'hex\')";
|
|
dbpool.query(query, queryparams, function (derr, dres) {
|
|
if (derr == null) {
|
|
if (dres.rowCount > 0) {
|
|
var redmineUser = {
|
|
uid: dres.rows[0].uid,
|
|
givenname: dres.rows[0].givenname,
|
|
sn: dres.rows[0].sn,
|
|
email: dres.rows[0].email,
|
|
cn: dres.rows[0].givenname + " " + dres.rows[0].sn,
|
|
memberOf: dres.rows[0].memberOf,
|
|
features: dres.rows[0].features,
|
|
redmineapikey: dres.rows[0].redmineapikey,
|
|
tfa_enabled: dres.rows[0].tfa_enabled
|
|
}
|
|
resolve(redmineUser);
|
|
} else {
|
|
resolve(false);
|
|
}
|
|
} else {
|
|
console.log("db connection error: ", derr);
|
|
resolve(false);
|
|
}
|
|
}
|
|
);
|
|
|
|
});
|
|
}
|
|
|
|
function IsValidRedmineAuth(username, password) {
|
|
return new Promise( function (resolve, reject) {
|
|
var uid = username.split("@")[0];
|
|
var query = ""
|
|
var queryparams = [];
|
|
queryparams[0] = uid;
|
|
queryparams[1] = password;
|
|
queryparams[2] = "sha1";
|
|
queryparams[3] = "hex";
|
|
|
|
var query = "SELECT DISTINCT ON (uid) users.login AS uid, users.firstname AS givenName, users.lastname AS sn FROM users ";
|
|
query += "WHERE users.status=1 AND lower(users.login)=$1 AND substring(users.hashed_password, 0, length(users.hashed_password)+1) = encode(digest(users.salt||encode(digest($2, $3), $4), $3), $4)";
|
|
dbpool.query(query, queryparams, function (derr, dres) {
|
|
if (derr == null) {
|
|
if (dres.rowCount > 0) {
|
|
resolve(true);
|
|
} else {
|
|
resolve(false);
|
|
}
|
|
} else {
|
|
console.log("db connection error: ", derr);
|
|
resolve(false);
|
|
}
|
|
}
|
|
);
|
|
|
|
});
|
|
}
|
|
|
|
|
|
function IsValidOldSecret(username, password) {
|
|
try {
|
|
var secretTimeseed = password.split("-")[0];
|
|
var oldHash = password.split("-")[1];
|
|
var OldYear = secretTimeseed.substring(4,8);
|
|
var OldDay = secretTimeseed.substring(0,2);
|
|
var OldMonth = secretTimeseed.substring(2,4);
|
|
var OldHour = secretTimeseed.substring(8,10);
|
|
var oldMoment = moment.utc(OldYear + "-" + OldMonth + "-" + OldDay + " " + OldHour).valueOf();
|
|
var nowMoment = moment.utc().valueOf();
|
|
if (oldMoment >= nowMoment) {
|
|
// OldSecretTest is still within valid time Timeseed as moment
|
|
var uid=username.split("@")[0].toLowerCase();
|
|
var mangledarray = [];
|
|
for (i = 0; i < uid.length; i++) {
|
|
mangledarray.push(uid.charCodeAt(i) + 1);
|
|
}
|
|
var mangledusername=mangledarray.join('');
|
|
|
|
var testSecret = secretTimeseed + mangledusername + config.xmppToken;
|
|
var testdigest = md5(testSecret);
|
|
var testhash = testdigest;
|
|
while (testhash.length < 32) {
|
|
testhash = '0' + testhash;
|
|
}
|
|
if (oldHash === testhash) {
|
|
console.log(moment().format("LTS") + " [OldSecretTest] oldSecret valid for ", username);
|
|
return true;
|
|
} else {
|
|
console.log(moment().format("LTS") + " [OldSecretTest] oldSecret invalid for ", username);
|
|
return false;
|
|
}
|
|
} else {
|
|
console.log(moment().format("LTS") + " [OldSecretTest] oldSecret expired for ", username);
|
|
return false;
|
|
}
|
|
} catch (e) {
|
|
console.error(moment().format("LTS") + " [OldSecretTest] error: ", e);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
function IsValidJWT(username,password) {
|
|
try {
|
|
var xmppToken = config.xmppToken;
|
|
var decodedToken = jwt.verify(password, xmppToken);
|
|
console.log("decodedJWT: ", decodedToken);
|
|
return true;
|
|
} catch (e) {
|
|
console.error(moment().format("LTS") + " [JWTValidation] error: ", e);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
function getFirstElement(elements) {
|
|
var result;
|
|
if (typeof(elements) == "string"){
|
|
result = elements;
|
|
} else if (typeof(elements) == "object") {
|
|
result = elements[0];
|
|
}
|
|
return result;
|
|
}
|
|
|
|
app.get('/', async function (req, res) {
|
|
if (req.headers.authorization && req.headers.authorization.startsWith("Basic")) {
|
|
try {
|
|
var b64input = req.headers.authorization.split(" ")[1];
|
|
var decodedInput = new Buffer(b64input, 'base64').toString('ascii');
|
|
var username = decodedInput.split(":")[0];
|
|
var passwordArray = decodedInput.split(":");
|
|
var password = "";
|
|
for (var i = 1; i < passwordArray.length; i++) {
|
|
password += passwordArray[i];
|
|
if (i < passwordArray.length - 1) {
|
|
password += ":";
|
|
}
|
|
}
|
|
if (config.useRedmineAuth) {
|
|
let validRedmineUser = false;
|
|
var finalProfile = {};
|
|
let userProfile = await IsValidRedmineUser(username);
|
|
|
|
if (userProfile) {
|
|
validRedmineUser = true;
|
|
}
|
|
console.log(moment().format("LTS") + ' username ' + username +' - IsValidRedmineUser: ', validRedmineUser);
|
|
if (config.debug) {
|
|
console.log(moment().format("LTS") + ' username ' + username +' - password: ', password);
|
|
}
|
|
console.log(moment().format("LTS") + ' username ' + username +' - indesxforOldToken: ', password.indexOf("-"));
|
|
if (validRedmineUser === true) {
|
|
let validRedmineAuth = await IsValidRedmineAuth(username, password);
|
|
console.log("IsValidRedmineAuth: ", validRedmineAuth);
|
|
if (config.debug) {
|
|
console.log("IsValidOldSecret: ", IsValidOldSecret(username, password));
|
|
}
|
|
console.log("IsValidJWT: ", IsValidJWT(username, password));
|
|
if (validRedmineUser && (validRedmineAuth || IsValidOldSecret(username, password) || IsValidJWT(username, password))) {
|
|
res.status(200).json(userProfile);
|
|
} else {
|
|
res.status(401).json(null);
|
|
}
|
|
} else {
|
|
res.status(401).json(null);
|
|
}
|
|
|
|
} else {
|
|
|
|
if (config.noLDAP && config.noLDAP == true) {
|
|
if (IsValidOldSecret(username, password) || IsValidJWT(username, password)) {
|
|
res.status(200).json(null);
|
|
} else {
|
|
res.status(401).json(null);
|
|
}
|
|
} else {
|
|
var finalProfile = {};
|
|
let userProfile = await IsValidLDAPUser(username);
|
|
let projectApiKey = await getProjectApiKey(username);
|
|
let validLdapUser = false;
|
|
let userLdapName = username;
|
|
console.log("got profile: ", userProfile);
|
|
if (userProfile) {
|
|
validLdapUser = true;
|
|
}
|
|
if (userProfile.uid && (userProfile.uid != null)) {
|
|
finalProfile["id"] = getFirstElement(userProfile.uid);
|
|
}
|
|
if (userProfile.givenName && (userProfile.givenName != null)) {
|
|
finalProfile["firstname"] = getFirstElement(userProfile.uid);
|
|
}
|
|
if (userProfile.sn && (userProfile.sn != null)) {
|
|
finalProfile["lastname"] = getFirstElement(userProfile.sn);
|
|
}
|
|
if (userProfile.email && (userProfile.email != null)) {
|
|
finalProfile["email"] = getFirstElement(userProfile.email);
|
|
userLdapName = getFirstElement(userProfile.email);
|
|
console.log("using for LDAP: ", userLdapName);
|
|
}
|
|
if (userProfile.mail && (userProfile.mail != null)) {
|
|
finalProfile["email"] = getFirstElement(userProfile.email);
|
|
userLdapName = getFirstElement(userProfile.mail);
|
|
console.log("using for LDAP: ", userLdapName);
|
|
}
|
|
if (userProfile.displayName && (userProfile.displayName != null)) {
|
|
finalProfile["displayName"] = getFirstElement(userProfile.displayName);
|
|
}
|
|
if (projectApiKey && (projectApiKey != null)) {
|
|
finalProfile["RedmineApiKey"] = projectApiKey;
|
|
}
|
|
console.log(moment().format("LTS") + ' username ' + username +' - validInLDAP: ', validLdapUser);
|
|
if (config.debug) {
|
|
console.log(moment().format("LTS") + ' username ' + username +' - password: ', password);
|
|
}
|
|
console.log(moment().format("LTS") + ' username ' + username +' - indesxforOldToken: ', password.indexOf("-"));
|
|
if (validLdapUser === true) {
|
|
// let validLdapAuth = await IsValidLDAPAuth(username, password);
|
|
let validLdapAuth = await IsValidLDAPAuth(userLdapName, password);
|
|
if (validLdapUser && (validLdapAuth || IsValidOldSecret(username, password) || IsValidJWT(username, password))) {
|
|
// if (IsValidOldSecret(username, password) || IsValidJWT(username, password)) {
|
|
|
|
res.status(200).json(finalProfile);
|
|
} else {
|
|
res.status(401).json(null);
|
|
}
|
|
} else {
|
|
res.status(401).json(null);
|
|
}
|
|
// res.status(200).json(null);
|
|
}
|
|
}
|
|
|
|
} catch (e) {
|
|
console.log(moment().format("LTS") + ' Error: ', e);
|
|
}
|
|
} else {
|
|
console.log(moment().format("LTS") + ' no auth header');
|
|
res.status(401).json(null);
|
|
}
|
|
|
|
});
|
|
|
|
app.get('/login2fa', async function (req, res) {
|
|
if (req.headers.authorization && req.headers.authorization.startsWith("Basic")) {
|
|
try {
|
|
var b64input = req.headers.authorization.split(" ")[1];
|
|
var decodedInput = new Buffer(b64input, 'base64').toString('ascii');
|
|
var username = decodedInput.split(":")[0];
|
|
var passwordArray = decodedInput.split(":");
|
|
var password = "";
|
|
for (var i = 1; i < passwordArray.length; i++) {
|
|
password += passwordArray[i];
|
|
if (i < passwordArray.length - 1) {
|
|
password += ":";
|
|
}
|
|
}
|
|
if (config.useRedmineAuth) {
|
|
let validRedmineUser = false;
|
|
var finalProfile = {};
|
|
let userProfile = await IsValidRedmineUser2FA(username);
|
|
|
|
if (userProfile) {
|
|
validRedmineUser = true;
|
|
}
|
|
console.log(moment().format("LTS") + ' username ' + username +' - IsValidRedmineUser: ', validRedmineUser);
|
|
if (config.debug) {
|
|
console.log(moment().format("LTS") + ' username ' + username +' - password: ', password);
|
|
}
|
|
console.log(moment().format("LTS") + ' username ' + username +' - indesxforOldToken: ', password.indexOf("-"));
|
|
if (validRedmineUser === true) {
|
|
let validRedmineAuth = await IsValidRedmineAuth(username, password);
|
|
console.log("IsValidRedmineAuth: ", validRedmineAuth);
|
|
if (config.debug) {
|
|
console.log("IsValidOldSecret: ", IsValidOldSecret(username, password));
|
|
}
|
|
console.log("IsValidJWT: ", IsValidJWT(username, password));
|
|
if (validRedmineUser && (validRedmineAuth || IsValidOldSecret(username, password) || IsValidJWT(username, password))) {
|
|
res.status(200).json(userProfile);
|
|
} else {
|
|
res.status(401).json(null);
|
|
}
|
|
} else {
|
|
res.status(401).json(null);
|
|
}
|
|
|
|
} else {
|
|
|
|
if (config.noLDAP && config.noLDAP == true) {
|
|
if (IsValidOldSecret(username, password) || IsValidJWT(username, password)) {
|
|
res.status(200).json(null);
|
|
} else {
|
|
res.status(401).json(null);
|
|
}
|
|
} else {
|
|
var finalProfile = {};
|
|
let userProfile = await IsValidLDAPUser(username);
|
|
let projectApiKey = await getProjectApiKey(username);
|
|
let validLdapUser = false;
|
|
let userLdapName = username;
|
|
console.log("got profile: ", userProfile);
|
|
if (userProfile) {
|
|
validLdapUser = true;
|
|
}
|
|
if (userProfile.uid && (userProfile.uid != null)) {
|
|
finalProfile["id"] = getFirstElement(userProfile.uid);
|
|
}
|
|
if (userProfile.givenName && (userProfile.givenName != null)) {
|
|
finalProfile["firstname"] = getFirstElement(userProfile.uid);
|
|
}
|
|
if (userProfile.sn && (userProfile.sn != null)) {
|
|
finalProfile["lastname"] = getFirstElement(userProfile.sn);
|
|
}
|
|
if (userProfile.email && (userProfile.email != null)) {
|
|
finalProfile["email"] = getFirstElement(userProfile.email);
|
|
userLdapName = getFirstElement(userProfile.email);
|
|
console.log("using for LDAP: ", userLdapName);
|
|
}
|
|
if (userProfile.mail && (userProfile.mail != null)) {
|
|
finalProfile["email"] = getFirstElement(userProfile.email);
|
|
userLdapName = getFirstElement(userProfile.mail);
|
|
console.log("using for LDAP: ", userLdapName);
|
|
}
|
|
if (userProfile.displayName && (userProfile.displayName != null)) {
|
|
finalProfile["displayName"] = getFirstElement(userProfile.displayName);
|
|
}
|
|
if (projectApiKey && (projectApiKey != null)) {
|
|
finalProfile["RedmineApiKey"] = projectApiKey;
|
|
}
|
|
console.log(moment().format("LTS") + ' username ' + username +' - validInLDAP: ', validLdapUser);
|
|
if (config.debug) {
|
|
console.log(moment().format("LTS") + ' username ' + username +' - password: ', password);
|
|
}
|
|
console.log(moment().format("LTS") + ' username ' + username +' - indesxforOldToken: ', password.indexOf("-"));
|
|
if (validLdapUser === true) {
|
|
// let validLdapAuth = await IsValidLDAPAuth(username, password);
|
|
let validLdapAuth = await IsValidLDAPAuth(userLdapName, password);
|
|
if (validLdapUser && (validLdapAuth || IsValidOldSecret(username, password) || IsValidJWT(username, password))) {
|
|
// if (IsValidOldSecret(username, password) || IsValidJWT(username, password)) {
|
|
|
|
res.status(200).json(finalProfile);
|
|
} else {
|
|
res.status(401).json(null);
|
|
}
|
|
} else {
|
|
res.status(401).json(null);
|
|
}
|
|
// res.status(200).json(null);
|
|
}
|
|
}
|
|
|
|
} catch (e) {
|
|
console.log(moment().format("LTS") + ' Error: ', e);
|
|
}
|
|
} else {
|
|
console.log(moment().format("LTS") + ' no auth header');
|
|
res.status(401).json(null);
|
|
}
|
|
|
|
});
|
|
|
|
app.get('/health', async function (req, res) {
|
|
console.log(moment().format("LTS") + ' health check called');
|
|
res.status(200).json(null);
|
|
});
|
|
|
|
var server = app.listen(servicePort, function () {
|
|
var host = server.address().address;
|
|
var port = server.address().port;
|
|
console.log(moment().format("LTS") + ' Service endpoint is http://%s:%s/notify', host, port);
|
|
console.log(moment().format("LTS") + ' config', config);
|
|
});
|