merge userdb.js into users.js

This commit is contained in:
Girish Ramakrishnan
2021-07-15 09:50:11 -07:00
parent 2840bba4bf
commit a1c61facdc
27 changed files with 1021 additions and 1456 deletions
+99 -112
View File
@@ -56,7 +56,9 @@ function getUsersWithAccessToApp(req, callback) {
assert.strictEqual(typeof req.app, 'object');
assert.strictEqual(typeof callback, 'function');
users.getAll(function (error, result) {
const getAllUsers = util.callbackify(users.getAll);
getAllUsers(function (error, result) {
if (error) return callback(new ldap.OperationsError(error.toString()));
async.filter(result, apps.hasAccessTo.bind(null, req.app), function (error, allowedUsers) {
@@ -331,53 +333,45 @@ function mailboxSearch(req, res, next) {
finalSend(results, req, res, next);
});
} else { // new sogo
mailboxdb.listAllMailboxes(1, 1000, function (error, mailboxes) {
mailboxdb.listAllMailboxes(1, 1000, async function (error, mailboxes) {
if (error) return next(new ldap.OperationsError(error.toString()));
mailboxes = mailboxes.filter(m => m.active);
let results = [];
// send mailbox objects
async.eachSeries(mailboxes, function (mailbox, callback) {
var dn = ldap.parseDN(`cn=${mailbox.name}@${mailbox.domain},ou=mailboxes,dc=cloudron`);
for (const mailbox of mailboxes) {
const dn = ldap.parseDN(`cn=${mailbox.name}@${mailbox.domain},ou=mailboxes,dc=cloudron`);
let getFunc = mailbox.ownerType === mail.OWNERTYPE_USER ? users.get : groups.get;
const [error, ownerObject] = await safe(mailbox.ownerType === mail.OWNERTYPE_USER ? users.get(mailbox.ownerId) : groups.get(mailbox.ownerId));
if (error || !ownerObject) continue; // skip mailboxes with unknown user
getFunc(mailbox.ownerId, function (error, ownerObject) {
if (error) return callback(); // skip mailboxes with unknown owner
var obj = {
dn: dn.toString(),
attributes: {
objectclass: ['mailbox'],
objectcategory: 'mailbox',
displayname: mailbox.ownerType === mail.OWNERTYPE_USER ? ownerObject.displayName : ownerObject.name,
cn: `${mailbox.name}@${mailbox.domain}`,
uid: `${mailbox.name}@${mailbox.domain}`,
mail: `${mailbox.name}@${mailbox.domain}`
}
};
mailbox.aliases.forEach(function (a, idx) {
obj.attributes['mail' + idx] = `${a.name}@${a.domain}`;
});
// ensure all filter values are also lowercase
var lowerCaseFilter = safe(function () { return ldap.parseFilter(req.filter.toString().toLowerCase()); }, null);
if (!lowerCaseFilter) return next(new ldap.OperationsError(safe.error.toString()));
if ((req.dn.equals(dn) || req.dn.parentOf(dn)) && lowerCaseFilter.matches(obj.attributes)) {
results.push(obj);
const obj = {
dn: dn.toString(),
attributes: {
objectclass: ['mailbox'],
objectcategory: 'mailbox',
displayname: mailbox.ownerType === mail.OWNERTYPE_USER ? ownerObject.displayName : ownerObject.name,
cn: `${mailbox.name}@${mailbox.domain}`,
uid: `${mailbox.name}@${mailbox.domain}`,
mail: `${mailbox.name}@${mailbox.domain}`
}
};
callback();
mailbox.aliases.forEach(function (a, idx) {
obj.attributes['mail' + idx] = `${a.name}@${a.domain}`;
});
}, function (error) {
if (error) return next(new ldap.OperationsError(error.toString()));
finalSend(results, req, res, next);
});
// ensure all filter values are also lowercase
const lowerCaseFilter = safe(function () { return ldap.parseFilter(req.filter.toString().toLowerCase()); }, null);
if (!lowerCaseFilter) return next(new ldap.OperationsError(safe.error.toString()));
if ((req.dn.equals(dn) || req.dn.parentOf(dn)) && lowerCaseFilter.matches(obj.attributes)) {
results.push(obj);
}
}
finalSend(results, req, res, next);
});
}
}
@@ -465,34 +459,33 @@ function mailingListSearch(req, res, next) {
}
// Will attach req.user if successful
function authenticateUser(req, res, next) {
async function authenticateUser(req, res, next) {
debug('user bind: %s (from %s)', req.dn.toString(), req.connection.ldap.id);
// extract the common name which might have different attribute names
var attributeName = Object.keys(req.dn.rdns[0].attrs)[0];
var commonName = req.dn.rdns[0].attrs[attributeName].value;
const attributeName = Object.keys(req.dn.rdns[0].attrs)[0];
const commonName = req.dn.rdns[0].attrs[attributeName].value;
if (!commonName) return next(new ldap.NoSuchObjectError(req.dn.toString()));
var api;
let verifyFunc;
if (attributeName === 'mail') {
api = users.verifyWithEmail;
verifyFunc = users.verifyWithEmail;
} else if (commonName.indexOf('@') !== -1) { // if mail is specified, enforce mail check
api = users.verifyWithEmail;
verifyFunc = users.verifyWithEmail;
} else if (commonName.indexOf('uid-') === 0) {
api = users.verify;
verifyFunc = users.verify;
} else {
api = users.verifyWithUsername;
verifyFunc = users.verifyWithUsername;
}
api(commonName, req.credentials || '', req.app.id, function (error, user) {
if (error && error.reason === BoxError.NOT_FOUND) return next(new ldap.NoSuchObjectError(req.dn.toString()));
if (error && error.reason === BoxError.INVALID_CREDENTIALS) return next(new ldap.InvalidCredentialsError(req.dn.toString()));
if (error) return next(new ldap.OperationsError(error.message));
const [error, user] = await safe(verifyFunc(commonName, req.credentials || '', req.app.id));
if (error && error.reason === BoxError.NOT_FOUND) return next(new ldap.NoSuchObjectError(req.dn.toString()));
if (error && error.reason === BoxError.INVALID_CREDENTIALS) return next(new ldap.InvalidCredentialsError(req.dn.toString()));
if (error) return next(new ldap.OperationsError(error.message));
req.user = user;
req.user = user;
next();
});
next();
}
function authorizeUserForApp(req, res, next) {
@@ -511,27 +504,24 @@ function authorizeUserForApp(req, res, next) {
});
}
async function verifyMailboxPassword(mailbox, password, callback) {
async function verifyMailboxPassword(mailbox, password) {
assert.strictEqual(typeof mailbox, 'object');
assert.strictEqual(typeof password, 'string');
assert.strictEqual(typeof callback, 'function');
if (mailbox.ownerType === mail.OWNERTYPE_USER) return users.verify(mailbox.ownerId, password, users.AP_MAIL /* identifier */, callback);
if (mailbox.ownerType === mail.OWNERTYPE_USER) return await users.verify(mailbox.ownerId, password, users.AP_MAIL /* identifier */);
const [error, userIds] = await safe(groups.getMembers(mailbox.ownerId));
if (error) return callback(error);
const userIds = await groups.getMembers(mailbox.ownerId);
let verifiedUser = null;
async.someSeries(userIds, function iterator(userId, iteratorDone) {
users.verify(userId, password, users.AP_MAIL /* identifier */, function (error, result) {
if (error) return iteratorDone(null, false);
verifiedUser = result;
iteratorDone(null, true);
});
}, function (error, result) {
if (!result) return callback(new BoxError(BoxError.INVALID_CREDENTIALS));
callback(null, verifiedUser);
});
for (const userId of userIds) {
const [error, result] = await safe(users.verify(userId, password, users.AP_MAIL /* identifier */));
if (error) continue; // try the next user
verifiedUser = result;
break; // found a matching validated user
}
if (!verifiedUser) throw new BoxError(BoxError.INVALID_CREDENTIALS);
return verifiedUser;
}
function authenticateUserMailbox(req, res, next) {
@@ -551,21 +541,20 @@ function authenticateUserMailbox(req, res, next) {
if (!domain.enabled) return next(new ldap.NoSuchObjectError(req.dn.toString()));
mailboxdb.getMailbox(parts[0], parts[1], function (error, mailbox) {
mailboxdb.getMailbox(parts[0], parts[1], async function (error, mailbox) {
if (error && error.reason === BoxError.NOT_FOUND) return next(new ldap.NoSuchObjectError(req.dn.toString()));
if (error) return next(new ldap.OperationsError(error.message));
if (!mailbox.active) return next(new ldap.NoSuchObjectError(req.dn.toString()));
verifyMailboxPassword(mailbox, req.credentials || '', async function (error, result) {
if (error && error.reason === BoxError.NOT_FOUND) return next(new ldap.NoSuchObjectError(req.dn.toString()));
if (error && error.reason === BoxError.INVALID_CREDENTIALS) return next(new ldap.InvalidCredentialsError(req.dn.toString()));
if (error) return next(new ldap.OperationsError(error.message));
const [verifyError, result] = await safe(verifyMailboxPassword(mailbox, req.credentials || ''));
if (verifyError && verifyError.reason === BoxError.NOT_FOUND) return next(new ldap.NoSuchObjectError(req.dn.toString()));
if (verifyError && verifyError.reason === BoxError.INVALID_CREDENTIALS) return next(new ldap.InvalidCredentialsError(req.dn.toString()));
if (verifyError) return next(new ldap.OperationsError(verifyError.message));
eventlog.upsertLoginEvent(eventlog.ACTION_USER_LOGIN, { authType: 'ldap', mailboxId: email }, { userId: result.id, user: users.removePrivateFields(result) });
eventlog.upsertLoginEvent(eventlog.ACTION_USER_LOGIN, { authType: 'ldap', mailboxId: email }, { userId: result.id, user: users.removePrivateFields(result) });
res.end();
});
res.end();
});
});
}
@@ -575,20 +564,19 @@ function authenticateSftp(req, res, next) {
if (!req.dn.rdns[0].attrs.cn) return next(new ldap.NoSuchObjectError(req.dn.toString()));
var email = req.dn.rdns[0].attrs.cn.value.toLowerCase();
var parts = email.split('@');
const email = req.dn.rdns[0].attrs.cn.value.toLowerCase();
const parts = email.split('@');
if (parts.length !== 2) return next(new ldap.NoSuchObjectError(req.dn.toString()));
apps.getByFqdn(parts[1], function (error, app) {
apps.getByFqdn(parts[1], async function (error, app) {
if (error) return next(new ldap.InvalidCredentialsError(req.dn.toString()));
users.verifyWithUsername(parts[0], req.credentials, app.id, function (error) {
if (error) return next(new ldap.InvalidCredentialsError(req.dn.toString()));
[error] = await safe(users.verifyWithUsername(parts[0], req.credentials, app.id));
if (error) return next(new ldap.InvalidCredentialsError(req.dn.toString()));
debug('sftp auth: success');
debug('sftp auth: success');
res.end();
});
res.end();
});
}
@@ -613,7 +601,7 @@ function userSearchSftp(req, res, next) {
var username = parts[0];
var appFqdn = parts[1];
apps.getByFqdn(appFqdn, function (error, app) {
apps.getByFqdn(appFqdn, async function (error, app) {
if (error) return next(new ldap.OperationsError(error.toString()));
// only allow apps which specify "ftp" support in the localstorage addon
@@ -622,30 +610,30 @@ function userSearchSftp(req, res, next) {
const uidNumber = app.manifest.addons.localstorage.ftp.uid;
users.getByUsername(username, function (error, user) {
const [userGetError, user] = await users.getByUsername(username);
if (userGetError) return next(new ldap.OperationsError(userGetError.toString()));
if (!user) return next(new ldap.OperationsError('Invalid username'));
if (req.requireAdmin && users.compareRoles(user.role, users.ROLE_ADMIN) < 0) return next(new ldap.InsufficientAccessRightsError('Insufficient previleges'));
apps.hasAccessTo(app, user, function (error, hasAccess) {
if (error) return next(new ldap.OperationsError(error.toString()));
if (!hasAccess) return next(new ldap.InsufficientAccessRightsError('Not authorized'));
if (req.requireAdmin && users.compareRoles(user.role, users.ROLE_ADMIN) < 0) return next(new ldap.InsufficientAccessRightsError('Insufficient previleges'));
var obj = {
dn: ldap.parseDN(`cn=${username}@${appFqdn},ou=sftp,dc=cloudron`).toString(),
attributes: {
homeDirectory: app.dataDir ? `/mnt/${app.id}` : `/mnt/appsdata/${app.id}/data`,
objectclass: ['user'],
objectcategory: 'person',
cn: user.id,
uid: `${username}@${appFqdn}`, // for bind after search
uidNumber: uidNumber, // unix uid for ftp access
gidNumber: uidNumber // unix gid for ftp access
}
};
apps.hasAccessTo(app, user, function (error, hasAccess) {
if (error) return next(new ldap.OperationsError(error.toString()));
if (!hasAccess) return next(new ldap.InsufficientAccessRightsError('Not authorized'));
var obj = {
dn: ldap.parseDN(`cn=${username}@${appFqdn},ou=sftp,dc=cloudron`).toString(),
attributes: {
homeDirectory: app.dataDir ? `/mnt/${app.id}` : `/mnt/appsdata/${app.id}/data`,
objectclass: ['user'],
objectcategory: 'person',
cn: user.id,
uid: `${username}@${appFqdn}`, // for bind after search
uidNumber: uidNumber, // unix uid for ftp access
gidNumber: uidNumber // unix gid for ftp access
}
};
finalSend([ obj ], req, res, next);
});
finalSend([ obj ], req, res, next);
});
});
}
@@ -696,21 +684,20 @@ function authenticateMailAddon(req, res, next) {
if (error && error.reason === BoxError.INVALID_CREDENTIALS) return next(new ldap.InvalidCredentialsError(req.dn.toString()));
if (error && error.reason !== BoxError.NOT_FOUND) return next(new ldap.OperationsError(error.message));
mailboxdb.getMailbox(parts[0], parts[1], function (error, mailbox) {
mailboxdb.getMailbox(parts[0], parts[1], async function (error, mailbox) {
if (error && error.reason === BoxError.NOT_FOUND) return next(new ldap.NoSuchObjectError(req.dn.toString()));
if (error) return next(new ldap.OperationsError(error.message));
if (!mailbox.active) return next(new ldap.NoSuchObjectError(req.dn.toString()));
verifyMailboxPassword(mailbox, req.credentials || '', async function (error, result) {
if (error && error.reason === BoxError.NOT_FOUND) return next(new ldap.NoSuchObjectError(req.dn.toString()));
if (error && error.reason === BoxError.INVALID_CREDENTIALS) return next(new ldap.InvalidCredentialsError(req.dn.toString()));
if (error) return next(new ldap.OperationsError(error.message));
const [verifyError, result] = await safe(verifyMailboxPassword(mailbox, req.credentials || ''));
if (verifyError && verifyError.reason === BoxError.NOT_FOUND) return next(new ldap.NoSuchObjectError(req.dn.toString()));
if (verifyError && verifyError.reason === BoxError.INVALID_CREDENTIALS) return next(new ldap.InvalidCredentialsError(req.dn.toString()));
if (verifyError) return next(new ldap.OperationsError(verifyError.message));
eventlog.upsertLoginEvent(eventlog.ACTION_USER_LOGIN, { authType: 'ldap', mailboxId: email }, { userId: result.id, user: users.removePrivateFields(result) });
eventlog.upsertLoginEvent(eventlog.ACTION_USER_LOGIN, { authType: 'ldap', mailboxId: email }, { userId: result.id, user: users.removePrivateFields(result) });
res.end();
});
res.end();
});
});
});