generate anonymization script into a file

This commit is contained in:
zadam
2022-01-17 23:47:26 +01:00
parent 66a6c76552
commit d1b39ee8fa
18 changed files with 119 additions and 116 deletions

View File

@@ -2,14 +2,13 @@
const dateUtils = require('../../services/date_utils');
const AbstractEntity = require("./abstract_entity");
const sql = require("../../services/sql.js");
/**
* EtapiToken is an entity representing token used to authenticate against Trilium REST API from client applications.
* Used by:
* - Trilium Sender
* - ETAPI clients
*
*
* The format user is presented with is "<etapiTokenId>_<tokenHash>". This is also called "authToken" to distinguish it
* from tokenHash and token.
*/
@@ -42,10 +41,10 @@ class EtapiToken extends AbstractEntity {
this.utcDateModified = row.utcDateModified || this.utcDateCreated;
/** @type {boolean} */
this.isDeleted = !!row.isDeleted;
this.becca.etapiTokens[this.etapiTokenId] = this;
}
init() {
if (this.etapiTokenId) {
this.becca.etapiTokens[this.etapiTokenId] = this;

View File

@@ -1,7 +1,7 @@
const becca = require("../becca/becca");
const eu = require("./etapi_utils");
const passwordEncryptionService = require("../services/password_encryption.js");
const etapiTokenService = require("../services/etapi_tokens.js");
const passwordEncryptionService = require("../services/password_encryption");
const etapiTokenService = require("../services/etapi_tokens");
function register(router) {
eu.NOT_AUTHENTICATED_ROUTE(router, 'post', '/etapi/auth/login', (req, res, next) => {

View File

@@ -2,8 +2,8 @@ const cls = require("../services/cls");
const sql = require("../services/sql");
const log = require("../services/log");
const becca = require("../becca/becca");
const etapiTokenService = require("../services/etapi_tokens.js");
const config = require("../services/config.js");
const etapiTokenService = require("../services/etapi_tokens");
const config = require("../services/config");
const GENERIC_CODE = "GENERIC";
const noAuthentication = config.General && config.General.noAuthentication === true;
@@ -11,7 +11,7 @@ const noAuthentication = config.General && config.General.noAuthentication === t
class EtapiError extends Error {
constructor(statusCode, code, message) {
super();
this.statusCode = statusCode;
this.code = code;
this.message = message;
@@ -72,7 +72,7 @@ function NOT_AUTHENTICATED_ROUTE(router, method, path, routeHandler) {
function getAndCheckNote(noteId) {
const note = becca.getNote(noteId);
if (note) {
return note;
}
@@ -118,7 +118,7 @@ function validateAndPatch(target, source, allowedProperties) {
}
}
}
// validation passed, let's patch
for (const propName of Object.keys(source)) {
target[propName] = source[propName];

View File

@@ -3,10 +3,11 @@
const sql = require('../../services/sql');
const log = require('../../services/log');
const backupService = require('../../services/backup');
const anonymizationService = require('../../services/anonymization');
const consistencyChecksService = require('../../services/consistency_checks');
async function anonymize() {
return await backupService.anonymize();
return await anonymizationService.createAnonymizedCopy();
}
async function backupDatabase() {

View File

@@ -0,0 +1,60 @@
const BUILTIN_ATTRIBUTES = require("./builtin_attributes");
const fs = require("fs-extra");
const dataDir = require("./data_dir");
const dateUtils = require("./date_utils");
const Database = require("better-sqlite3");
const sql = require("./sql");
function getAnonymizationScript() {
// we want to delete all non-builtin attributes because they can contain sensitive names and values
// on the other hand builtin/system attrs should not contain any sensitive info
const builtinAttrNames = BUILTIN_ATTRIBUTES
.map(attr => "'" + attr.name + "'").join(', ');
const anonymizeScript = `
UPDATE etapi_tokens SET tokenHash = 'API token hash value';
UPDATE notes SET title = 'title';
UPDATE note_contents SET content = 'text' WHERE content IS NOT NULL;
UPDATE note_revisions SET title = 'title';
UPDATE note_revision_contents SET content = 'text' WHERE content IS NOT NULL;
UPDATE attributes SET name = 'name', value = 'value' WHERE type = 'label' AND name NOT IN(${builtinAttrNames});
UPDATE attributes SET name = 'name' WHERE type = 'relation' AND name NOT IN (${builtinAttrNames});
UPDATE branches SET prefix = 'prefix' WHERE prefix IS NOT NULL;
UPDATE options SET value = 'anonymized' WHERE name IN
('documentId', 'documentSecret', 'encryptedDataKey',
'passwordVerificationHash', 'passwordVerificationSalt',
'passwordDerivedKeySalt', 'username', 'syncServerHost', 'syncProxy')
AND value != '';
VACUUM;
`;
return anonymizeScript;
}
async function createAnonymizedCopy() {
if (!fs.existsSync(dataDir.ANONYMIZED_DB_DIR)) {
fs.mkdirSync(dataDir.ANONYMIZED_DB_DIR, 0o700);
}
const anonymizedFile = dataDir.ANONYMIZED_DB_DIR + "/" + "anonymized-" + dateUtils.getDateTimeForFile() + ".db";
await sql.copyDatabase(anonymizedFile);
const db = new Database(anonymizedFile);
db.exec(getAnonymizationScript());
db.close();
return {
success: true,
anonymizedFilePath: anonymizedFile
};
}
module.exports = {
getAnonymizationScript,
createAnonymizedCopy
}

View File

@@ -6,9 +6,8 @@ const fs = require('fs-extra');
const dataDir = require('./data_dir');
const log = require('./log');
const syncMutexService = require('./sync_mutex');
const attributeService = require('./attributes');
const cls = require('./cls');
const Database = require('better-sqlite3');
const sql = require('./sql');
function regularBackup() {
cls.init(() => {
@@ -41,23 +40,12 @@ function periodBackup(optionName, backupType, periodInSeconds) {
}
}
async function copyFile(backupFile) {
const sql = require('./sql');
try {
fs.unlinkSync(backupFile);
} catch (e) {
} // unlink throws exception if the file did not exist
await sql.dbConnection.backup(backupFile);
}
async function backupNow(name) {
// we don't want to backup DB in the middle of sync with potentially inconsistent DB state
// we don't want to back up DB in the middle of sync with potentially inconsistent DB state
return await syncMutexService.doExclusively(async () => {
const backupFile = `${dataDir.BACKUP_DIR}/backup-${name}.db`;
await copyFile(backupFile);
await sql.copyDatabase(backupFile);
log.info("Created backup at " + backupFile);
@@ -65,53 +53,11 @@ async function backupNow(name) {
});
}
async function anonymize() {
if (!fs.existsSync(dataDir.ANONYMIZED_DB_DIR)) {
fs.mkdirSync(dataDir.ANONYMIZED_DB_DIR, 0o700);
}
const anonymizedFile = dataDir.ANONYMIZED_DB_DIR + "/" + "anonymized-" + dateUtils.getDateTimeForFile() + ".db";
await copyFile(anonymizedFile);
const db = new Database(anonymizedFile);
db.prepare("UPDATE etapi_tokens SET tokenHash = 'API token hash value'").run();
db.prepare("UPDATE notes SET title = 'title'").run();
db.prepare("UPDATE note_contents SET content = 'text' WHERE content IS NOT NULL").run();
db.prepare("UPDATE note_revisions SET title = 'title'").run();
db.prepare("UPDATE note_revision_contents SET content = 'text' WHERE content IS NOT NULL").run();
// we want to delete all non-builtin attributes because they can contain sensitive names and values
// on the other hand builtin/system attrs should not contain any sensitive info
const builtinAttrs = attributeService
.getBuiltinAttributeNames()
.map(name => "'" + name + "'").join(', ');
db.prepare(`UPDATE attributes SET name = 'name', value = 'value' WHERE type = 'label' AND name NOT IN(${builtinAttrs})`).run();
db.prepare(`UPDATE attributes SET name = 'name' WHERE type = 'relation' AND name NOT IN (${builtinAttrs})`).run();
db.prepare("UPDATE branches SET prefix = 'prefix' WHERE prefix IS NOT NULL").run();
db.prepare(`UPDATE options SET value = 'anonymized' WHERE name IN
('documentId', 'documentSecret', 'encryptedDataKey',
'passwordVerificationHash', 'passwordVerificationSalt',
'passwordDerivedKeySalt', 'username', 'syncServerHost', 'syncProxy')
AND value != ''`).run();
db.prepare("VACUUM").run();
db.close();
return {
success: true,
anonymizedFilePath: anonymizedFile
};
}
if (!fs.existsSync(dataDir.BACKUP_DIR)) {
fs.mkdirSync(dataDir.BACKUP_DIR, 0o700);
}
module.exports = {
backupNow,
anonymize,
regularBackup
};

View File

@@ -8,6 +8,7 @@ const log = require('./log');
const Database = require('better-sqlite3');
const dataDir = require('./data_dir');
const cls = require('./cls');
const fs = require("fs-extra");
const dbConnection = new Database(dataDir.DOCUMENT_PATH);
dbConnection.pragma('journal_mode = WAL');
@@ -276,6 +277,15 @@ function fillParamList(paramIds, truncate = true) {
s.run(paramIds);
}
async function copyDatabase(targetFilePath) {
try {
fs.unlinkSync(targetFilePath);
} catch (e) {
} // unlink throws exception if the file did not exist
await dbConnection.backup(targetFilePath);
}
module.exports = {
dbConnection,
insert,
@@ -347,5 +357,6 @@ module.exports = {
executeScript,
transactional,
upsert,
fillParamList
fillParamList,
copyDatabase
};