Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[stable-2.20] ToS #1668

Merged
merged 2 commits into from
Feb 10, 2025
Merged
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,39 @@
/*
* Nextcloud Android Library
*
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2024 Tobias Kaminsky <[email protected]>
* SPDX-License-Identifier: MIT
*/

package com.nextcloud.android.lib.resources.tos

import com.owncloud.android.AbstractIT
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue

class TermsOfServicesIT : AbstractIT() {
// @Test disabled for now as no good way to test on CI
fun getAndSignTerms() {
// user 3 with ToS
var result = GetTermsRemoteOperation().execute(nextcloudClient)
assertTrue(result.isSuccess)

var terms = result.resultData
assertTrue(terms.terms.isNotEmpty())
assertFalse(terms.hasSigned)

val id = terms.terms[0].id

// sign
assertTrue(SignTermRemoteOperation(id).execute(nextcloudClient).isSuccess)

// signed terms
result = GetTermsRemoteOperation().execute(nextcloudClient)
assertTrue(result.isSuccess)

terms = result.resultData
assertTrue(terms.terms.isNotEmpty())
assertTrue(terms.hasSigned)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* Nextcloud Android Library
*
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2024 Tobias Kaminsky <[email protected]>
* SPDX-License-Identifier: MIT
*/
package com.nextcloud.android.lib.resources.tos

import com.google.gson.reflect.TypeToken
import com.nextcloud.common.NextcloudClient
import com.nextcloud.operations.GetMethod
import com.owncloud.android.lib.common.operations.RemoteOperationResult
import com.owncloud.android.lib.common.utils.Log_OC
import com.owncloud.android.lib.ocs.ServerResponse
import com.owncloud.android.lib.resources.OCSRemoteOperation
import org.apache.commons.httpclient.HttpStatus

/**
* Get terms of service of an user
*/
class GetTermsRemoteOperation : OCSRemoteOperation<Terms>() {
@Suppress("TooGenericExceptionCaught")
override fun run(client: NextcloudClient): RemoteOperationResult<Terms> {
var result: RemoteOperationResult<Terms>
var getMethod: GetMethod? = null
try {
getMethod =
GetMethod(
client.baseUri.toString() + ENDPOINT + JSON_FORMAT,
true
)
val status = client.execute(getMethod)
if (status == HttpStatus.SC_OK) {
val terms =
getServerResponse(
getMethod,
object : TypeToken<ServerResponse<Terms>>() {}
)?.ocs?.data

if (terms != null) {
result = RemoteOperationResult(true, getMethod)
result.setResultData(terms)
} else {
result = RemoteOperationResult(false, getMethod)
}
} else {
result = RemoteOperationResult(false, getMethod)
}
} catch (e: Exception) {
result = RemoteOperationResult(e)
Log_OC.e(
TAG,
"Get terms failed: " + result.logMessage,
result.exception
)
} finally {
getMethod?.releaseConnection()
}
return result
}

companion object {
private val TAG = GetTermsRemoteOperation::class.java.simpleName
private const val ENDPOINT = "/ocs/v2.php/apps/terms_of_service/terms"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* Nextcloud Android Library
*
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2024 Tobias Kaminsky <[email protected]>
* SPDX-License-Identifier: MIT
*/
package com.nextcloud.android.lib.resources.tos

import com.nextcloud.common.NextcloudClient
import com.nextcloud.operations.PostMethod
import com.owncloud.android.lib.common.operations.RemoteOperation
import com.owncloud.android.lib.common.operations.RemoteOperationResult
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.RequestBody.Companion.toRequestBody
import org.apache.commons.httpclient.HttpStatus

/**
* Sign terms of services
*/
class SignTermRemoteOperation(
val id: Int
) : RemoteOperation<Void>() {
@Suppress("TooGenericExceptionCaught")
override fun run(client: NextcloudClient): RemoteOperationResult<Void> {
val requestBody = hashMapOf("termId" to id)

val json = gson.toJson(requestBody)

val request = json.toRequestBody("application/json".toMediaTypeOrNull())

val postMethod = PostMethod(client.baseUri.toString() + ENDPOINT, true, request)

val status = postMethod.execute(client)

return if (status == HttpStatus.SC_OK) {
RemoteOperationResult<Void>(true, postMethod)
} else {
RemoteOperationResult<Void>(false, postMethod)
}
}

companion object {
private const val ENDPOINT = "/ocs/v2.php/apps/terms_of_service/sign"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/*
* Nextcloud Android Library
*
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2024 Tobias Kaminsky <[email protected]>
* SPDX-License-Identifier: MIT
*/

package com.nextcloud.android.lib.resources.tos

data class Term(
val id: Int,
val countryCode: String,
val languageCode: String,
val body: String,
val renderedBody: String
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/*
* Nextcloud Android Library
*
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2024 Tobias Kaminsky <[email protected]>
* SPDX-License-Identifier: MIT
*/

package com.nextcloud.android.lib.resources.tos

data class Terms(
val terms: List<Term>,
val hasSigned: Boolean,
val languages: Map<String, String>
)
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ public class ExceptionParser {
private static final String INVALID_PATH_EXCEPTION_STRING = "OC\\Connector\\Sabre\\Exception\\InvalidPath";
private static final String INVALID_PATH_EXCEPTION_UPLOAD_STRING = "OCP\\Files\\InvalidPathException";
private static final String VIRUS_EXCEPTION_STRING = "OCA\\DAV\\Connector\\Sabre\\Exception\\UnsupportedMediaType";
private static final String TOS_EXCEPTION_STRING = "OCA\\TermsOfService\\TermsNotSignedException";

// No namespaces
private static final String ns = null;
Expand Down Expand Up @@ -75,6 +76,10 @@ public boolean isInvalidCharacterException() {
public boolean isVirusException() {
return VIRUS_EXCEPTION_STRING.equalsIgnoreCase(exception) && message.startsWith("Virus");
}

public boolean isToSException() {
return TOS_EXCEPTION_STRING.equalsIgnoreCase(exception);
}

/**
* Parse OCS node
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,8 @@ public enum ResultCode {
VIRUS_DETECTED,
FOLDER_ALREADY_EXISTS,
CANNOT_CREATE_FILE,
LOCKED
LOCKED,
SIGNING_TOS_NEEDED
}

private boolean mSuccess = false;
Expand Down Expand Up @@ -329,7 +330,9 @@ public RemoteOperationResult(boolean success, OkHttpMethodBase httpMethod) {
public RemoteOperationResult(boolean success, HttpMethod httpMethod) {
this(success, httpMethod.getStatusCode(), httpMethod.getStatusText(), httpMethod.getResponseHeaders());

if (mHttpCode == HttpStatus.SC_BAD_REQUEST || mHttpCode == HttpStatus.SC_UNSUPPORTED_MEDIA_TYPE) {
if (mHttpCode == HttpStatus.SC_BAD_REQUEST ||
mHttpCode == HttpStatus.SC_UNSUPPORTED_MEDIA_TYPE ||
mHttpCode == HttpStatus.SC_FORBIDDEN) {
try {
String bodyResponse = httpMethod.getResponseBodyAsString();

Expand All @@ -343,6 +346,9 @@ public RemoteOperationResult(boolean success, HttpMethod httpMethod) {
if (xmlParser.isVirusException()) {
mCode = ResultCode.VIRUS_DETECTED;
}
if (xmlParser.isToSException()) {
mCode = ResultCode.SIGNING_TOS_NEEDED;
}

mHttpPhrase = xmlParser.getMessage();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ protected RemoteOperationResult run(OwnCloudClient client) {
status = mRedirectionPath.getLastStatus();
}
client.exhaustResponse(head.getResponseBodyAsStream());
boolean success = (status == HttpStatus.SC_OK && !mSuccessIfAbsent) ||
boolean success = ((status == HttpStatus.SC_OK || status == HttpStatus.SC_UNAUTHORIZED || status == HttpStatus.SC_FORBIDDEN) && !mSuccessIfAbsent) ||
(status == HttpStatus.SC_NOT_FOUND && mSuccessIfAbsent);
result = new RemoteOperationResult(
success,
Expand Down
Loading