Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* Nextcloud Android Library
*
* SPDX-FileCopyrightText: 2026 Alper Ozturk <alper.ozturk@nextcloud.com>
* SPDX-License-Identifier: MIT
*/
package com.owncloud.android.lib.resources.e2ee

import com.owncloud.android.AbstractIT
import com.owncloud.android.lib.common.OwnCloudClientManagerFactory
import com.owncloud.android.lib.resources.users.GetServerPublicKeyRemoteOperation
import org.apache.commons.httpclient.HttpStatus
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test

class GetServerPublicKeyRemoteOperationIT : AbstractIT() {
@Before
fun init() {
OwnCloudClientManagerFactory.setUserAgent(SUPPORTED_USER_AGENT)
}

@Test
fun serverPublicKeyIsReturnedAsPem() {
val result = GetServerPublicKeyRemoteOperation().execute(nextcloudClient)

assertTrue(result.isSuccess)
val key = result.resultData.trim()
assertTrue(key.startsWith(PEM_HEADER))
assertTrue(key.endsWith(PEM_FOOTER))
}

@Test
fun unsupportedClientIsRejected() {
OwnCloudClientManagerFactory.setUserAgent(UNSUPPORTED_USER_AGENT)

val result = GetServerPublicKeyRemoteOperation().execute(nextcloudClient)

assertFalse(result.isSuccess)
assertEquals(HttpStatus.SC_FORBIDDEN, result.httpCode)
}

companion object {
private const val SUPPORTED_USER_AGENT = "Mozilla/5.0 (Android) Nextcloud-android/3.13.0"
private const val UNSUPPORTED_USER_AGENT = "Mozilla/5.0 (Android) Nextcloud-android/1.0.0"
private const val PEM_HEADER = "-----BEGIN PUBLIC KEY-----"
private const val PEM_FOOTER = "-----END PUBLIC KEY-----"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ package com.owncloud.android.lib.resources.e2ee

import com.owncloud.android.AbstractIT
import com.owncloud.android.lib.common.OwnCloudClientManagerFactory
import com.owncloud.android.lib.resources.users.GetPublicKeyRemoteOperation
import com.owncloud.android.lib.resources.users.GetCertificateRemoteOperation
import com.owncloud.android.lib.resources.users.SendCSRRemoteOperation
import junit.framework.TestCase
import junit.framework.TestCase.assertTrue
Expand Down Expand Up @@ -44,7 +44,7 @@ class SendCSRRemoteOperationIT : AbstractIT() {
assertTrue(result.isSuccess)

// verify public key
result = GetPublicKeyRemoteOperation(client.userId).execute(nextcloudClient)
result = GetCertificateRemoteOperation(client.userId).execute(nextcloudClient)
assertTrue(result.isSuccess)
TestCase.assertNotNull(result.resultData)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
* Nextcloud Android Library
*
* SPDX-FileCopyrightText: 2017-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2026 Alper Ozturk <alper.ozturk@nextcloud.com>
* SPDX-FileCopyrightText: 2017 Tobias Kaminsky <tobias@kaminsky.me>
* SPDX-License-Identifier: MIT
*/
package com.owncloud.android.lib.resources.users

import com.nextcloud.common.NextcloudClient
import com.nextcloud.operations.GetMethod
import com.owncloud.android.lib.common.operations.RemoteOperation
import com.owncloud.android.lib.common.operations.RemoteOperationResult
import com.owncloud.android.lib.common.utils.Log_OC
import org.apache.commons.httpclient.HttpStatus
import org.json.JSONObject

/**
* Fetches the end-to-end encryption certificate of a user.
*
* Note: although the endpoint is `.../api/v1/public-key` and the response node is `public-keys`,
* the server returns the user's PEM-encoded X.509 **certificate** (`-----BEGIN CERTIFICATE-----`).
*/
class GetCertificateRemoteOperation
@JvmOverloads
constructor(
private val user: String = ""
) : RemoteOperation<String>() {
@Suppress("TooGenericExceptionCaught")
override fun run(client: NextcloudClient): RemoteOperationResult<String> {
val targetUser = user.ifEmpty { client.userId }
val getMethod = GetMethod(client.baseUri.toString() + CERTIFICATE_URL + JSON_FORMAT, true)
if (user.isNotEmpty()) {
getMethod.setQueryString(mapOf(USERS_PARAM to "[\"$user\"]"))
}

return try {
val status = client.execute(getMethod)
if (status != HttpStatus.SC_OK) {
return RemoteOperationResult(false, getMethod)
}
val key =
JSONObject(getMethod.getResponseBodyAsString())
.getJSONObject(NODE_OCS)
.getJSONObject(NODE_DATA)
.getJSONObject(NODE_PUBLIC_KEYS)
.getString(targetUser)
RemoteOperationResult<String>(true, getMethod).apply { resultData = key }
} catch (e: Exception) {
failure(e, targetUser)
} finally {
getMethod.releaseConnection()
}
}

@Suppress("DEPRECATION")
private fun failure(
e: Exception,
targetUser: String
): RemoteOperationResult<String> =
RemoteOperationResult<String>(e).also {
Log_OC.e(TAG, "Fetching of public key failed for user $targetUser: ${it.logMessage}", it.exception)
}

companion object {
private val TAG = GetCertificateRemoteOperation::class.java.simpleName
private const val CERTIFICATE_URL = "/ocs/v2.php/apps/end_to_end_encryption/api/v1/public-key"
private const val USERS_PARAM = "users"
private const val NODE_OCS = "ocs"
private const val NODE_DATA = "data"
private const val NODE_PUBLIC_KEYS = "public-keys"
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -16,43 +16,42 @@ import org.apache.commons.httpclient.HttpStatus
import org.json.JSONObject

class GetServerPublicKeyRemoteOperation : RemoteOperation<String>() {
companion object {
private val TAG: String = GetPublicKeyRemoteOperation::class.java.simpleName
private const val ENDPOINT = "/ocs/v2.php/apps/end_to_end_encryption/api/v1/server-key"
}

@Suppress("TooGenericExceptionCaught")
override fun run(client: NextcloudClient): RemoteOperationResult<String> {
var getMethod: GetMethod? = null
var result: RemoteOperationResult<String>
val user: String = client.userId

try {
getMethod = GetMethod(client.baseUri.toString() + ENDPOINT + JSON_FORMAT, true)
val getMethod = GetMethod(client.baseUri.toString() + ENDPOINT + JSON_FORMAT, true)

return try {
val status = client.execute(getMethod)

if (status == HttpStatus.SC_OK) {
val response = getMethod.getResponseBodyAsString()
val respJSON = JSONObject(response)
val serverKey = respJSON.getJSONObject("ocs").getJSONObject("data").getString("public-key")

result = RemoteOperationResult(true, getMethod)
result.setResultData(serverKey)
} else {
result = RemoteOperationResult(false, getMethod)
if (status != HttpStatus.SC_OK) {
return RemoteOperationResult(false, getMethod)
}
val serverKey =
JSONObject(getMethod.getResponseBodyAsString())
.getJSONObject(NODE_OCS)
.getJSONObject(NODE_DATA)
.getString(NODE_PUBLIC_KEY)
RemoteOperationResult<String>(true, getMethod).apply { resultData = serverKey }
} catch (e: Exception) {
result = RemoteOperationResult(e)
Log_OC.e(
TAG,
"Fetching of server public key failed for user " + user + ": " + result.logMessage,
result.exception
)
failure(e, client.userId)
} finally {
getMethod?.releaseConnection()
getMethod.releaseConnection()
}
}

@Suppress("DEPRECATION")
private fun failure(
e: Exception,
user: String
): RemoteOperationResult<String> =
RemoteOperationResult<String>(e).also {
Log_OC.e(TAG, "Fetching of server public key failed for user $user: ${it.logMessage}", it.exception)
}

return result
companion object {
private val TAG = GetServerPublicKeyRemoteOperation::class.java.simpleName
private const val ENDPOINT = "/ocs/v2.php/apps/end_to_end_encryption/api/v1/server-key"
private const val NODE_OCS = "ocs"
private const val NODE_DATA = "data"
private const val NODE_PUBLIC_KEY = "public-key"
}
}
Loading