safe import implementation

This commit is contained in:
zadam 2019-02-11 23:45:58 +01:00
parent caa7dd9619
commit 14f7a8b7b9
5 changed files with 53 additions and 17 deletions

View file

@ -12,6 +12,7 @@ const $fileUploadInput = $("#import-file-upload-input");
const $importProgressCountWrapper = $("#import-progress-count-wrapper"); const $importProgressCountWrapper = $("#import-progress-count-wrapper");
const $importProgressCount = $("#import-progress-count"); const $importProgressCount = $("#import-progress-count");
const $importButton = $("#import-button"); const $importButton = $("#import-button");
const $safeImport = $("#safe-import");
let importId; let importId;
@ -21,6 +22,7 @@ async function showDialog() {
$importProgressCountWrapper.hide(); $importProgressCountWrapper.hide();
$importProgressCount.text('0'); $importProgressCount.text('0');
$fileUploadInput.val('').change(); // to trigger Import button disabling listener below $fileUploadInput.val('').change(); // to trigger Import button disabling listener below
$safeImport.attr("checked", "checked");
glob.activeDialog = $dialog; glob.activeDialog = $dialog;
@ -49,8 +51,10 @@ function importIntoNote(importNoteId) {
// dialog (which shouldn't happen, but still ...) // dialog (which shouldn't happen, but still ...)
importId = utils.randomString(10); importId = utils.randomString(10);
const safeImport = $safeImport.is(":checked") ? 1 : 0;
$.ajax({ $.ajax({
url: baseApiUrl + 'notes/' + importNoteId + '/import/' + importId, url: baseApiUrl + 'notes/' + importNoteId + '/import/' + importId + '/safe/' + safeImport,
headers: server.getHeaders(), headers: server.getHeaders(),
data: formData, data: formData,
dataType: 'json', dataType: 'json',

View file

@ -12,10 +12,13 @@ const noteCacheService = require('../../services/note_cache');
const log = require('../../services/log'); const log = require('../../services/log');
class ImportContext { class ImportContext {
constructor(importId) { constructor(importId, safeImport) {
// importId is to distinguish between different import events - it is possible (though not recommended) // importId is to distinguish between different import events - it is possible (though not recommended)
// to have multiple imports going at the same time // to have multiple imports going at the same time
this.importId = importId; this.importId = importId;
this.safeImport = safeImport;
// // count is mean to represent count of exported notes where practical, otherwise it's just some measure of progress // // count is mean to represent count of exported notes where practical, otherwise it's just some measure of progress
this.progressCount = 0; this.progressCount = 0;
this.lastSentCountTs = Date.now(); this.lastSentCountTs = Date.now();
@ -53,7 +56,10 @@ class ImportContext {
} }
async function importToBranch(req) { async function importToBranch(req) {
const {parentNoteId, importId} = req.params; let {parentNoteId, importId, safeImport} = req.params;
safeImport = safeImport !== '0';
const file = req.file; const file = req.file;
if (!file) { if (!file) {
@ -74,7 +80,7 @@ async function importToBranch(req) {
let note; // typically root of the import - client can show it after finishing the import let note; // typically root of the import - client can show it after finishing the import
const importContext = new ImportContext(importId); const importContext = new ImportContext(importId, safeImport);
try { try {
if (extension === '.tar') { if (extension === '.tar') {

View file

@ -129,7 +129,7 @@ function register(app) {
apiRoute(PUT, '/api/notes/:noteId/clone-after/:afterBranchId', cloningApiRoute.cloneNoteAfter); apiRoute(PUT, '/api/notes/:noteId/clone-after/:afterBranchId', cloningApiRoute.cloneNoteAfter);
route(GET, '/api/notes/:branchId/export/:type/:format/:exportId', [auth.checkApiAuthOrElectron], exportRoute.exportBranch); route(GET, '/api/notes/:branchId/export/:type/:format/:exportId', [auth.checkApiAuthOrElectron], exportRoute.exportBranch);
route(POST, '/api/notes/:parentNoteId/import/:importId', [auth.checkApiAuthOrElectron, uploadMiddleware], importRoute.importToBranch, apiResultHandler); route(POST, '/api/notes/:parentNoteId/import/:importId/safe/:safeImport', [auth.checkApiAuthOrElectron, uploadMiddleware], importRoute.importToBranch, apiResultHandler);
route(POST, '/api/notes/:parentNoteId/upload', [auth.checkApiAuthOrElectron, uploadMiddleware], route(POST, '/api/notes/:parentNoteId/upload', [auth.checkApiAuthOrElectron, uploadMiddleware],
filesRoute.uploadFile, apiResultHandler); filesRoute.uploadFile, apiResultHandler);

View file

@ -5,13 +5,14 @@ const sql = require('./sql');
const utils = require('./utils'); const utils = require('./utils');
const Attribute = require('../entities/attribute'); const Attribute = require('../entities/attribute');
const ATTRIBUTE_TYPES = [ 'label', 'label-definition', 'relation', 'relation-definition' ];
const BUILTIN_ATTRIBUTES = [ const BUILTIN_ATTRIBUTES = [
// label names // label names
{ type: 'label', name: 'disableVersioning' }, { type: 'label', name: 'disableVersioning' },
{ type: 'label', name: 'calendarRoot' }, { type: 'label', name: 'calendarRoot' },
{ type: 'label', name: 'archived' }, { type: 'label', name: 'archived' },
{ type: 'label', name: 'excludeFromExport' }, { type: 'label', name: 'excludeFromExport' },
{ type: 'label', name: 'run' },
{ type: 'label', name: 'manualTransactionHandling' }, { type: 'label', name: 'manualTransactionHandling' },
{ type: 'label', name: 'disableInclusion' }, { type: 'label', name: 'disableInclusion' },
{ type: 'label', name: 'appCss' }, { type: 'label', name: 'appCss' },
@ -19,19 +20,20 @@ const BUILTIN_ATTRIBUTES = [
{ type: 'label', name: 'hideChildrenOverview' }, { type: 'label', name: 'hideChildrenOverview' },
{ type: 'label', name: 'hidePromotedAttributes' }, { type: 'label', name: 'hidePromotedAttributes' },
{ type: 'label', name: 'readOnly' }, { type: 'label', name: 'readOnly' },
{ type: 'label', name: 'customRequestHandler' }, { type: 'label', name: 'run', isDangerous: true },
{ type: 'label', name: 'customResourceProvider' }, { type: 'label', name: 'customRequestHandler', isDangerous: true },
{ type: 'label', name: 'customResourceProvider', isDangerous: true },
// relation names // relation names
{ type: 'relation', name: 'runOnNoteView' }, { type: 'relation', name: 'runOnNoteView', isDangerous: true },
{ type: 'relation', name: 'runOnNoteCreation' }, { type: 'relation', name: 'runOnNoteCreation', isDangerous: true },
{ type: 'relation', name: 'runOnNoteTitleChange' }, { type: 'relation', name: 'runOnNoteTitleChange', isDangerous: true },
{ type: 'relation', name: 'runOnNoteChange' }, { type: 'relation', name: 'runOnNoteChange', isDangerous: true },
{ type: 'relation', name: 'runOnChildNoteCreation' }, { type: 'relation', name: 'runOnChildNoteCreation', isDangerous: true },
{ type: 'relation', name: 'runOnAttributeCreation' }, { type: 'relation', name: 'runOnAttributeCreation', isDangerous: true },
{ type: 'relation', name: 'runOnAttributeChange' }, { type: 'relation', name: 'runOnAttributeChange', isDangerous: true },
{ type: 'relation', name: 'template' }, { type: 'relation', name: 'template' },
{ type: 'relation', name: 'renderNote' } { type: 'relation', name: 'renderNote', isDangerous: true }
]; ];
async function getNotesWithLabel(name, value) { async function getNotesWithLabel(name, value) {
@ -94,11 +96,25 @@ async function getAttributeNames(type, nameLike) {
return names; return names;
} }
function isAttributeType(type) {
return ATTRIBUTE_TYPES.includes(type);
}
function isAttributeDangerous(type, name) {
return BUILTIN_ATTRIBUTES.some(attr =>
attr.type === attr.type &&
attr.name.toLowerCase() === name.trim().toLowerCase() &&
attr.isDangerous
);
}
module.exports = { module.exports = {
getNotesWithLabel, getNotesWithLabel,
getNotesWithLabels, getNotesWithLabels,
getNoteWithLabel, getNoteWithLabel,
createLabel, createLabel,
createAttribute, createAttribute,
getAttributeNames getAttributeNames,
isAttributeType,
isAttributeDangerous
}; };

View file

@ -6,6 +6,7 @@ const utils = require('../../services/utils');
const log = require('../../services/log'); const log = require('../../services/log');
const repository = require('../../services/repository'); const repository = require('../../services/repository');
const noteService = require('../../services/notes'); const noteService = require('../../services/notes');
const attributeService = require('../../services/attributes');
const Branch = require('../../entities/branch'); const Branch = require('../../entities/branch');
const tar = require('tar-stream'); const tar = require('tar-stream');
const stream = require('stream'); const stream = require('stream');
@ -153,10 +154,19 @@ async function importTar(importContext, fileBuffer, importRootNote) {
for (const attr of noteMeta.attributes) { for (const attr of noteMeta.attributes) {
attr.noteId = note.noteId; attr.noteId = note.noteId;
if (!attributeService.isAttributeType(attr.type)) {
log.error("Unrecognized attribute type " + attr.type);
continue;
}
if (attr.type === 'relation') { if (attr.type === 'relation') {
attr.value = getNewNoteId(attr.value); attr.value = getNewNoteId(attr.value);
} }
if (importContext.safeImport && attributeService.isAttributeDangerous(attr.type, attr.name)) {
attr.name = 'disabled-' + attr.name;
}
attributes.push(attr); attributes.push(attr);
} }