Skip to content

Commit 8f6d922

Browse files
ToS
Signed-off-by: tobiasKaminsky <tobias@kaminsky.me>
1 parent e3c0611 commit 8f6d922

8 files changed

Lines changed: 196 additions & 3 deletions

File tree

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
* Nextcloud Android Library
3+
*
4+
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
5+
* SPDX-FileCopyrightText: 2024 Tobias Kaminsky <tobias@kaminsky.me>
6+
* SPDX-License-Identifier: MIT
7+
*/
8+
9+
package com.nextcloud.android.lib.resources.tos
10+
11+
import com.owncloud.android.AbstractIT
12+
import org.junit.Assert.assertFalse
13+
import org.junit.Assert.assertTrue
14+
15+
class TermsOfServicesIT : AbstractIT() {
16+
// @Test disabled for now as no good way to test on CI
17+
fun getAndSignTerms() {
18+
// user 3 with ToS
19+
var result = GetTermsRemoteOperation().execute(nextcloudClient)
20+
assertTrue(result.isSuccess)
21+
22+
var terms = result.resultData
23+
assertTrue(terms.terms.isNotEmpty())
24+
assertFalse(terms.hasSigned)
25+
26+
val id = terms.terms[0].id
27+
28+
// sign
29+
assertTrue(SignTermRemoteOperation(id).execute(nextcloudClient).isSuccess)
30+
31+
// signed terms
32+
result = GetTermsRemoteOperation().execute(nextcloudClient)
33+
assertTrue(result.isSuccess)
34+
35+
terms = result.resultData
36+
assertTrue(terms.terms.isNotEmpty())
37+
assertTrue(terms.hasSigned)
38+
}
39+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/*
2+
* Nextcloud Android Library
3+
*
4+
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
5+
* SPDX-FileCopyrightText: 2024 Tobias Kaminsky <tobias@kaminsky.me>
6+
* SPDX-License-Identifier: MIT
7+
*/
8+
package com.nextcloud.android.lib.resources.tos
9+
10+
import com.google.gson.reflect.TypeToken
11+
import com.nextcloud.common.NextcloudClient
12+
import com.nextcloud.operations.GetMethod
13+
import com.owncloud.android.lib.common.operations.RemoteOperationResult
14+
import com.owncloud.android.lib.common.utils.Log_OC
15+
import com.owncloud.android.lib.ocs.ServerResponse
16+
import com.owncloud.android.lib.resources.OCSRemoteOperation
17+
import org.apache.commons.httpclient.HttpStatus
18+
19+
/**
20+
* Get terms of service of an user
21+
*/
22+
class GetTermsRemoteOperation : OCSRemoteOperation<Terms>() {
23+
@Suppress("TooGenericExceptionCaught")
24+
override fun run(client: NextcloudClient): RemoteOperationResult<Terms> {
25+
var result: RemoteOperationResult<Terms>
26+
var getMethod: GetMethod? = null
27+
try {
28+
getMethod =
29+
GetMethod(
30+
client.baseUri.toString() + ENDPOINT + JSON_FORMAT,
31+
true
32+
)
33+
val status = client.execute(getMethod)
34+
if (status == HttpStatus.SC_OK) {
35+
val terms =
36+
getServerResponse(
37+
getMethod,
38+
object : TypeToken<ServerResponse<Terms>>() {}
39+
)?.ocs?.data
40+
41+
if (terms != null) {
42+
result = RemoteOperationResult(true, getMethod)
43+
result.setResultData(terms)
44+
} else {
45+
result = RemoteOperationResult(false, getMethod)
46+
}
47+
} else {
48+
result = RemoteOperationResult(false, getMethod)
49+
}
50+
} catch (e: Exception) {
51+
result = RemoteOperationResult(e)
52+
Log_OC.e(
53+
TAG,
54+
"Get terms failed: " + result.logMessage,
55+
result.exception
56+
)
57+
} finally {
58+
getMethod?.releaseConnection()
59+
}
60+
return result
61+
}
62+
63+
companion object {
64+
private val TAG = GetTermsRemoteOperation::class.java.simpleName
65+
private const val ENDPOINT = "/ocs/v2.php/apps/terms_of_service/terms"
66+
}
67+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* Nextcloud Android Library
3+
*
4+
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
5+
* SPDX-FileCopyrightText: 2024 Tobias Kaminsky <tobias@kaminsky.me>
6+
* SPDX-License-Identifier: MIT
7+
*/
8+
package com.nextcloud.android.lib.resources.tos
9+
10+
import com.nextcloud.common.NextcloudClient
11+
import com.nextcloud.operations.PostMethod
12+
import com.owncloud.android.lib.common.operations.RemoteOperation
13+
import com.owncloud.android.lib.common.operations.RemoteOperationResult
14+
import okhttp3.MediaType.Companion.toMediaTypeOrNull
15+
import okhttp3.RequestBody.Companion.toRequestBody
16+
import org.apache.commons.httpclient.HttpStatus
17+
18+
/**
19+
* Sign terms of services
20+
*/
21+
class SignTermRemoteOperation(val id: Int) : RemoteOperation<Void>() {
22+
@Suppress("TooGenericExceptionCaught")
23+
override fun run(client: NextcloudClient): RemoteOperationResult<Void> {
24+
val requestBody = hashMapOf("termId" to id)
25+
26+
val json = gson.toJson(requestBody)
27+
28+
val request = json.toRequestBody("application/json".toMediaTypeOrNull())
29+
30+
val postMethod = PostMethod(client.baseUri.toString() + ENDPOINT, true, request)
31+
32+
val status = postMethod.execute(client)
33+
34+
return if (status == HttpStatus.SC_OK) {
35+
RemoteOperationResult<Void>(true, postMethod)
36+
} else {
37+
RemoteOperationResult<Void>(false, postMethod)
38+
}
39+
}
40+
41+
companion object {
42+
private const val ENDPOINT = "/ocs/v2.php/apps/terms_of_service/sign"
43+
}
44+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/*
2+
* Nextcloud Android Library
3+
*
4+
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
5+
* SPDX-FileCopyrightText: 2024 Tobias Kaminsky <tobias@kaminsky.me>
6+
* SPDX-License-Identifier: MIT
7+
*/
8+
9+
package com.nextcloud.android.lib.resources.tos
10+
11+
data class Term(
12+
val id: Int,
13+
val countryCode: String,
14+
val languageCode: String,
15+
val body: String,
16+
val renderedBody: String
17+
)
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/*
2+
* Nextcloud Android Library
3+
*
4+
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
5+
* SPDX-FileCopyrightText: 2024 Tobias Kaminsky <tobias@kaminsky.me>
6+
* SPDX-License-Identifier: MIT
7+
*/
8+
9+
package com.nextcloud.android.lib.resources.tos
10+
11+
data class Terms(
12+
val terms: List<Term>,
13+
val hasSigned: Boolean,
14+
val languages: Map<String, String>
15+
)

library/src/main/java/com/owncloud/android/lib/common/operations/ExceptionParser.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ public class ExceptionParser {
3030
private static final String INVALID_PATH_EXCEPTION_STRING = "OC\\Connector\\Sabre\\Exception\\InvalidPath";
3131
private static final String INVALID_PATH_EXCEPTION_UPLOAD_STRING = "OCP\\Files\\InvalidPathException";
3232
private static final String VIRUS_EXCEPTION_STRING = "OCA\\DAV\\Connector\\Sabre\\Exception\\UnsupportedMediaType";
33+
private static final String TOS_EXCEPTION_STRING = "OCA\\TermsOfService\\TermsNotSignedException";
3334

3435
// No namespaces
3536
private static final String ns = null;
@@ -75,6 +76,10 @@ public boolean isInvalidCharacterException() {
7576
public boolean isVirusException() {
7677
return VIRUS_EXCEPTION_STRING.equalsIgnoreCase(exception) && message.startsWith("Virus");
7778
}
79+
80+
public boolean isToSException() {
81+
return TOS_EXCEPTION_STRING.equalsIgnoreCase(exception);
82+
}
7883

7984
/**
8085
* Parse OCS node

library/src/main/java/com/owncloud/android/lib/common/operations/RemoteOperationResult.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,8 @@ public enum ResultCode {
135135
VIRUS_DETECTED,
136136
FOLDER_ALREADY_EXISTS,
137137
CANNOT_CREATE_FILE,
138-
LOCKED
138+
LOCKED,
139+
SIGNING_TOS_NEEDED
139140
}
140141

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

332-
if (mHttpCode == HttpStatus.SC_BAD_REQUEST || mHttpCode == HttpStatus.SC_UNSUPPORTED_MEDIA_TYPE) {
333+
if (mHttpCode == HttpStatus.SC_BAD_REQUEST ||
334+
mHttpCode == HttpStatus.SC_UNSUPPORTED_MEDIA_TYPE ||
335+
mHttpCode == HttpStatus.SC_FORBIDDEN) {
333336
try {
334337
String bodyResponse = httpMethod.getResponseBodyAsString();
335338

@@ -343,6 +346,9 @@ public RemoteOperationResult(boolean success, HttpMethod httpMethod) {
343346
if (xmlParser.isVirusException()) {
344347
mCode = ResultCode.VIRUS_DETECTED;
345348
}
349+
if (xmlParser.isToSException()) {
350+
mCode = ResultCode.SIGNING_TOS_NEEDED;
351+
}
346352

347353
mHttpPhrase = xmlParser.getMessage();
348354
}

library/src/main/java/com/owncloud/android/lib/resources/files/ExistenceCheckRemoteOperation.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ protected RemoteOperationResult run(OwnCloudClient client) {
8181
status = mRedirectionPath.getLastStatus();
8282
}
8383
client.exhaustResponse(head.getResponseBodyAsStream());
84-
boolean success = (status == HttpStatus.SC_OK && !mSuccessIfAbsent) ||
84+
boolean success = ((status == HttpStatus.SC_OK || status == HttpStatus.SC_UNAUTHORIZED || status == HttpStatus.SC_FORBIDDEN) && !mSuccessIfAbsent) ||
8585
(status == HttpStatus.SC_NOT_FOUND && mSuccessIfAbsent);
8686
result = new RemoteOperationResult(
8787
success,

0 commit comments

Comments
 (0)