Skip to content

Commit

Permalink
E2e sharing
Browse files Browse the repository at this point in the history
Signed-off-by: tobiasKaminsky <[email protected]>

CsrHelper.java to library

Signed-off-by: tobiasKaminsky <[email protected]>

fix some spotbugs

Signed-off-by: tobiasKaminsky <[email protected]>

fix some spotbugs

Signed-off-by: tobiasKaminsky <[email protected]>

fix after rebase

Signed-off-by: tobiasKaminsky <[email protected]>

revert to master lib

Signed-off-by: tobiasKaminsky <[email protected]>
  • Loading branch information
tobiasKaminsky committed Jan 16, 2024
1 parent 2382361 commit 470825c
Show file tree
Hide file tree
Showing 71 changed files with 6,599 additions and 926 deletions.
3 changes: 3 additions & 0 deletions .idea/inspectionProfiles/ktlint.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@ dependencies {
implementation 'org.greenrobot:eventbus:3.3.1'
implementation 'com.googlecode.ez-vcard:ez-vcard:0.12.0'
implementation 'org.lukhnos:nnio:0.2'
implementation 'org.bouncycastle:bcpkix-jdk15to18:1.72'
implementation 'org.bouncycastle:bcpkix-jdk18on:1.75'
implementation 'com.google.code.gson:gson:2.10.1'
implementation 'com.github.nextcloud-deps:sectioned-recyclerview:0.6.1'
implementation 'com.github.chrisbanes:PhotoView:2.3.0'
Expand Down
1 change: 1 addition & 0 deletions app/lint.xml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
<issue id="TrustAllX509TrustManager">
<ignore path="**/bouncycastle/est/jcajce/*.class" />
<ignore path="**/bcpkix-jdk15to18-1.72.jar" />
<ignore path="**/bcpkix-jdk18on-1.75.jar" />
</issue>

<issue id="RestrictedApi" severity="error">
Expand Down
1,191 changes: 1,191 additions & 0 deletions app/schemas/com.nextcloud.client.database.NextcloudDatabase/77.json

Large diffs are not rendered by default.

32 changes: 32 additions & 0 deletions app/src/androidTest/java/com/nextcloud/client/EndToEndAction.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
*
* Nextcloud Android client application
*
* @author Tobias Kaminsky
* Copyright (C) 2023 Tobias Kaminsky
* Copyright (C) 2023 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.nextcloud.client;

public enum EndToEndAction {
CREATE_FOLDER,
GO_INTO_FOLDER,
GO_UP,
UPLOAD_FILE,
DOWNLOAD_FILE,
DELETE_FILE,
}
139 changes: 92 additions & 47 deletions app/src/androidTest/java/com/nextcloud/client/EndToEndRandomIT.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,34 +30,37 @@
import com.owncloud.android.datamodel.ArbitraryDataProvider;
import com.owncloud.android.datamodel.ArbitraryDataProviderImpl;
import com.owncloud.android.datamodel.OCFile;
import com.owncloud.android.datamodel.e2e.v2.encrypted.EncryptedFolderMetadataFile;
import com.owncloud.android.db.OCUpload;
import com.owncloud.android.files.services.FileUploader;
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.ocs.responses.PrivateKey;
import com.owncloud.android.lib.resources.e2ee.CsrHelper;
import com.owncloud.android.lib.resources.e2ee.ToggleEncryptionRemoteOperation;
import com.owncloud.android.lib.resources.files.ReadFileRemoteOperation;
import com.owncloud.android.lib.resources.shares.OCShare;
import com.owncloud.android.lib.resources.shares.ShareType;
import com.owncloud.android.lib.resources.status.OCCapability;
import com.owncloud.android.lib.resources.status.OwnCloudVersion;
import com.owncloud.android.lib.resources.users.DeletePrivateKeyOperation;
import com.owncloud.android.lib.resources.users.DeletePublicKeyOperation;
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.lib.resources.users.DeletePrivateKeyRemoteOperation;
import com.owncloud.android.lib.resources.users.DeletePublicKeyRemoteOperation;
import com.owncloud.android.lib.resources.users.GetPrivateKeyRemoteOperation;
import com.owncloud.android.lib.resources.users.GetPublicKeyRemoteOperation;
import com.owncloud.android.lib.resources.users.SendCSRRemoteOperation;
import com.owncloud.android.lib.resources.users.StorePrivateKeyRemoteOperation;
import com.owncloud.android.operations.CreateShareWithShareeOperation;
import com.owncloud.android.operations.DownloadFileOperation;
import com.owncloud.android.operations.GetCapabilitiesOperation;
import com.owncloud.android.operations.RemoveFileOperation;
import com.owncloud.android.utils.CsrHelper;
import com.owncloud.android.utils.EncryptionUtils;
import com.owncloud.android.utils.FileStorageUtils;

import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;

import java.io.File;
import java.io.IOException;
Expand All @@ -69,32 +72,19 @@
import java.util.List;
import java.util.Random;

import androidx.test.ext.junit.runners.AndroidJUnit4;

import static com.owncloud.android.lib.resources.status.OwnCloudVersion.nextcloud_19;
import static junit.framework.TestCase.assertEquals;
import static junit.framework.TestCase.assertFalse;
import static junit.framework.TestCase.assertNotNull;
import static junit.framework.TestCase.assertNull;
import static junit.framework.TestCase.assertTrue;
import static org.junit.Assume.assumeTrue;

@RunWith(AndroidJUnit4.class)
public class EndToEndRandomIT extends AbstractOnServerIT {
public enum Action {
CREATE_FOLDER,
GO_INTO_FOLDER,
GO_UP,
UPLOAD_FILE,
DOWNLOAD_FILE,
DELETE_FILE,
}

private static ArbitraryDataProvider arbitraryDataProvider;

private OCFile currentFolder;
private int actionCount = 20;
private final int actionCount = 20;
private String rootEncFolder = "/e/";

@Rule
public RetryTestRule retryTestRule = new RetryTestRule();

Expand Down Expand Up @@ -130,7 +120,7 @@ public void run() throws Exception {
init();

for (int i = 0; i < actionCount; i++) {
Action nextAction = Action.values()[new Random().nextInt(Action.values().length)];
EndToEndAction nextAction = EndToEndAction.values()[new Random().nextInt(EndToEndAction.values().length)];

switch (nextAction) {
case CREATE_FOLDER:
Expand Down Expand Up @@ -490,13 +480,13 @@ public void testCheckCSR() throws Exception {
// create CSR
AccountManager accountManager = AccountManager.get(targetContext);
String userId = accountManager.getUserData(account, AccountUtils.Constants.KEY_USER_ID);
String urlEncoded = CsrHelper.generateCsrPemEncodedString(keyPair, userId);
String urlEncoded = new CsrHelper().generateCsrPemEncodedString(keyPair, userId);

SendCSROperation operation = new SendCSROperation(urlEncoded);
RemoteOperationResult result = operation.execute(account, targetContext);
SendCSRRemoteOperation operation = new SendCSRRemoteOperation(urlEncoded);
RemoteOperationResult<String> result = operation.executeNextcloudClient(account, targetContext);

assertTrue(result.isSuccess());
String publicKeyString = (String) result.getData().get(0);
String publicKeyString = result.getResultData();

// check key
RSAPrivateCrtKey privateKey = (RSAPrivateCrtKey) keyPair.getPrivate();
Expand Down Expand Up @@ -562,10 +552,60 @@ public void reInit() throws Exception {
useExistingKeys();
}

@Test
public void shareFolder() throws Exception {
init();

Object object = EncryptionUtils.downloadFolderMetadata(currentFolder,
client,
targetContext,
user);

// metadata does not yet exist
assertNull(object);

assertTrue(new CreateShareWithShareeOperation(
currentFolder.getRemotePath(),
"e2e",
ShareType.USER,
OCShare.SHARE_PERMISSION_FLAG,
"",
"",
-1,
false,
fileDataStorageManager,
targetContext,
user,
new ArbitraryDataProviderImpl(targetContext))
.execute(client)
.isSuccess());

// verify
Object newObject = EncryptionUtils.downloadFolderMetadata(currentFolder,
client,
targetContext,
user);

assertTrue(newObject instanceof EncryptedFolderMetadataFile);

assertEquals(2, ((EncryptedFolderMetadataFile) newObject).getUsers().size());
}

// @Test
// public void testRemoveFiles() throws Exception {
// init();
// createFolder();
// goIntoFolder(1);
//
// OCFile root = fileDataStorageManager.getFileByDecryptedRemotePath("/");
// removeFolder(root);
// }

private void useExistingKeys() throws Exception {
// download them from server
GetPublicKeyOperation publicKeyOperation = new GetPublicKeyOperation();
RemoteOperationResult<String> publicKeyResult = publicKeyOperation.execute(account, targetContext);
GetPublicKeyRemoteOperation publicKeyOperation = new GetPublicKeyRemoteOperation();
RemoteOperationResult<String> publicKeyResult = publicKeyOperation.executeNextcloudClient(account,
targetContext);

assertTrue("Result code:" + publicKeyResult.getHttpCode(), publicKeyResult.isSuccess());

Expand All @@ -574,8 +614,8 @@ private void useExistingKeys() throws Exception {
EncryptionUtils.PUBLIC_KEY,
publicKeyFromServer);

RemoteOperationResult<PrivateKey> privateKeyResult = new GetPrivateKeyOperation().execute(account,
targetContext);
RemoteOperationResult<PrivateKey> privateKeyResult = new GetPrivateKeyRemoteOperation()
.executeNextcloudClient(account, targetContext);
assertTrue(privateKeyResult.isSuccess());

PrivateKey privateKey = privateKeyResult.getResultData();
Expand Down Expand Up @@ -605,13 +645,13 @@ private static void createKeys() throws Exception {
// create CSR
AccountManager accountManager = AccountManager.get(targetContext);
String userId = accountManager.getUserData(account, AccountUtils.Constants.KEY_USER_ID);
String urlEncoded = CsrHelper.generateCsrPemEncodedString(keyPair, userId);
String urlEncoded = new CsrHelper().generateCsrPemEncodedString(keyPair, userId);

SendCSROperation operation = new SendCSROperation(urlEncoded);
RemoteOperationResult result = operation.execute(account, targetContext);
SendCSRRemoteOperation operation = new SendCSRRemoteOperation(urlEncoded);
RemoteOperationResult<String> result = operation.executeNextcloudClient(account, targetContext);

if (result.isSuccess()) {
publicKeyString = (String) result.getData().get(0);
publicKeyString = result.getResultData();

// check key
RSAPrivateCrtKey privateKey = (RSAPrivateCrtKey) keyPair.getPrivate();
Expand All @@ -634,8 +674,9 @@ private static void createKeys() throws Exception {
generateMnemonicString());

// upload encryptedPrivateKey
StorePrivateKeyOperation storePrivateKeyOperation = new StorePrivateKeyOperation(encryptedPrivateKey);
RemoteOperationResult storePrivateKeyResult = storePrivateKeyOperation.execute(account, targetContext);
StorePrivateKeyRemoteOperation storePrivateKeyOperation = new StorePrivateKeyRemoteOperation(encryptedPrivateKey);
RemoteOperationResult storePrivateKeyResult = storePrivateKeyOperation.executeNextcloudClient(account,
targetContext);

if (storePrivateKeyResult.isSuccess()) {
arbitraryDataProvider.storeOrUpdateKeyValue(account.name, EncryptionUtils.PRIVATE_KEY,
Expand All @@ -649,17 +690,21 @@ private static void createKeys() throws Exception {
}

private static void deleteKeys() {
RemoteOperationResult<PrivateKey> privateKeyRemoteOperationResult = new GetPrivateKeyOperation().execute(client);
RemoteOperationResult<String> publicKeyRemoteOperationResult = new GetPublicKeyOperation().execute(client);
RemoteOperationResult<PrivateKey> privateKeyRemoteOperationResult =
new GetPrivateKeyRemoteOperation().execute(nextcloudClient);
RemoteOperationResult<String> publicKeyRemoteOperationResult =
new GetPublicKeyRemoteOperation().execute(nextcloudClient);

if (privateKeyRemoteOperationResult.isSuccess() || publicKeyRemoteOperationResult.isSuccess()) {
// delete keys
assertTrue(new DeletePrivateKeyOperation().execute(client).isSuccess());
assertTrue(new DeletePublicKeyOperation().execute(client).isSuccess());
assertTrue(new DeletePrivateKeyRemoteOperation().execute(nextcloudClient).isSuccess());
assertTrue(new DeletePublicKeyRemoteOperation().execute(nextcloudClient).isSuccess());

arbitraryDataProvider.deleteKeyForAccount(account.name, EncryptionUtils.PRIVATE_KEY);
arbitraryDataProvider.deleteKeyForAccount(account.name, EncryptionUtils.PUBLIC_KEY);
arbitraryDataProvider.deleteKeyForAccount(account.name, EncryptionUtils.MNEMONIC);
} else {
throw new RuntimeException("Error fetching keys");
}
}

Expand Down Expand Up @@ -696,11 +741,11 @@ private void removeFolder(OCFile folder) {
// 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());
RemoteOperationResult result = new ToggleEncryptionRemoteOperation(child.getLocalId(),
child.getRemotePath(),
false)
.execute(client);
assertTrue(result.getLogMessage(), result.isSuccess());

OCFile f = getStorageManager().getFileByEncryptedRemotePath(child.getRemotePath());
f.setEncrypted(false);
Expand Down
29 changes: 20 additions & 9 deletions app/src/androidTest/java/com/owncloud/android/AbstractIT.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@
import com.nextcloud.common.NextcloudClient;
import com.nextcloud.java.util.Optional;
import com.nextcloud.test.GrantStoragePermissionRule;
import com.nextcloud.test.RandomStringGenerator;
import com.owncloud.android.datamodel.ArbitraryDataProvider;
import com.owncloud.android.datamodel.ArbitraryDataProviderImpl;
import com.owncloud.android.datamodel.FileDataStorageManager;
import com.owncloud.android.datamodel.OCFile;
import com.owncloud.android.datamodel.UploadsStorageManager;
Expand Down Expand Up @@ -100,6 +103,8 @@ public abstract class AbstractIT {
protected FileDataStorageManager fileDataStorageManager =
new FileDataStorageManager(user, targetContext.getContentResolver());

protected ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProviderImpl(targetContext);

@BeforeClass
public static void beforeAll() {
try {
Expand All @@ -118,14 +123,11 @@ public static void beforeAll() {

client = OwnCloudClientFactory.createOwnCloudClient(account, targetContext);
nextcloudClient = OwnCloudClientFactory.createNextcloudClient(user, targetContext);
} catch (OperationCanceledException e) {
e.printStackTrace();
} catch (AuthenticatorException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (AccountUtils.AccountNotFoundException e) {
e.printStackTrace();
} catch (OperationCanceledException |
IOException |
AccountUtils.AccountNotFoundException |
AuthenticatorException e) {
throw new RuntimeException("Error setting up clients", e);
}

Bundle arguments = androidx.test.platform.app.InstrumentationRegistry.getArguments();
Expand Down Expand Up @@ -338,7 +340,8 @@ public OCFile createFolder(String remotePath) {
.execute(client)
.isSuccess());

return getStorageManager().getFileByDecryptedRemotePath(remotePath);

return getStorageManager().getFileByDecryptedRemotePath(remotePath.endsWith("/") ? remotePath : remotePath + "/");
}

public void uploadFile(File file, String remotePath) {
Expand Down Expand Up @@ -473,6 +476,14 @@ public static String getUserId(User user) {
return AccountManager.get(targetContext).getUserData(user.toPlatformAccount(), KEY_USER_ID);
}

public String getRandomName() {
return getRandomName(5);
}

public String getRandomName(int length) {
return RandomStringGenerator.make(length);
}

protected static User getUser(Account account) {
Optional<User> optionalUser = UserAccountManagerImpl.fromContext(targetContext).getUser(account.name);
return optionalUser.orElseThrow(IllegalAccessError::new);
Expand Down
Loading

0 comments on commit 470825c

Please sign in to comment.