testpermissions.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  1. /*
  2. * This software is in the public domain, furnished "as is", without technical
  3. * support, and with no warranty, express or implied, as to its usefulness for
  4. * any purpose.
  5. *
  6. */
  7. #include <QtTest>
  8. #include "syncenginetestutils.h"
  9. #include <syncengine.h>
  10. #include "common/ownsql.h"
  11. using namespace OCC;
  12. static void applyPermissionsFromName(FileInfo &info) {
  13. static QRegularExpression rx("_PERM_([^_]*)_[^/]*$");
  14. auto m = rx.match(info.name);
  15. if (m.hasMatch()) {
  16. info.permissions = RemotePermissions::fromServerString(m.captured(1));
  17. }
  18. for (FileInfo &sub : info.children)
  19. applyPermissionsFromName(sub);
  20. }
  21. // Check if the expected rows in the DB are non-empty. Note that in some cases they might be, then we cannot use this function
  22. // https://github.com/owncloud/client/issues/2038
  23. static void assertCsyncJournalOk(SyncJournalDb &journal)
  24. {
  25. SqlDatabase db;
  26. QVERIFY(db.openReadOnly(journal.databaseFilePath()));
  27. SqlQuery q("SELECT count(*) from metadata where length(fileId) == 0", db);
  28. QVERIFY(q.exec());
  29. QVERIFY(q.next().hasData);
  30. QCOMPARE(q.intValue(0), 0);
  31. #if defined(Q_OS_WIN) // Make sure the file does not appear in the FileInfo
  32. FileSystem::setFileHidden(journal.databaseFilePath() + "-shm", true);
  33. #endif
  34. }
  35. class TestPermissions : public QObject
  36. {
  37. Q_OBJECT
  38. private slots:
  39. void t7pl()
  40. {
  41. FakeFolder fakeFolder{ FileInfo() };
  42. QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
  43. const int cannotBeModifiedSize = 133;
  44. const int canBeModifiedSize = 144;
  45. //create some files
  46. auto insertIn = [&](const QString &dir) {
  47. fakeFolder.remoteModifier().insert(dir + "normalFile_PERM_WVND_.data", 100 );
  48. fakeFolder.remoteModifier().insert(dir + "cannotBeRemoved_PERM_WVN_.data", 101 );
  49. fakeFolder.remoteModifier().insert(dir + "canBeRemoved_PERM_D_.data", 102 );
  50. fakeFolder.remoteModifier().insert(dir + "cannotBeModified_PERM_DVN_.data", cannotBeModifiedSize , 'A');
  51. fakeFolder.remoteModifier().insert(dir + "canBeModified_PERM_W_.data", canBeModifiedSize );
  52. };
  53. //put them in some directories
  54. fakeFolder.remoteModifier().mkdir("normalDirectory_PERM_CKDNV_");
  55. insertIn("normalDirectory_PERM_CKDNV_/");
  56. fakeFolder.remoteModifier().mkdir("readonlyDirectory_PERM_M_" );
  57. insertIn("readonlyDirectory_PERM_M_/" );
  58. fakeFolder.remoteModifier().mkdir("readonlyDirectory_PERM_M_/subdir_PERM_CK_");
  59. fakeFolder.remoteModifier().mkdir("readonlyDirectory_PERM_M_/subdir_PERM_CK_/subsubdir_PERM_CKDNV_");
  60. fakeFolder.remoteModifier().insert("readonlyDirectory_PERM_M_/subdir_PERM_CK_/subsubdir_PERM_CKDNV_/normalFile_PERM_WVND_.data", 100);
  61. applyPermissionsFromName(fakeFolder.remoteModifier());
  62. QVERIFY(fakeFolder.syncOnce());
  63. QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
  64. assertCsyncJournalOk(fakeFolder.syncJournal());
  65. qInfo("Do some changes and see how they propagate");
  66. //1. remove the file than cannot be removed
  67. // (they should be recovered)
  68. fakeFolder.localModifier().remove("normalDirectory_PERM_CKDNV_/cannotBeRemoved_PERM_WVN_.data");
  69. fakeFolder.localModifier().remove("readonlyDirectory_PERM_M_/cannotBeRemoved_PERM_WVN_.data");
  70. //2. remove the file that can be removed
  71. // (they should properly be gone)
  72. auto removeReadOnly = [&] (const QString &file) {
  73. QVERIFY(!QFileInfo(fakeFolder.localPath() + file).permission(QFile::WriteOwner));
  74. QFile(fakeFolder.localPath() + file).setPermissions(QFile::WriteOwner | QFile::ReadOwner);
  75. fakeFolder.localModifier().remove(file);
  76. };
  77. removeReadOnly("normalDirectory_PERM_CKDNV_/canBeRemoved_PERM_D_.data");
  78. removeReadOnly("readonlyDirectory_PERM_M_/canBeRemoved_PERM_D_.data");
  79. //3. Edit the files that cannot be modified
  80. // (they should be recovered, and a conflict shall be created)
  81. auto editReadOnly = [&] (const QString &file) {
  82. QVERIFY(!QFileInfo(fakeFolder.localPath() + file).permission(QFile::WriteOwner));
  83. QFile(fakeFolder.localPath() + file).setPermissions(QFile::WriteOwner | QFile::ReadOwner);
  84. fakeFolder.localModifier().appendByte(file);
  85. };
  86. editReadOnly("normalDirectory_PERM_CKDNV_/cannotBeModified_PERM_DVN_.data");
  87. editReadOnly("readonlyDirectory_PERM_M_/cannotBeModified_PERM_DVN_.data");
  88. //4. Edit other files
  89. // (they should be uploaded)
  90. fakeFolder.localModifier().appendByte("normalDirectory_PERM_CKDNV_/canBeModified_PERM_W_.data");
  91. fakeFolder.localModifier().appendByte("readonlyDirectory_PERM_M_/canBeModified_PERM_W_.data");
  92. //5. Create a new file in a read write folder
  93. // (should be uploaded)
  94. fakeFolder.localModifier().insert("normalDirectory_PERM_CKDNV_/newFile_PERM_WDNV_.data", 106 );
  95. applyPermissionsFromName(fakeFolder.remoteModifier());
  96. //do the sync
  97. QVERIFY(fakeFolder.syncOnce());
  98. assertCsyncJournalOk(fakeFolder.syncJournal());
  99. auto currentLocalState = fakeFolder.currentLocalState();
  100. //1.
  101. // File should be recovered
  102. QVERIFY(currentLocalState.find("normalDirectory_PERM_CKDNV_/cannotBeRemoved_PERM_WVN_.data"));
  103. QVERIFY(currentLocalState.find("readonlyDirectory_PERM_M_/cannotBeRemoved_PERM_WVN_.data"));
  104. //2.
  105. // File should be deleted
  106. QVERIFY(!currentLocalState.find("normalDirectory_PERM_CKDNV_/canBeRemoved_PERM_D_.data"));
  107. QVERIFY(!currentLocalState.find("readonlyDirectory_PERM_M_/canBeRemoved_PERM_D_.data"));
  108. //3.
  109. // File should be recovered
  110. QCOMPARE(currentLocalState.find("normalDirectory_PERM_CKDNV_/cannotBeModified_PERM_DVN_.data")->size, cannotBeModifiedSize);
  111. QCOMPARE(currentLocalState.find("readonlyDirectory_PERM_M_/cannotBeModified_PERM_DVN_.data")->size, cannotBeModifiedSize);
  112. // and conflict created
  113. auto c1 = findConflict(currentLocalState, "normalDirectory_PERM_CKDNV_/cannotBeModified_PERM_DVN_.data");
  114. QVERIFY(c1);
  115. QCOMPARE(c1->size, cannotBeModifiedSize + 1);
  116. auto c2 = findConflict(currentLocalState, "readonlyDirectory_PERM_M_/cannotBeModified_PERM_DVN_.data");
  117. QVERIFY(c2);
  118. QCOMPARE(c2->size, cannotBeModifiedSize + 1);
  119. // remove the conflicts for the next state comparison
  120. fakeFolder.localModifier().remove(c1->path());
  121. fakeFolder.localModifier().remove(c2->path());
  122. //4. File should be updated, that's tested by assertLocalAndRemoteDir
  123. QCOMPARE(currentLocalState.find("normalDirectory_PERM_CKDNV_/canBeModified_PERM_W_.data")->size, canBeModifiedSize + 1);
  124. QCOMPARE(currentLocalState.find("readonlyDirectory_PERM_M_/canBeModified_PERM_W_.data")->size, canBeModifiedSize + 1);
  125. //5.
  126. // the file should be in the server and local
  127. QVERIFY(currentLocalState.find("normalDirectory_PERM_CKDNV_/newFile_PERM_WDNV_.data"));
  128. // Both side should still be the same
  129. QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
  130. // Next test
  131. //6. Create a new file in a read only folder
  132. // (they should not be uploaded)
  133. fakeFolder.localModifier().insert("readonlyDirectory_PERM_M_/newFile_PERM_WDNV_.data", 105 );
  134. applyPermissionsFromName(fakeFolder.remoteModifier());
  135. // error: can't upload to readonly
  136. QVERIFY(!fakeFolder.syncOnce());
  137. assertCsyncJournalOk(fakeFolder.syncJournal());
  138. currentLocalState = fakeFolder.currentLocalState();
  139. //6.
  140. // The file should not exist on the remote, but still be there
  141. QVERIFY(currentLocalState.find("readonlyDirectory_PERM_M_/newFile_PERM_WDNV_.data"));
  142. QVERIFY(!fakeFolder.currentRemoteState().find("readonlyDirectory_PERM_M_/newFile_PERM_WDNV_.data"));
  143. // remove it so next test succeed.
  144. fakeFolder.localModifier().remove("readonlyDirectory_PERM_M_/newFile_PERM_WDNV_.data");
  145. // Both side should still be the same
  146. QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
  147. //######################################################################
  148. qInfo( "remove the read only directory" );
  149. // -> It must be recovered
  150. fakeFolder.localModifier().remove("readonlyDirectory_PERM_M_");
  151. applyPermissionsFromName(fakeFolder.remoteModifier());
  152. QVERIFY(fakeFolder.syncOnce());
  153. assertCsyncJournalOk(fakeFolder.syncJournal());
  154. currentLocalState = fakeFolder.currentLocalState();
  155. QVERIFY(currentLocalState.find("readonlyDirectory_PERM_M_/cannotBeRemoved_PERM_WVN_.data"));
  156. QVERIFY(currentLocalState.find("readonlyDirectory_PERM_M_/subdir_PERM_CK_/subsubdir_PERM_CKDNV_/normalFile_PERM_WVND_.data"));
  157. QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
  158. //######################################################################
  159. qInfo( "move a directory in a outside read only folder" );
  160. //Missing directory should be restored
  161. //new directory should be uploaded
  162. fakeFolder.localModifier().rename("readonlyDirectory_PERM_M_/subdir_PERM_CK_", "normalDirectory_PERM_CKDNV_/subdir_PERM_CKDNV_");
  163. applyPermissionsFromName(fakeFolder.remoteModifier());
  164. QVERIFY(fakeFolder.syncOnce());
  165. currentLocalState = fakeFolder.currentLocalState();
  166. // old name restored
  167. QVERIFY(currentLocalState.find("readonlyDirectory_PERM_M_/subdir_PERM_CK_/subsubdir_PERM_CKDNV_"));
  168. QVERIFY(currentLocalState.find("readonlyDirectory_PERM_M_/subdir_PERM_CK_/subsubdir_PERM_CKDNV_/normalFile_PERM_WVND_.data"));
  169. // new still exist (and is uploaded)
  170. QVERIFY(currentLocalState.find("normalDirectory_PERM_CKDNV_/subdir_PERM_CKDNV_/subsubdir_PERM_CKDNV_/normalFile_PERM_WVND_.data"));
  171. QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
  172. //######################################################################
  173. qInfo( "rename a directory in a read only folder and move a directory to a read-only" );
  174. // do a sync to update the database
  175. applyPermissionsFromName(fakeFolder.remoteModifier());
  176. QVERIFY(fakeFolder.syncOnce());
  177. assertCsyncJournalOk(fakeFolder.syncJournal());
  178. //1. rename a directory in a read only folder
  179. //Missing directory should be restored
  180. //new directory should stay but not be uploaded
  181. fakeFolder.localModifier().rename("readonlyDirectory_PERM_M_/subdir_PERM_CK_", "readonlyDirectory_PERM_M_/newname_PERM_CK_" );
  182. //2. move a directory from read to read only (move the directory from previous step)
  183. fakeFolder.localModifier().rename("normalDirectory_PERM_CKDNV_/subdir_PERM_CKDNV_", "readonlyDirectory_PERM_M_/moved_PERM_CK_" );
  184. // error: can't upload to readonly!
  185. QVERIFY(!fakeFolder.syncOnce());
  186. currentLocalState = fakeFolder.currentLocalState();
  187. //1.
  188. // old name restored
  189. QVERIFY(currentLocalState.find("readonlyDirectory_PERM_M_/subdir_PERM_CK_/subsubdir_PERM_CKDNV_/normalFile_PERM_WVND_.data" ));
  190. // new still exist
  191. QVERIFY(currentLocalState.find("readonlyDirectory_PERM_M_/newname_PERM_CK_/subsubdir_PERM_CKDNV_/normalFile_PERM_WVND_.data" ));
  192. // but is not on server: so remove it localy for the future comarison
  193. fakeFolder.localModifier().remove("readonlyDirectory_PERM_M_/newname_PERM_CK_");
  194. //2.
  195. // old removed
  196. QVERIFY(!currentLocalState.find("normalDirectory_PERM_CKDNV_/subdir_PERM_CKDNV_"));
  197. // new still there
  198. QVERIFY(currentLocalState.find("readonlyDirectory_PERM_M_/moved_PERM_CK_/subsubdir_PERM_CKDNV_/normalFile_PERM_WVND_.data" ));
  199. //but not on server
  200. fakeFolder.localModifier().remove("readonlyDirectory_PERM_M_/moved_PERM_CK_");
  201. QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
  202. //######################################################################
  203. qInfo( "multiple restores of a file create different conflict files" );
  204. editReadOnly("readonlyDirectory_PERM_M_/cannotBeModified_PERM_DVN_.data");
  205. fakeFolder.localModifier().setContents("readonlyDirectory_PERM_M_/cannotBeModified_PERM_DVN_.data", 's');
  206. //do the sync
  207. applyPermissionsFromName(fakeFolder.remoteModifier());
  208. QVERIFY(fakeFolder.syncOnce());
  209. assertCsyncJournalOk(fakeFolder.syncJournal());
  210. QThread::sleep(1); // make sure changes have different mtime
  211. editReadOnly("readonlyDirectory_PERM_M_/cannotBeModified_PERM_DVN_.data");
  212. fakeFolder.localModifier().setContents("readonlyDirectory_PERM_M_/cannotBeModified_PERM_DVN_.data", 'd');
  213. //do the sync
  214. applyPermissionsFromName(fakeFolder.remoteModifier());
  215. QVERIFY(fakeFolder.syncOnce());
  216. assertCsyncJournalOk(fakeFolder.syncJournal());
  217. // there should be two conflict files
  218. currentLocalState = fakeFolder.currentLocalState();
  219. int count = 0;
  220. while (auto i = findConflict(currentLocalState, "readonlyDirectory_PERM_M_/cannotBeModified_PERM_DVN_.data")) {
  221. QVERIFY((i->contentChar == 's') || (i->contentChar == 'd'));
  222. fakeFolder.localModifier().remove(i->path());
  223. currentLocalState = fakeFolder.currentLocalState();
  224. count++;
  225. }
  226. QCOMPARE(count, 2);
  227. QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
  228. }
  229. };
  230. QTEST_GUILESS_MAIN(TestPermissions)
  231. #include "testpermissions.moc"