mirror of
https://github.com/zadam/trilium.git
synced 2025-11-12 00:05:50 +01:00
generate anonymization script into a file
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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() {
|
||||
|
||||
60
src/services/anonymization.js
Normal file
60
src/services/anonymization.js
Normal 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
|
||||
}
|
||||
@@ -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
|
||||
};
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user