From 57657dcaad6b0b60ffb051fe9e047e3adea670c0 Mon Sep 17 00:00:00 2001 From: Sergey Konovalov Date: Tue, 4 May 2021 15:09:46 +0300 Subject: [PATCH] [bug] Fix bug 49714 --- Common/sources/commondefines.js | 8 ++++++ DocService/sources/DocsCoServer.js | 15 +++++++--- DocService/sources/baseConnector.js | 22 ++++++-------- DocService/sources/canvasservice.js | 21 +++++++------- DocService/sources/converterservice.js | 5 ++-- DocService/sources/editorDataMemory.js | 4 +-- DocService/sources/taskresult.js | 40 ++++++++++++++++++++------ FileConverter/sources/converter.js | 27 +++++++++++++---- 8 files changed, 95 insertions(+), 47 deletions(-) diff --git a/Common/sources/commondefines.js b/Common/sources/commondefines.js index 817788a6e..3622e3583 100644 --- a/Common/sources/commondefines.js +++ b/Common/sources/commondefines.js @@ -37,6 +37,7 @@ const constants = require('./constants'); function InputCommand(data, copyExplicit) { //must be set explicitly to prevent vulnerability(downloadAs(with url) creates request to integrator with authorization) this['withAuthorization'] = undefined;//bool + this['externalChangeInfo'] = undefined;//zero DB changes case: set password, undo all changes if (data) { this['c'] = data['c']; this['id'] = data['id']; @@ -100,6 +101,7 @@ function InputCommand(data, copyExplicit) { this['attempt'] = data['attempt']; if (copyExplicit) { this['withAuthorization'] = data['withAuthorization']; + this['externalChangeInfo'] = data['externalChangeInfo']; } } else { this['c'] = undefined;//string command @@ -438,6 +440,12 @@ InputCommand.prototype = { }, setWithAuthorization: function(data) { this['withAuthorization'] = data; + }, + getExternalChangeInfo: function() { + return this['externalChangeInfo']; + }, + setExternalChangeInfo: function(data) { + this['externalChangeInfo'] = data; } }; diff --git a/DocService/sources/DocsCoServer.js b/DocService/sources/DocsCoServer.js index a4fab7825..f62f11ca0 100644 --- a/DocService/sources/DocsCoServer.js +++ b/DocService/sources/DocsCoServer.js @@ -709,6 +709,7 @@ function* getChangesIndex(docId) { } const hasChanges = co.wrap(function*(docId) { + //todo check editorData.getForceSave in case of "undo all changes" let puckerIndex = yield* getChangesIndex(docId); if (0 === puckerIndex) { let selectRes = yield taskResult.select(docId); @@ -779,7 +780,7 @@ let startForceSave = co.wrap(function*(docId, type, opt_userdata, opt_userId, op priority = constants.QUEUE_PRIORITY_LOW; } //start new convert - let status = yield* converterService.convertFromChanges(docId, baseUrl, forceSave, opt_userdata, + let status = yield* converterService.convertFromChanges(docId, baseUrl, forceSave, startedForceSave.changeInfo, opt_userdata, opt_userConnectionId, opt_responseKey, priority, expiration, opt_queue); if (constants.NO_ERROR === status.err) { res.time = forceSave.getTime(); @@ -793,10 +794,13 @@ let startForceSave = co.wrap(function*(docId, type, opt_userdata, opt_userId, op logger.debug('startForceSave end:docId = %s', docId); return res; }); -let resetForceSaveAfterChanges = co.wrap(function*(docId, newChangesLastTime, puckerIndex, baseUrl) { +function getExternalChangeInfo(user, date) { + return {user_id: user.id, user_id_original: user.idOriginal, user_name: user.username, change_date: date}; +} +let resetForceSaveAfterChanges = co.wrap(function*(docId, newChangesLastTime, puckerIndex, baseUrl, changeInfo) { //last save if (newChangesLastTime) { - yield editorData.setForceSave(docId, newChangesLastTime, puckerIndex, baseUrl); + yield editorData.setForceSave(docId, newChangesLastTime, puckerIndex, baseUrl, changeInfo); if (cfgForceSaveEnable) { let expireAt = newChangesLastTime + cfgForceSaveInterval; yield editorData.addForceSaveTimerNX(docId, expireAt); @@ -1026,6 +1030,7 @@ function* cleanDocumentOnExit(docId, deleteChanges) { yield editorData.cleanDocumentOnExit(docId); //remove changes if (deleteChanges) { + yield taskResult.restoreInitialPassword(docId); sqlBase.deleteChanges(docId, null); //delete forgotten after successful send on callbackUrl yield storage.deletePath(cfgForgottenFiles + '/' + docId); @@ -1208,6 +1213,7 @@ exports.cleanDocumentOnExitNoChangesPromise = co.wrap(cleanDocumentOnExitNoChang exports.setForceSave = setForceSave; exports.startForceSave = startForceSave; exports.resetForceSaveAfterChanges = resetForceSaveAfterChanges; +exports.getExternalChangeInfo = getExternalChangeInfo; exports.checkJwt = checkJwt; exports.getRequestParams = getRequestParams; exports.checkJwtHeader = checkJwtHeader; @@ -2561,7 +2567,8 @@ exports.install = function(server, callbackFunction) { // Автоматически снимаем lock сами и посылаем индекс для сохранения yield* unSaveLock(conn, changesIndex, newChangesLastTime); //last save - yield resetForceSaveAfterChanges(docId, newChangesLastTime, puckerIndex, utils.getBaseUrlByConnection(conn)); + let changeInfo = getExternalChangeInfo(conn.user, newChangesLastTime); + yield resetForceSaveAfterChanges(docId, newChangesLastTime, puckerIndex, utils.getBaseUrlByConnection(conn), changeInfo); } else { let changesToSend = arrNewDocumentChanges; if(changesToSend.length > cfgPubSubMaxChanges) { diff --git a/DocService/sources/baseConnector.js b/DocService/sources/baseConnector.js index 967a7925a..eafc0b6e6 100644 --- a/DocService/sources/baseConnector.js +++ b/DocService/sources/baseConnector.js @@ -317,22 +317,18 @@ exports.UserCallback = UserCallback; function DocumentPassword() { this.password = undefined; - this.userId = undefined; - this.userIndex = undefined; + this.change = undefined; } DocumentPassword.prototype.fromString = function(passwordStr){ var parsed = JSON.parse(passwordStr); - this.fromValues(parsed.password, parsed.userId, parsed.userIndex); + this.fromValues(parsed.password, parsed.change); }; -DocumentPassword.prototype.fromValues = function(password, userId, userIndex){ +DocumentPassword.prototype.fromValues = function(password, change){ if(null !== password){ this.password = password; } - if(null !== userId){ - this.userId = userId; - } - if(null !== userIndex){ - this.userIndex = userIndex; + if(null !== change) { + this.change = change; } }; DocumentPassword.prototype.delimiter = String.fromCharCode(5); @@ -340,10 +336,10 @@ DocumentPassword.prototype.toSQLInsert = function(){ return this.delimiter + JSON.stringify(this); }; DocumentPassword.prototype.isInitial = function(){ - return !this.userId; + return !this.change; }; DocumentPassword.prototype.getDocPassword = function(docId, docPasswordStr) { - let res = {initial: undefined, current: undefined, userId: undefined, userIndex: undefined}; + let res = {initial: undefined, current: undefined, change: undefined}; if (docPasswordStr) { logger.debug("getDocPassword: docId = %s passwords = %s", docId, docPasswordStr); let passwords = docPasswordStr.split(UserCallback.prototype.delimiter); @@ -353,10 +349,10 @@ DocumentPassword.prototype.getDocPassword = function(docId, docPasswordStr) { password.fromString(passwords[i]); if (password.isInitial()) { res.initial = password.password; + } else { + res.change = password.change; } res.current = password.password; - res.userId = password.userId; - res.userIndex = password.userIndex; } } return res; diff --git a/DocService/sources/canvasservice.js b/DocService/sources/canvasservice.js index 8ae797243..9fd5f6afd 100644 --- a/DocService/sources/canvasservice.js +++ b/DocService/sources/canvasservice.js @@ -505,9 +505,8 @@ function* commandSfctByCmd(cmd, opt_priority, opt_expiration, opt_queue) { let docPassword = row && sqlBase.DocumentPassword.prototype.getDocPassword(cmd.getDocId(), row.password); if (docPassword.current) { cmd.setSavePassword(docPassword.current); - if (docPassword.userId) { - cmd.setUserId(docPassword.userId); - cmd.setUserIndex(docPassword.userIndex); + if (docPassword.change) { + cmd.setExternalChangeInfo(docPassword.change); } } var queueData = getSaveTask(cmd); @@ -714,12 +713,15 @@ function* commandSetPassword(conn, cmd, outputData) { updateMask.key = cmd.getDocId(); updateMask.status = taskResult.FileStatus.Ok; + let newChangesLastDate = new Date(); + newChangesLastDate.setMilliseconds(0);//remove milliseconds avoid issues with MySQL datetime rounding + var task = new taskResult.TaskResultData(); task.key = cmd.getDocId(); task.password = cmd.getPassword() || ""; + let changeInfo = null; if (conn.user) { - task.innerUserId = conn.user.idOriginal; - task.innerUserIndex = utils.getIndexFromUserId(conn.user.id, conn.user.idOriginal); + changeInfo = task.innerPasswordChange = docsCoServer.getExternalChangeInfo(conn.user, newChangesLastDate.getTime()); } var upsertRes = yield taskResult.updateIf(task, updateMask); @@ -728,9 +730,7 @@ function* commandSetPassword(conn, cmd, outputData) { if (!conn.isEnterCorrectPassword) { docsCoServer.modifyConnectionForPassword(conn, true); } - let newChangesLastDate = new Date(); - newChangesLastDate.setMilliseconds(0);//remove milliseconds avoid issues with MySQL datetime rounding - yield docsCoServer.resetForceSaveAfterChanges(cmd.getDocId(), newChangesLastDate.getTime(), 0x7FFFFFFF, utils.getBaseUrlByConnection(conn)); + yield docsCoServer.resetForceSaveAfterChanges(cmd.getDocId(), newChangesLastDate.getTime(), 0, utils.getBaseUrlByConnection(conn), changeInfo); } else { logger.debug('commandSetPassword sql update error: docId = %s', cmd.getDocId()); outputData.setStatus('err'); @@ -1305,9 +1305,8 @@ exports.saveFromChanges = function(docId, statusInfo, optFormat, opt_userId, opt let docPassword = row && sqlBase.DocumentPassword.prototype.getDocPassword(cmd.getDocId(), row.password); if (docPassword.current) { cmd.setSavePassword(docPassword.current); - if (docPassword.userId) { - cmd.setUserId(docPassword.userId); - cmd.setUserIndex(docPassword.userIndex); + if (docPassword.change) { + cmd.setExternalChangeInfo(docPassword.change); } } yield* addRandomKeyTaskCmd(cmd); diff --git a/DocService/sources/converterservice.js b/DocService/sources/converterservice.js index fef043a24..a539d1676 100644 --- a/DocService/sources/converterservice.js +++ b/DocService/sources/converterservice.js @@ -148,8 +148,8 @@ function* convertByCmd(cmd, async, baseUrl, opt_fileTo, opt_taskExist, opt_prior return status; } -function* convertFromChanges(docId, baseUrl, forceSave, opt_userdata, opt_userConnectionId, opt_responseKey, opt_priority, - opt_expiration, opt_queue, opt_redisKey) { +function* convertFromChanges(docId, baseUrl, forceSave, externalChangeInfo, opt_userdata, opt_userConnectionId, + opt_responseKey, opt_priority, opt_expiration, opt_queue, opt_redisKey) { var cmd = new commonDefines.InputCommand(); cmd.setCommand('sfcm'); cmd.setDocId(docId); @@ -158,6 +158,7 @@ function* convertFromChanges(docId, baseUrl, forceSave, opt_userdata, opt_userCo cmd.setCodepage(commonDefines.c_oAscCodePageUtf8); cmd.setDelimiter(commonDefines.c_oAscCsvDelimiter.Comma); cmd.setForceSave(forceSave); + cmd.setExternalChangeInfo(externalChangeInfo); if (opt_userdata) { cmd.setUserData(opt_userdata); } diff --git a/DocService/sources/editorDataMemory.js b/DocService/sources/editorDataMemory.js index 22b560339..6bd29434e 100644 --- a/DocService/sources/editorDataMemory.js +++ b/DocService/sources/editorDataMemory.js @@ -162,9 +162,9 @@ EditorData.prototype.getdelSaved = function(docId) { data.saved = undefined; return Promise.resolve(res); }; -EditorData.prototype.setForceSave = function(docId, time, index, baseUrl) { +EditorData.prototype.setForceSave = function(docId, time, index, baseUrl, changeInfo) { let data = this._getDocumentData(docId); - data.forceSave = {time: time, index: index, baseUrl: baseUrl, started: false, ended: false}; + data.forceSave = {time: time, index: index, baseUrl: baseUrl, changeInfo: changeInfo, started: false, ended: false}; return Promise.resolve(); }; EditorData.prototype.getForceSave = function(docId) { diff --git a/DocService/sources/taskresult.js b/DocService/sources/taskresult.js index 7146c45dc..708da59a1 100644 --- a/DocService/sources/taskresult.js +++ b/DocService/sources/taskresult.js @@ -67,8 +67,7 @@ function TaskResultData() { this.baseurl = null; this.password = null; - this.innerUserId = null;//not a DB field - this.innerUserIndex = null;//not a DB field + this.innerPasswordChange = null;//not a DB field } TaskResultData.prototype.completeDefaults = function() { if (!this.key) { @@ -118,7 +117,7 @@ function select(docId) { }, undefined, undefined, values); }); } -function toUpdateArray(task, updateTime, isMask, values) { +function toUpdateArray(task, updateTime, isMask, values, setPassword) { var res = []; if (null != task.status) { let sqlParam = addSqlParam(task.status, values); @@ -154,19 +153,22 @@ function toUpdateArray(task, updateTime, isMask, values) { let sqlParam = addSqlParam(task.baseurl, values); res.push(`baseurl=${sqlParam}`); } - if (null != task.password) { + if (setPassword) { + let sqlParam = addSqlParam(task.password, values); + res.push(`password=${sqlParam}`); + } else if (null != task.password || setPassword) { var documentPassword = new sqlBase.DocumentPassword(); - documentPassword.fromValues(task.password, task.innerUserId, task.innerUserIndex); + documentPassword.fromValues(task.password, task.innerPasswordChange); let sqlParam = addSqlParam(documentPassword.toSQLInsert(), values); res.push(`password=${concatParams('password', sqlParam)}`); } return res; } -function update(task) { +function update(task, setPassword) { return new Promise(function(resolve, reject) { let values = []; - let updateElems = toUpdateArray(task, true, false, values); + let updateElems = toUpdateArray(task, true, false, values, setPassword); let sqlSet = updateElems.join(', '); let sqlParam = addSqlParam(task.key, values); let sqlCommand = `UPDATE task_result SET ${sqlSet} WHERE id=${sqlParam};`; @@ -183,8 +185,8 @@ function update(task) { function updateIf(task, mask) { return new Promise(function(resolve, reject) { let values = []; - let commandArg = toUpdateArray(task, true, false, values); - let commandArgMask = toUpdateArray(mask, false, true, values); + let commandArg = toUpdateArray(task, true, false, values, false); + let commandArgMask = toUpdateArray(mask, false, true, values, false); commandArgMask.push('id=' + addSqlParam(mask.key, values)); let sqlSet = commandArg.join(', '); let sqlWhere = commandArgMask.join(' AND '); @@ -198,6 +200,25 @@ function updateIf(task, mask) { }, undefined, undefined, values); }); } +function restoreInitialPassword(docId) { + return select(docId).then(function(selectRes) { + if (selectRes.length > 0) { + var row = selectRes[0]; + let docPassword = sqlBase.DocumentPassword.prototype.getDocPassword(docId, row.password); + var updateTask = new TaskResultData(); + updateTask.key = docId; + if (docPassword.initial) { + var documentPassword = new sqlBase.DocumentPassword(); + documentPassword.fromValues(docPassword.initial); + updateTask.password = documentPassword.toSQLInsert(); + return update(updateTask, true); + } else if (docPassword.current) { + updateTask.password = null; + return update(updateTask, true); + } + } + }); +} function addRandomKey(task, opt_prefix, opt_size) { return new Promise(function(resolve, reject) { @@ -291,6 +312,7 @@ exports.upsert = upsert; exports.select = select; exports.update = update; exports.updateIf = updateIf; +exports.restoreInitialPassword = restoreInitialPassword; exports.addRandomKeyTask = addRandomKeyTask; exports.remove = remove; exports.getExpired = getExpired; diff --git a/FileConverter/sources/converter.js b/FileConverter/sources/converter.js index ca71c5cad..8ac3df229 100644 --- a/FileConverter/sources/converter.js +++ b/FileConverter/sources/converter.js @@ -407,11 +407,28 @@ function* processChanges(tempDirs, cmd, authorProps) { forceSaveTime = forceSave.getTime(); forceSaveIndex = forceSave.getIndex(); } + let extChangeInfo = cmd.getExternalChangeInfo(); + let extChanges; + if (extChangeInfo) { + extChanges = [{ + id: cmd.getDocId(), change_id: 0, change_data: "", user_id: extChangeInfo.user_id, + user_id_original: extChangeInfo.user_id_original, user_name: extChangeInfo.user_name, + change_date: new Date(extChangeInfo.change_date) + }]; + } + let streamObj = yield* streamCreate(cmd.getDocId(), changesDir, indexFile++, {highWaterMark: cfgStreamWriterBufferSize}); let curIndexStart = 0; let curIndexEnd = Math.min(curIndexStart + cfgMaxRequestChanges, forceSaveIndex); - while (curIndexStart < curIndexEnd) { - let changes = yield baseConnector.getChangesPromise(cmd.getDocId(), curIndexStart, curIndexEnd, forceSaveTime); + while (curIndexStart < curIndexEnd || extChanges) { + let changes = []; + if (curIndexStart < curIndexEnd) { + changes = yield baseConnector.getChangesPromise(cmd.getDocId(), curIndexStart, curIndexEnd, forceSaveTime); + } + if (0 === changes.length && extChanges) { + changes = extChanges; + } + extChanges = undefined; for (let i = 0; i < changes.length; ++i) { let change = changes[i]; if (change.change_data.startsWith('ENCRYPTED;')) { @@ -457,10 +474,8 @@ function* processChanges(tempDirs, cmd, authorProps) { changesAuthor = forceSave.getAuthorUserId(); changesIndex = forceSave.getAuthorUserIndex(); } - if (null != changesAuthor && null != changesIndex) { - cmd.setUserId(changesAuthor); - cmd.setUserIndex(changesIndex); - } + cmd.setUserId(changesAuthor); + cmd.setUserIndex(changesIndex); fs.writeFileSync(path.join(tempDirs.result, 'changesHistory.json'), JSON.stringify(changesHistory), 'utf8'); return res; }