Skip to content

Commit

Permalink
NMC-1923: E2EE related changes with test cases.
Browse files Browse the repository at this point in the history
NMC-1550: Allow to create encrypted folder directly from bottom sheet dialog from NC PR: nextcloud#10782
  • Loading branch information
surinder-tsys committed Nov 25, 2024
1 parent d4b4689 commit d52e1a6
Show file tree
Hide file tree
Showing 24 changed files with 2,875 additions and 2,026 deletions.
152 changes: 152 additions & 0 deletions app/src/androidTest/java/com/nmc/android/FileMenuFilterIT.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
/*
* Nextcloud Android client application
*
* @author Álvaro Brey Vilas
* Copyright (C) 2022 Álvaro Brey Vilas
* Copyright (C) 2022 Nextcloud GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.nmc.android

import androidx.test.core.app.launchActivity
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.nextcloud.client.account.User
import com.nextcloud.client.jobs.download.FileDownloadWorker
import com.nextcloud.client.jobs.upload.FileUploadHelper
import com.nextcloud.test.TestActivity
import com.nextcloud.utils.EditorUtils
import com.owncloud.android.AbstractIT
import com.owncloud.android.R
import com.owncloud.android.datamodel.ArbitraryDataProvider
import com.owncloud.android.datamodel.FileDataStorageManager
import com.owncloud.android.datamodel.OCFile
import com.owncloud.android.files.FileMenuFilter
import com.owncloud.android.lib.resources.status.CapabilityBooleanType
import com.owncloud.android.lib.resources.status.OCCapability
import com.owncloud.android.services.OperationsService
import com.owncloud.android.ui.activity.ComponentsGetter
import com.owncloud.android.utils.MimeType
import io.mockk.MockKAnnotations
import io.mockk.every
import io.mockk.impl.annotations.MockK
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import java.security.SecureRandom

@RunWith(AndroidJUnit4::class)
class FileMenuFilterIT : AbstractIT() {

@MockK
private lateinit var mockComponentsGetter: ComponentsGetter

@MockK
private lateinit var mockStorageManager: FileDataStorageManager

@MockK
private lateinit var mockFileUploaderBinder: FileUploadHelper

@MockK
private lateinit var mockOperationsServiceBinder: OperationsService.OperationsServiceBinder

@MockK
private lateinit var mockFileDownloadProgressListener: FileDownloadWorker.FileDownloadProgressListener

@MockK
private lateinit var mockArbitraryDataProvider: ArbitraryDataProvider

private lateinit var editorUtils: EditorUtils

@Before
fun setup() {
MockKAnnotations.init(this)
every { mockFileUploaderBinder.isUploading(any(), any()) } returns false
every { mockComponentsGetter.fileUploaderHelper } returns mockFileUploaderBinder
every { mockFileDownloadProgressListener.isDownloading(any(), any()) } returns false
every { mockComponentsGetter.fileDownloadProgressListener } returns mockFileDownloadProgressListener
every { mockOperationsServiceBinder.isSynchronizing(any(), any()) } returns false
every { mockComponentsGetter.operationsServiceBinder } returns mockOperationsServiceBinder
every { mockStorageManager.getFileById(any()) } returns OCFile("/")
every { mockStorageManager.getFolderContent(any(), any()) } returns ArrayList<OCFile>()
every { mockArbitraryDataProvider.getValue(any<User>(), any()) } returns ""
editorUtils = EditorUtils(mockArbitraryDataProvider)
}

@Test
fun hide_shareAndFavouriteMenu_encryptedFolder() {
val capability = OCCapability().apply {
endToEndEncryption = CapabilityBooleanType.TRUE
}

val encryptedFolder = OCFile("/encryptedFolder/").apply {
isEncrypted = true
mimeType = MimeType.DIRECTORY
fileLength = SecureRandom().nextLong()
}

configureCapability(capability)

launchActivity<TestActivity>().use {
it.onActivity { activity ->
val filterFactory =
FileMenuFilter.Factory(mockStorageManager, activity, editorUtils)

val sut = filterFactory.newInstance(encryptedFolder, mockComponentsGetter, true, user)
val toHide = sut.getToHide(false)

// encrypted folder
assertTrue(toHide.contains(R.id.action_see_details))
assertTrue(toHide.contains(R.id.action_favorite))
assertTrue(toHide.contains(R.id.action_unset_favorite))
}
}
}

@Test
fun show_shareAndFavouriteMenu_normalFolder() {
val capability = OCCapability().apply {
endToEndEncryption = CapabilityBooleanType.TRUE
}

val normalFolder = OCFile("/folder/").apply {
mimeType = MimeType.DIRECTORY
fileLength = SecureRandom().nextLong()
}

configureCapability(capability)

launchActivity<TestActivity>().use {
it.onActivity { activity ->
val filterFactory =
FileMenuFilter.Factory(mockStorageManager, activity, editorUtils)

val sut = filterFactory.newInstance(normalFolder, mockComponentsGetter, true, user)
val toHide = sut.getToHide(false)

// normal folder
assertFalse(toHide.contains(R.id.action_see_details))
assertFalse(toHide.contains(R.id.action_favorite))
assertTrue(toHide.contains(R.id.action_unset_favorite))
}
}
}

private fun configureCapability(capability: OCCapability) {
every { mockStorageManager.getCapability(any<User>()) } returns capability
every { mockStorageManager.getCapability(any<String>()) } returns capability
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package com.nmc.android

import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.intent.rule.IntentsTestRule
import androidx.test.espresso.matcher.ViewMatchers.withHint
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.internal.runner.junit4.statement.UiThreadStatement
import com.nextcloud.test.TestActivity
import com.owncloud.android.AbstractIT
import com.owncloud.android.ui.dialog.setupEncryption.SetupEncryptionDialogFragment
import org.junit.Rule
import org.junit.Test
import com.owncloud.android.R

class SetupEncryptionDialogFragmentIT : AbstractIT() {

@get:Rule
val testActivityRule = IntentsTestRule(TestActivity::class.java, true, false)

@Test
fun validatePassphraseInputHint() {
val activity = testActivityRule.launchActivity(null)

val sut = SetupEncryptionDialogFragment.newInstance(user, 0)

sut.show(activity.supportFragmentManager, "1")

val keyWords = arrayListOf(
"ability",
"able",
"about",
"above",
"absent",
"absorb",
"abstract",
"absurd",
"abuse",
"access",
"accident",
"account",
"accuse"
)

shortSleep()

UiThreadStatement.runOnUiThread {
sut.setMnemonic(keyWords)
sut.showMnemonicInfo()
}

waitForIdleSync()

onView(withId(R.id.encryption_passwordInput)).check(matches(withHint("Passphrase…")))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,11 @@ public void createFolder() {

}

@Override
public void createEncryptedFolder() {

}

@Override
public void uploadFromApp() {

Expand Down
7 changes: 6 additions & 1 deletion app/src/main/java/com/owncloud/android/datamodel/OCFile.java
Original file line number Diff line number Diff line change
Expand Up @@ -624,14 +624,19 @@ public boolean isHidden() {
}

/**
* The unique fileId for the file within the instance This only works if we have 12 digits for instanceId RemoteId
* is a combination of localId + instanceId If a file has remoteId: 4174305739oc97a8ddfc96, in this 4174305739 is
* localId & oc97a8ddfc96 is instanceId which is of 12 digits
*
* unique fileId for the file within the instance
*/
@SuppressFBWarnings("STT")
public long getLocalId() {
if (localId > 0) {
return localId;
} else if (remoteId != null && remoteId.length() > 8) {
return Long.parseLong(remoteId.substring(0, 8).replaceAll("^0*", ""));
//NMC Customization --> for long remote id's
return Long.parseLong(remoteId.substring(0, remoteId.length() - 12).replaceAll("^0*", ""));
} else {
return -1;
}
Expand Down
29 changes: 22 additions & 7 deletions app/src/main/java/com/owncloud/android/files/FileMenuFilter.java
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ private List<Integer> filter(boolean inSingleFileFragment) {


private void filterShareFile(List<Integer> toHide, OCCapability capability) {
if (!isSingleSelection() || containsEncryptedFile() || hasEncryptedParent() ||
if (!isSingleSelection() || containsEncryptedFile() || hasEncryptedParent() || containsEncryptedFolder() ||
(!isShareViaLinkAllowed() && !isShareWithUsersAllowed()) ||
!isShareApiEnabled(capability) || !files.iterator().next().canReshare()) {
toHide.add(R.id.action_send_share_file);
Expand Down Expand Up @@ -208,19 +208,21 @@ private void filterSendFiles(List<Integer> toHide, boolean inSingleFileFragment)
}

private void filterDetails(Collection<Integer> toHide) {
if (!isSingleSelection()) {
if (!isSingleSelection() || containsEncryptedFolder() || containsEncryptedFile()) {
toHide.add(R.id.action_see_details);
}
}

private void filterFavorite(List<Integer> toHide, boolean synchronizing) {
if (files.isEmpty() || synchronizing || allFavorites()) {
if (files.isEmpty() || synchronizing || allFavorites() || containsEncryptedFile()
|| containsEncryptedFolder()) {
toHide.add(R.id.action_favorite);
}
}

private void filterUnfavorite(List<Integer> toHide, boolean synchronizing) {
if (files.isEmpty() || synchronizing || allNotFavorites()) {
if (files.isEmpty() || synchronizing || allNotFavorites() || containsEncryptedFile()
|| containsEncryptedFolder()) {
toHide.add(R.id.action_unset_favorite);
}
}
Expand Down Expand Up @@ -253,7 +255,7 @@ private void filterUnlock(List<Integer> toHide, boolean fileLockingEnabled) {

private void filterEncrypt(List<Integer> toHide, boolean endToEndEncryptionEnabled) {
if (files.isEmpty() || !isSingleSelection() || isSingleFile() || isEncryptedFolder() || isGroupFolder()
|| !endToEndEncryptionEnabled || !isEmptyFolder() || isShared()) {
|| !endToEndEncryptionEnabled || !isEmptyFolder() || isShared() || isInSubFolder()) {
toHide.add(R.id.action_encrypted);
}
}
Expand Down Expand Up @@ -355,8 +357,10 @@ private void filterSelectAll(List<Integer> toHide, boolean inSingleFileFragment)
}

private void filterRemove(List<Integer> toHide, boolean synchronizing) {
if (files.isEmpty() || synchronizing || containsLockedFile()
|| containsEncryptedFolder() || isFolderAndContainsEncryptedFile()) {
if ((files.isEmpty() || synchronizing || containsLockedFile()
|| containsEncryptedFolder() || isFolderAndContainsEncryptedFile())
//show delete option for encrypted sub-folder
&& !hasEncryptedParent()) {
toHide.add(R.id.action_remove_file);
}
}
Expand Down Expand Up @@ -597,4 +601,15 @@ private boolean isShared() {
}
return false;
}

private boolean isInSubFolder() {
OCFile folder = files.iterator().next();
OCFile parent = storageManager.getFileById(folder.getParentId());

if (parent == null) {
return false;
}

return !OCFile.ROOT_PATH.equals(parent.getRemotePath());
}
}
Loading

0 comments on commit d52e1a6

Please sign in to comment.