Fixed create folder due to new system (write on server is only performed upon unlock)

added test case for remove file

Signed-off-by: tobiasKaminsky <tobias@kaminsky.me>
This commit is contained in:
tobiasKaminsky 2020-07-16 10:49:26 +02:00
parent 2dfde203c8
commit 121c6c1f23
No known key found for this signature in database
GPG key ID: 0E00D4D47D0C5AF7
9 changed files with 237 additions and 39 deletions

View file

@ -32,10 +32,13 @@ import com.owncloud.android.lib.common.accounts.AccountUtils;
import com.owncloud.android.lib.common.operations.RemoteOperationResult;
import com.owncloud.android.lib.common.utils.Log_OC;
import com.owncloud.android.lib.resources.e2ee.ToggleEncryptionRemoteOperation;
import com.owncloud.android.lib.resources.status.OCCapability;
import com.owncloud.android.lib.resources.status.OwnCloudVersion;
import com.owncloud.android.lib.resources.users.GetPrivateKeyOperation;
import com.owncloud.android.lib.resources.users.GetPublicKeyOperation;
import com.owncloud.android.lib.resources.users.SendCSROperation;
import com.owncloud.android.lib.resources.users.StorePrivateKeyOperation;
import com.owncloud.android.operations.GetCapabilitiesOperation;
import com.owncloud.android.operations.RemoveFileOperation;
import com.owncloud.android.utils.CsrHelper;
import com.owncloud.android.utils.EncryptionUtils;
@ -52,9 +55,11 @@ import java.io.IOException;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import static com.owncloud.android.lib.resources.status.OwnCloudVersion.nextcloud_19;
import static junit.framework.TestCase.assertEquals;
import static junit.framework.TestCase.assertNotNull;
import static junit.framework.TestCase.assertTrue;
import static org.junit.Assume.assumeTrue;
@ -80,6 +85,12 @@ public class EndToEndRandomIT extends AbstractOnServerIT {
@Before
public void before() {
OCCapability capability = getStorageManager().getCapability(account.name);
if (capability.getVersion().equals(new OwnCloudVersion("0.0.0"))) {
// fetch new one
assertTrue(new GetCapabilitiesOperation().execute(client, getStorageManager()).isSuccess());
}
// tests only for NC19+
assumeTrue(getStorageManager()
.getCapability(account.name)
@ -282,6 +293,7 @@ public class EndToEndRandomIT extends AbstractOnServerIT {
}
OCFile fileToDelete = files.get(new Random().nextInt(files.size()));
assertNotNull(fileToDelete.getRemoteId());
Log_OC.d(this,
"[" + i + "/" + actionCount + "] " +
@ -395,4 +407,58 @@ public class EndToEndRandomIT extends AbstractOnServerIT {
private String generateMnemonicString() {
return "1 2 3 4 5 6";
}
public void after() {
// remove all encrypted files
OCFile root = fileDataStorageManager.getFileByDecryptedRemotePath("/");
removeFolder(root);
// List<OCFile> files = fileDataStorageManager.getFolderContent(root, false);
//
// for (OCFile child : files) {
// removeFolder(child);
// }
assertEquals(0, fileDataStorageManager.getFolderContent(root, false).size());
super.after();
}
private void removeFolder(OCFile folder) {
Log_OC.d(this, "Start removing content of folder: " + folder.getDecryptedRemotePath());
List<OCFile> children = fileDataStorageManager.getFolderContent(folder, false);
// remove children
for (OCFile child : children) {
if (child.isFolder()) {
removeFolder(child);
// remove folder
Log_OC.d(this, "Remove folder: " + child.getDecryptedRemotePath());
if (!folder.isEncrypted() && child.isEncrypted()) {
assertTrue(new ToggleEncryptionRemoteOperation(child.getLocalId(),
child.getRemotePath(),
false)
.execute(client)
.isSuccess());
OCFile f = getStorageManager().getFileByEncryptedRemotePath(child.getRemotePath());
f.setEncrypted(false);
getStorageManager().saveFile(f);
child.setEncrypted(false);
}
} else {
Log_OC.d(this, "Remove file: " + child.getDecryptedRemotePath());
}
assertTrue(new RemoveFileOperation(child, false, account, false, targetContext)
.execute(client, getStorageManager())
.isSuccess()
);
}
Log_OC.d(this, "Finished removing content of folder: " + folder.getDecryptedRemotePath());
}
}

View file

@ -9,7 +9,6 @@ import android.content.ActivityNotFoundException;
import android.content.Context;
import com.facebook.testing.screenshot.Screenshot;
import com.nextcloud.client.RetryTestRule;
import com.nextcloud.client.account.User;
import com.nextcloud.client.account.UserAccountManager;
import com.nextcloud.client.account.UserAccountManagerImpl;
@ -35,7 +34,6 @@ import junit.framework.TestCase;
import org.apache.commons.io.FileUtils;
import org.junit.BeforeClass;
import org.junit.Rule;
import java.io.File;
import java.io.FileWriter;
@ -61,7 +59,7 @@ import static org.junit.Assert.assertTrue;
*/
public abstract class AbstractIT {
@Rule public RetryTestRule retryTestRule = new RetryTestRule();
//@Rule public RetryTestRule retryTestRule = new RetryTestRule();
protected static OwnCloudClient client;
protected static Account account;

View file

@ -16,6 +16,7 @@ import com.nextcloud.client.device.PowerManagementService;
import com.nextcloud.client.network.Connectivity;
import com.nextcloud.client.network.ConnectivityService;
import com.nextcloud.java.util.Optional;
import com.owncloud.android.datamodel.OCFile;
import com.owncloud.android.datamodel.UploadsStorageManager;
import com.owncloud.android.db.OCUpload;
import com.owncloud.android.files.services.FileUploader;
@ -43,6 +44,7 @@ import java.io.IOException;
import androidx.annotation.NonNull;
import androidx.test.platform.app.InstrumentationRegistry;
import static junit.framework.TestCase.assertEquals;
import static org.junit.Assert.assertTrue;
@ -111,6 +113,9 @@ public abstract class AbstractOnServerIT extends AbstractIT {
@After
public void after() {
deleteAllFiles();
OCFile root = fileDataStorageManager.getFileByDecryptedRemotePath("/");
assertEquals(0, fileDataStorageManager.getFolderContent(root, false).size());
}
public static void deleteAllFiles() {
@ -236,5 +241,10 @@ public abstract class AbstractOnServerIT extends AbstractIT {
RemoteOperationResult result = newUpload.execute(client, getStorageManager());
assertTrue(result.getLogMessage(), result.isSuccess());
//
// shortSleep();
// shortSleep();
//
// assertNotNull(getStorageManager().getFileByDecryptedRemotePath(ocUpload.getRemotePath()).getRemoteId());
}
}

View file

@ -26,5 +26,7 @@ public class FileDataStorageManagerContentResolverTest extends FileDataStorageMa
@Override
public void before() {
sut = new FileDataStorageManager(account, targetContext.getContentResolver());
super.before();
}
}

View file

@ -55,7 +55,13 @@ abstract public class FileDataStorageManagerTest extends AbstractOnServerIT {
protected FileDataStorageManager sut;
@Before
abstract public void before();
public void before() {
// make sure everything is removed
sut.deleteAllFiles();
sut.deleteVirtuals(VirtualFolderType.PHOTOS);
assertEquals(0, sut.getAllFiles().size());
}
@After
public void after() {

View file

@ -0,0 +1,98 @@
/*
*
* Nextcloud Android client application
*
* @author Tobias Kaminsky
* Copyright (C) 2020 Tobias Kaminsky
* Copyright (C) 2020 Nextcloud GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.owncloud.android.operations;
import com.owncloud.android.AbstractOnServerIT;
import com.owncloud.android.datamodel.OCFile;
import com.owncloud.android.db.OCUpload;
import com.owncloud.android.utils.FileStorageUtils;
import org.junit.Test;
import static junit.framework.TestCase.assertNotNull;
import static junit.framework.TestCase.assertTrue;
public class RemoveFileOperationIT extends AbstractOnServerIT {
@Test
public void deleteFolder() {
String parent = "/test/";
String path = parent + "folder1/";
assertTrue(new CreateFolderOperation(path, user, targetContext).execute(client, getStorageManager())
.isSuccess());
OCFile folder = getStorageManager().getFileByPath(path);
assertNotNull(folder);
assertTrue(new RemoveFileOperation(folder,
false,
account,
false,
targetContext)
.execute(client, getStorageManager())
.isSuccess());
OCFile parentFolder = getStorageManager().getFileByPath(parent);
assertNotNull(parentFolder);
assertTrue(new RemoveFileOperation(parentFolder,
false,
account,
false,
targetContext)
.execute(client, getStorageManager())
.isSuccess());
}
@Test
public void deleteFile() {
String parent = "/test/";
String path = parent + "empty.txt";
OCUpload ocUpload = new OCUpload(FileStorageUtils.getSavePath(account.name) + "/empty.txt", path, account.name);
uploadOCUpload(ocUpload);
OCFile file = getStorageManager().getFileByPath(path);
assertNotNull(file);
assertTrue(new RemoveFileOperation(file,
false,
account,
false,
targetContext)
.execute(client, getStorageManager())
.isSuccess());
OCFile parentFolder = getStorageManager().getFileByPath(parent);
assertNotNull(parentFolder);
assertTrue(new RemoveFileOperation(parentFolder,
false,
account,
false,
targetContext)
.execute(client, getStorageManager())
.isSuccess());
}
}

View file

@ -21,7 +21,6 @@
package com.owncloud.android.operations;
import android.accounts.Account;
import android.content.Context;
import android.os.Build;
import android.util.Pair;
@ -159,31 +158,6 @@ public class CreateFolderOperation extends SyncOperation implements OnRemoteOper
.execute(client);
if (result.isSuccess()) {
RemoteOperationResult remoteFolderOperationResult = new ReadFolderRemoteOperation(encryptedRemotePath)
.execute(client);
createdRemoteFolder = (RemoteFile) remoteFolderOperationResult.getData().get(0);
OCFile newDir = new OCFile(createdRemoteFolder.getRemotePath());
newDir.setMimeType(MimeType.DIRECTORY);
newDir.setParentId(parent.getFileId());
newDir.setRemoteId(createdRemoteFolder.getRemoteId());
newDir.setModificationTimestamp(System.currentTimeMillis());
newDir.setEncrypted(true);
newDir.setPermissions(createdRemoteFolder.getPermissions());
newDir.setDecryptedRemotePath(parent.getDecryptedRemotePath() + filename + "/");
getStorageManager().saveFile(newDir);
RemoteOperationResult encryptionOperationResult = new ToggleEncryptionRemoteOperation(
newDir.getLocalId(),
newDir.getRemotePath(),
true)
.execute(client);
if (!encryptionOperationResult.isSuccess()) {
throw new RuntimeException("Error creating encrypted subfolder!");
}
// Key, always generate new one
byte[] key = EncryptionUtils.generateKey();
@ -212,6 +186,43 @@ public class CreateFolderOperation extends SyncOperation implements OnRemoteOper
token,
client,
metadataExists);
// unlock folder
if (token != null) {
RemoteOperationResult unlockFolderResult = EncryptionUtils.unlockFolder(parent, client, token);
if (unlockFolderResult.isSuccess()) {
token = null;
} else {
// TODO do better
throw new RuntimeException("Could not unlock folder!");
}
}
RemoteOperationResult remoteFolderOperationResult = new ReadFolderRemoteOperation(encryptedRemotePath)
.execute(client);
createdRemoteFolder = (RemoteFile) remoteFolderOperationResult.getData().get(0);
OCFile newDir = new OCFile(createdRemoteFolder.getRemotePath());
newDir.setMimeType(MimeType.DIRECTORY);
newDir.setParentId(parent.getFileId());
newDir.setRemoteId(createdRemoteFolder.getRemoteId());
newDir.setModificationTimestamp(System.currentTimeMillis());
newDir.setEncrypted(true);
newDir.setPermissions(createdRemoteFolder.getPermissions());
newDir.setDecryptedRemotePath(parent.getDecryptedRemotePath() + filename + "/");
getStorageManager().saveFile(newDir);
RemoteOperationResult encryptionOperationResult = new ToggleEncryptionRemoteOperation(
newDir.getLocalId(),
newDir.getRemotePath(),
true)
.execute(client);
if (!encryptionOperationResult.isSuccess()) {
throw new RuntimeException("Error creating encrypted subfolder!");
}
} else {
// revert to sane state in case of any error
Log_OC.e(TAG, remotePath + " hasn't been created");
@ -249,7 +260,6 @@ public class CreateFolderOperation extends SyncOperation implements OnRemoteOper
// TODO do better
return new RemoteOperationResult(e);
} finally {
// unlock folder
if (token != null) {
RemoteOperationResult unlockFolderResult = EncryptionUtils.unlockFolder(parent, client, token);

View file

@ -106,7 +106,7 @@ public class RemoveFileOperation extends SyncOperation {
context,
fileToRemove.getEncryptedFileName());
} else {
operation = new RemoveFileRemoteOperation(fileToRemove.getDecryptedRemotePath());
operation = new RemoveFileRemoteOperation(fileToRemove.getRemotePath());
}
result = operation.execute(client);
if (result.isSuccess() || result.getCode() == ResultCode.FILE_NOT_FOUND) {

View file

@ -604,7 +604,7 @@ public class UploadFileOperation extends SyncOperation {
metadata.getFiles().put(encryptedFileName, decryptedFile);
EncryptedFolderMetadata encryptedFolderMetadata = EncryptionUtils.encryptFolderMetadata(metadata,
privateKey);
privateKey);
String serializedFolderMetadata = EncryptionUtils.serializeJSON(encryptedFolderMetadata);
// upload metadata
@ -613,6 +613,13 @@ public class UploadFileOperation extends SyncOperation {
token,
client,
metadataExists);
// unlock
result = EncryptionUtils.unlockFolder(parentFile, client, token);
if (result.isSuccess()) {
token = null;
}
}
} catch (FileNotFoundException e) {
Log_OC.d(TAG, mFile.getStoragePath() + " not exists anymore");
@ -650,13 +657,14 @@ public class UploadFileOperation extends SyncOperation {
}
// unlock must be done always
// TODO check if in good state
RemoteOperationResult unlockFolderResult = EncryptionUtils.unlockFolder(parentFile,
client,
token);
if (token != null) {
RemoteOperationResult unlockFolderResult = EncryptionUtils.unlockFolder(parentFile,
client,
token);
if (!unlockFolderResult.isSuccess()) {
return unlockFolderResult;
if (!unlockFolderResult.isSuccess()) {
return unlockFolderResult;
}
}
// delete temporal file