diff options
author | Tünde Tóth <toth.tunde@nisz.hu> | 2021-10-12 15:11:48 +0200 |
---|---|---|
committer | László Németh <nemeth@numbertext.org> | 2021-10-26 11:59:19 +0200 |
commit | 1b53c1dfc76f08ca7df40a0673aa50eca700d072 (patch) | |
tree | a1b4d8b32e9a7ff7e0e95670a6358807349f6270 /sw | |
parent | 31ded6f8718a90c7b3b9eb36909c2b7a0c6e9b68 (diff) |
tdf#144374 DOCX: export the password for editing
The password for editing wasn't exported in DOCX,
resulting unprotected documents in Writer and
in MSO 2019 or newer.
Change-Id: Ieb8a339795635bc1c4223ccbe2a40ea85222cc2e
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/123543
Tested-by: László Németh <nemeth@numbertext.org>
Reviewed-by: László Németh <nemeth@numbertext.org>
Diffstat (limited to 'sw')
-rw-r--r-- | sw/qa/uitest/writer_tests6/tdf144374.py | 63 | ||||
-rw-r--r-- | sw/source/filter/ww8/docxexport.cxx | 61 |
2 files changed, 122 insertions, 2 deletions
diff --git a/sw/qa/uitest/writer_tests6/tdf144374.py b/sw/qa/uitest/writer_tests6/tdf144374.py new file mode 100644 index 000000000000..c484b79b54cd --- /dev/null +++ b/sw/qa/uitest/writer_tests6/tdf144374.py @@ -0,0 +1,63 @@ +# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*- +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +from uitest.framework import UITestCase +from libreoffice.uno.propertyvalue import mkPropertyValues +from org.libreoffice.unotest import systemPathToFileUrl +from uitest.uihelper.common import select_by_text +from tempfile import TemporaryDirectory +import os.path + +#Bug 144374 - Writer: FILESAVE to DOCX as read-only with additional password protection for editing not working + +class tdf144374(UITestCase): + + def test_tdf144374_DOCX(self): + with self.ui_test.create_doc_in_start_center("writer"): + with TemporaryDirectory() as tempdir: + xFilePath = os.path.join(tempdir, "tdf144374-tmp.docx") + + # Save the document + with self.ui_test.execute_dialog_through_command(".uno:Save", close_button="") as xSaveDialog: + xFileName = xSaveDialog.getChild("file_name") + xFileName.executeAction("TYPE", mkPropertyValues({"KEYCODE":"CTRL+A"})) + xFileName.executeAction("TYPE", mkPropertyValues({"KEYCODE":"BACKSPACE"})) + xFileName.executeAction("TYPE", mkPropertyValues({"TEXT": xFilePath})) + xFileTypeCombo = xSaveDialog.getChild("file_type") + select_by_text(xFileTypeCombo, "Office Open XML Text (Transitional) (.docx)") + xPasswordCheckButton = xSaveDialog.getChild("password") + xPasswordCheckButton.executeAction("CLICK", tuple()) + xOpen = xSaveDialog.getChild("open") + + with self.ui_test.execute_dialog_through_action(xOpen, "CLICK") as xPasswordDialog: + xReadonly = xPasswordDialog.getChild("readonly") + xReadonly.executeAction("CLICK", tuple()) + xNewPassword = xPasswordDialog.getChild("newpassroEntry") + xNewPassword.executeAction("TYPE", mkPropertyValues({"TEXT": "password"})) + xConfirmPassword = xPasswordDialog.getChild("confirmropassEntry") + xConfirmPassword.executeAction("TYPE", mkPropertyValues({"TEXT": "password"})) + + # DOCX confirmation dialog is displayed + xWarnDialog = self.xUITest.getTopFocusWindow() + xSave = xWarnDialog.getChild("save") + self.ui_test.close_dialog_through_button(xSave) + + self.ui_test.close_doc() + + with self.ui_test.load_file(systemPathToFileUrl(xFilePath)): + xWriterEdit = self.xUITest.getTopFocusWindow().getChild("writer_edit") + document = self.ui_test.get_component() + + self.assertTrue(document.isReadonly()) + + #Without the fix in place, this dialog wouldn't have been displayed + with self.ui_test.execute_dialog_through_action(xWriterEdit, "TYPE", mkPropertyValues({"KEYCODE": "CTRL+SHIFT+M"})) as xDialog: + xPassword = xDialog.getChild("newpassEntry") + xPassword.executeAction("TYPE", mkPropertyValues({"TEXT": "password"})) + + self.assertFalse(document.isReadonly()) + +# vim: set shiftwidth=4 softtabstop=4 expandtab: diff --git a/sw/source/filter/ww8/docxexport.cxx b/sw/source/filter/ww8/docxexport.cxx index b90fc8c74a02..a1ead25a3b7e 100644 --- a/sw/source/filter/ww8/docxexport.cxx +++ b/sw/source/filter/ww8/docxexport.cxx @@ -987,6 +987,8 @@ void DocxExport::WriteSettings() if( !pViewShell && !m_aSettings.hasData() && !m_pAttrOutput->HasFootnotes() && !m_pAttrOutput->HasEndnotes()) return; + SwDocShell* pDocShell = m_rDoc.GetDocShell(); + m_rFilter.addRelation( m_pDocumentFS->getOutputStream(), oox::getRelationship(Relationship::SETTINGS), u"settings.xml" ); @@ -998,6 +1000,61 @@ void DocxExport::WriteSettings() pFS->startElementNS( XML_w, XML_settings, FSNS( XML_xmlns, XML_w ), m_rFilter.getNamespaceURL(OOX_NS(doc)) ); + // Write protection + const uno::Sequence<beans::PropertyValue> aInfo = pDocShell->GetModifyPasswordInfo(); + if (aInfo.hasElements()) + { + OUString sAlgorithm, sSalt, sHash; + sal_Int32 nCount = 0; + for (const auto& prop : aInfo) + { + if (prop.Name == "algorithm-name") + prop.Value >>= sAlgorithm; + else if (prop.Name == "salt") + prop.Value >>= sSalt; + else if (prop.Name == "iteration-count") + prop.Value >>= nCount; + else if (prop.Name == "hash") + prop.Value >>= sHash; + } + if (!sAlgorithm.isEmpty() && !sSalt.isEmpty() && !sHash.isEmpty()) + { + sal_Int32 nAlgorithmSid = 0; + if (sAlgorithm == "MD2") + nAlgorithmSid = 1; + else if (sAlgorithm == "MD4") + nAlgorithmSid = 2; + else if (sAlgorithm == "MD5") + nAlgorithmSid = 3; + else if (sAlgorithm == "SHA-1") + nAlgorithmSid = 4; + else if (sAlgorithm == "MAC") + nAlgorithmSid = 5; + else if (sAlgorithm == "RIPEMD") + nAlgorithmSid = 6; + else if (sAlgorithm == "RIPEMD-160") + nAlgorithmSid = 7; + else if (sAlgorithm == "HMAC") + nAlgorithmSid = 9; + else if (sAlgorithm == "SHA-256") + nAlgorithmSid = 12; + else if (sAlgorithm == "SHA-384") + nAlgorithmSid = 13; + else if (sAlgorithm == "SHA-512") + nAlgorithmSid = 14; + + if (nAlgorithmSid != 0) + pFS->singleElementNS(XML_w, XML_writeProtection, + FSNS(XML_w, XML_cryptProviderType), "rsaAES", + FSNS(XML_w, XML_cryptAlgorithmClass), "hash", + FSNS(XML_w, XML_cryptAlgorithmType), "typeAny", + FSNS(XML_w, XML_cryptAlgorithmSid), OString::number(nAlgorithmSid).getStr(), + FSNS(XML_w, XML_cryptSpinCount), OString::number(nCount).getStr(), + FSNS(XML_w, XML_hash), sHash.toUtf8().getStr(), + FSNS(XML_w, XML_salt), sSalt.toUtf8().getStr()); + } + } + // View if (pViewShell && pViewShell->GetViewOptions()->getBrowseMode()) { @@ -1114,7 +1171,7 @@ void DocxExport::WriteSettings() DocxAttributeOutput::WriteFootnoteEndnotePr( pFS, XML_endnotePr, m_rDoc.GetEndNoteInfo(), XML_endnote ); // Has themeFontLang information - uno::Reference< beans::XPropertySet > xPropSet( m_rDoc.GetDocShell()->GetBaseModel(), uno::UNO_QUERY_THROW ); + uno::Reference< beans::XPropertySet > xPropSet( pDocShell->GetBaseModel(), uno::UNO_QUERY_THROW ); bool bUseGrabBagProtection = false; bool bWriterWantsToProtect = false; @@ -1274,7 +1331,7 @@ void DocxExport::WriteSettings() else if ( nToken == XML_edit && sValue == "readOnly" ) { // Ignore the case where read-only was not enforced, but now is. That is handled by _MarkAsFinal - bReadOnlyStatusUnchanged = m_rDoc.GetDocShell()->IsSecurityOptOpenReadOnly(); + bReadOnlyStatusUnchanged = pDocShell->IsSecurityOptOpenReadOnly(); } else if ( nToken == XML_enforcement ) bEnforced = sValue.toBoolean(); |