summaryrefslogtreecommitdiff
path: root/sw
diff options
context:
space:
mode:
authorTünde Tóth <toth.tunde@nisz.hu>2021-10-12 15:11:48 +0200
committerLászló Németh <nemeth@numbertext.org>2021-10-26 11:59:19 +0200
commit1b53c1dfc76f08ca7df40a0673aa50eca700d072 (patch)
treea1b4d8b32e9a7ff7e0e95670a6358807349f6270 /sw
parent31ded6f8718a90c7b3b9eb36909c2b7a0c6e9b68 (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.py63
-rw-r--r--sw/source/filter/ww8/docxexport.cxx61
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();