mirror of https://github.com/xwiki-labs/cryptpad
93 lines
2.8 KiB
JavaScript
93 lines
2.8 KiB
JavaScript
// SPDX-FileCopyrightText: 2023 XWiki CryptPad Team <contact@cryptpad.org> and contributors
|
|
//
|
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
|
|
const Basic = require("./basic");
|
|
const Path = require("node:path");
|
|
const Util = require("../common-util");
|
|
const Sessions = require("./sessions");
|
|
const nThen = require("nthen");
|
|
|
|
const MFA = module.exports;
|
|
|
|
/*
|
|
This module manages storage related to accounts' multi-factor authentication settings.
|
|
|
|
These settings are checked every time a block is accessed, so we do as little as possible
|
|
so that it can be accessed quickly.
|
|
|
|
*/
|
|
|
|
/* The path for a given account's settings is based on the public signing key
|
|
which identifies its "login block". We expect that any action to create or access
|
|
a block will be authenticated with a challenge-response protocol, so we
|
|
don't bother checking the validity of an identifier in here aside from
|
|
ensuring that it won't throw when using string methods.
|
|
|
|
*/
|
|
var pathFromId = function (Env, id) {
|
|
if (!id || typeof(id) !== 'string') { return; }
|
|
id = Util.escapeKeyCharacters(id);
|
|
return Path.join(Env.paths.base, "mfa", id.slice(0, 2), `${id}.json`);
|
|
};
|
|
|
|
MFA.read = function (Env, id, cb) {
|
|
var path = pathFromId(Env, id);
|
|
Basic.read(Env, path, cb);
|
|
};
|
|
|
|
// data should be a string
|
|
MFA.write = function (Env, id, data, cb) {
|
|
var path = pathFromId(Env, id);
|
|
Basic.write(Env, path, data, cb);
|
|
};
|
|
|
|
MFA.delete = function (Env, id, cb) {
|
|
var path = pathFromId(Env, id);
|
|
Basic.delete(Env, path, cb);
|
|
};
|
|
|
|
MFA.revoke = function (Env, publicKey, cb) {
|
|
nThen(function (w) {
|
|
MFA.delete(Env, publicKey, w(function (err) {
|
|
if (!err) { return; }
|
|
w.abort();
|
|
Env.Log.error('TOTP_REVOKE_MFA_DELETE', {
|
|
error: err,
|
|
publicKey: publicKey,
|
|
});
|
|
cb('MFA_ERROR');
|
|
}));
|
|
}).nThen(function () {
|
|
Sessions.deleteUser(Env, publicKey, function (err) {
|
|
if (!err) { return; }
|
|
// If we can't delete the sessions, don't send an error, just log to the server.
|
|
// The MFA will still be correctly disabled as long as the first step is done.
|
|
Env.Log.error('TOTP_REVOKE_SESSIONS__DELETE', {
|
|
error: err,
|
|
publicKey: publicKey,
|
|
});
|
|
});
|
|
}).nThen(function () {
|
|
cb(void 0, {
|
|
success: true
|
|
});
|
|
});
|
|
};
|
|
|
|
MFA.copy = function (Env, oldKey, newKey, cb) {
|
|
let content;
|
|
nThen(function (w) {
|
|
MFA.read(Env, oldKey, w(function (err, c) {
|
|
if (err) {
|
|
// No MFA configured, nothing to copy
|
|
w.abort();
|
|
return void cb();
|
|
}
|
|
content = c;
|
|
}));
|
|
}).nThen(function () {
|
|
MFA.write(Env, newKey, content, cb);
|
|
});
|
|
};
|