mirror of
https://github.com/owncloud/android-library.git
synced 2025-06-07 16:06:08 +00:00
Merge pull request #368 from owncloud/fix/cookie_handling
[Fix] Cookie handling
This commit is contained in:
commit
87a05491ab
@ -9,12 +9,13 @@ dependencies {
|
||||
api 'com.github.AppDevNext.Logcat:LogcatCore:2.2.2'
|
||||
|
||||
// Moshi
|
||||
implementation ("com.squareup.moshi:moshi-kotlin:$moshiVersion") {
|
||||
implementation("com.squareup.moshi:moshi-kotlin:$moshiVersion") {
|
||||
exclude module: "kotlin-reflect"
|
||||
}
|
||||
kapt "com.squareup.moshi:moshi-kotlin-codegen:$moshiVersion"
|
||||
|
||||
testImplementation 'junit:junit:4.13.2'
|
||||
testImplementation 'org.robolectric:robolectric:4.3.1'
|
||||
}
|
||||
|
||||
android {
|
||||
@ -37,4 +38,10 @@ android {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
testOptions {
|
||||
unitTests {
|
||||
includeAndroidResources = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -27,6 +27,9 @@ package com.owncloud.android.lib.common;
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
|
||||
import com.owncloud.android.lib.common.http.HttpClient;
|
||||
import com.owncloud.android.lib.resources.status.GetRemoteStatusOperation;
|
||||
|
||||
public class OwnCloudClientFactory {
|
||||
|
||||
/**
|
||||
@ -42,8 +45,14 @@ public class OwnCloudClientFactory {
|
||||
|
||||
client.setFollowRedirects(followRedirects);
|
||||
|
||||
client.setContext(context);
|
||||
HttpClient.setContext(context);
|
||||
retrieveCookiesFromMiddleware(client);
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
private static void retrieveCookiesFromMiddleware(OwnCloudClient client) {
|
||||
final GetRemoteStatusOperation statusOperation = new GetRemoteStatusOperation();
|
||||
statusOperation.run(client);
|
||||
}
|
||||
}
|
||||
|
@ -24,8 +24,6 @@
|
||||
|
||||
package com.owncloud.android.lib.common;
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.accounts.AccountManager;
|
||||
import android.accounts.AuthenticatorException;
|
||||
import android.accounts.OperationCanceledException;
|
||||
import android.content.Context;
|
||||
@ -37,7 +35,6 @@ import com.owncloud.android.lib.common.http.HttpClient;
|
||||
import timber.log.Timber;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Iterator;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
@ -111,6 +108,12 @@ public class SingleSessionManager {
|
||||
account.getBaseUri(),
|
||||
context.getApplicationContext(),
|
||||
true); // TODO remove dependency on OwnCloudClientFactory
|
||||
|
||||
//the next two lines are a hack because okHttpclient is used as a singleton instead of being an
|
||||
//injected instance that can be deleted when required
|
||||
client.clearCookies();
|
||||
client.clearCredentials();
|
||||
|
||||
client.setAccount(account);
|
||||
HttpClient.setContext(context);
|
||||
|
||||
@ -130,7 +133,6 @@ public class SingleSessionManager {
|
||||
Timber.v("reusing client for session %s", sessionName);
|
||||
}
|
||||
|
||||
keepCookiesUpdated(context, account, client);
|
||||
keepUriUpdated(account, client);
|
||||
}
|
||||
Timber.d("getClientFor finishing ");
|
||||
@ -161,32 +163,6 @@ public class SingleSessionManager {
|
||||
Timber.d("removeClientFor finishing ");
|
||||
}
|
||||
|
||||
public void saveAllClients(Context context, String accountType) {
|
||||
Timber.d("Saving sessions... ");
|
||||
|
||||
Iterator<String> accountNames = mClientsWithKnownUsername.keySet().iterator();
|
||||
String accountName;
|
||||
Account account;
|
||||
while (accountNames.hasNext()) {
|
||||
accountName = accountNames.next();
|
||||
account = new Account(accountName, accountType);
|
||||
AccountUtils.saveClient(mClientsWithKnownUsername.get(accountName), account, context);
|
||||
}
|
||||
|
||||
Timber.d("All sessions saved");
|
||||
}
|
||||
|
||||
private void keepCookiesUpdated(Context context, OwnCloudAccount account, OwnCloudClient reusedClient) {
|
||||
AccountManager am = AccountManager.get(context.getApplicationContext());
|
||||
if (am != null && account.getSavedAccount() != null) {
|
||||
String recentCookies = am.getUserData(account.getSavedAccount(), AccountUtils.Constants.KEY_COOKIES);
|
||||
String previousCookies = reusedClient.getCookiesString();
|
||||
if (recentCookies != null && !previousCookies.equals("") && !recentCookies.equals(previousCookies)) {
|
||||
AccountUtils.restoreCookies(account.getSavedAccount(), reusedClient, context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void refreshCredentialsForAccount(String accountName, OwnCloudCredentials credentials) {
|
||||
OwnCloudClient ownCloudClient = mClientsWithKnownUsername.get(accountName);
|
||||
if (ownCloudClient == null) {
|
||||
|
@ -36,15 +36,10 @@ import android.net.Uri;
|
||||
import com.owncloud.android.lib.common.OwnCloudClient;
|
||||
import com.owncloud.android.lib.common.authentication.OwnCloudCredentials;
|
||||
import com.owncloud.android.lib.common.authentication.OwnCloudCredentialsFactory;
|
||||
import com.owncloud.android.lib.resources.files.FileUtils;
|
||||
import com.owncloud.android.lib.resources.status.OwnCloudVersion;
|
||||
import okhttp3.Cookie;
|
||||
import timber.log.Timber;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class AccountUtils {
|
||||
/**
|
||||
@ -202,64 +197,6 @@ public class AccountUtils {
|
||||
return username + "@" + url;
|
||||
}
|
||||
|
||||
public static void saveClient(OwnCloudClient client, Account savedAccount, Context context) {
|
||||
// Account Manager
|
||||
AccountManager ac = AccountManager.get(context.getApplicationContext());
|
||||
|
||||
if (client != null) {
|
||||
String cookiesString = client.getCookiesString();
|
||||
if (!"".equals(cookiesString)) {
|
||||
ac.setUserData(savedAccount, Constants.KEY_COOKIES, cookiesString);
|
||||
Timber.d("Saving Cookies: %s", cookiesString);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore the client cookies persisted in an account stored in the system AccountManager.
|
||||
*
|
||||
* @param account Stored account.
|
||||
* @param client Client to restore cookies in.
|
||||
* @param context Android context used to access the system AccountManager.
|
||||
*/
|
||||
public static void restoreCookies(Account account, OwnCloudClient client, Context context) {
|
||||
if (account == null) {
|
||||
Timber.d("Cannot restore cookie for null account");
|
||||
|
||||
} else {
|
||||
Timber.d("Restoring cookies for %s", account.name);
|
||||
|
||||
// Account Manager
|
||||
AccountManager am = AccountManager.get(context.getApplicationContext());
|
||||
|
||||
Uri serverUri = (client.getBaseUri() != null) ? client.getBaseUri() : client.getUserFilesWebDavUri();
|
||||
|
||||
String cookiesString = am.getUserData(account, Constants.KEY_COOKIES);
|
||||
if (cookiesString != null) {
|
||||
String[] rawCookies = cookiesString.split(";");
|
||||
List<Cookie> cookieList = new ArrayList<>(rawCookies.length);
|
||||
for (String rawCookie : rawCookies) {
|
||||
rawCookie = rawCookie.replace(" ", "");
|
||||
final int equalPos = rawCookie.indexOf('=');
|
||||
if (equalPos == -1) {
|
||||
continue;
|
||||
}
|
||||
cookieList.add(new Cookie.Builder()
|
||||
.name(rawCookie.substring(0, equalPos))
|
||||
.value(rawCookie.substring(equalPos + 1))
|
||||
.domain(serverUri.getHost())
|
||||
.path(
|
||||
serverUri.getPath().equals("")
|
||||
? File.separator
|
||||
: serverUri.getPath()
|
||||
)
|
||||
.build());
|
||||
}
|
||||
client.setCookiesForCurrentAccount(cookieList);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class AccountNotFoundException extends AccountsException {
|
||||
|
||||
/**
|
||||
@ -299,11 +236,6 @@ public class AccountUtils {
|
||||
|
||||
public static final String OAUTH_SUPPORTED_TRUE = "TRUE";
|
||||
|
||||
/**
|
||||
* OC account cookies
|
||||
*/
|
||||
public static final String KEY_COOKIES = "oc_account_cookies";
|
||||
|
||||
/**
|
||||
* OC account version
|
||||
*/
|
||||
|
@ -0,0 +1,63 @@
|
||||
/* ownCloud Android Library is available under MIT license
|
||||
* Copyright (C) 2021 ownCloud GmbH.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
package com.owncloud.android.lib.common.http
|
||||
|
||||
import okhttp3.Cookie
|
||||
import okhttp3.CookieJar
|
||||
import okhttp3.HttpUrl
|
||||
|
||||
class CookieJarImpl(
|
||||
private val sCookieStore: HashMap<String, List<Cookie>>
|
||||
) : CookieJar {
|
||||
|
||||
fun containsCookieWithName(cookies: List<Cookie>, name: String): Boolean {
|
||||
for (cookie: Cookie in cookies) {
|
||||
if (cookie.name == name) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fun getUpdatedCookies(oldCookies: List<Cookie>, newCookies: List<Cookie>): List<Cookie> {
|
||||
val updatedList = ArrayList<Cookie>(newCookies)
|
||||
for (oldCookie: Cookie in oldCookies) {
|
||||
if (!containsCookieWithName(updatedList, oldCookie.name)) {
|
||||
updatedList.add(oldCookie)
|
||||
}
|
||||
}
|
||||
return updatedList
|
||||
}
|
||||
|
||||
override fun saveFromResponse(url: HttpUrl, cookies: List<Cookie>) {
|
||||
// Avoid duplicated cookies but update
|
||||
val currentCookies: List<Cookie> = sCookieStore[url.host] ?: ArrayList()
|
||||
val updatedCookies: List<Cookie> = getUpdatedCookies(currentCookies, cookies)
|
||||
sCookieStore[url.host] = updatedCookies
|
||||
}
|
||||
|
||||
override fun loadForRequest(url: HttpUrl) =
|
||||
sCookieStore[url.host] ?: ArrayList()
|
||||
|
||||
}
|
@ -33,19 +33,18 @@ import okhttp3.CookieJar;
|
||||
import okhttp3.HttpUrl;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Protocol;
|
||||
import okhttp3.TlsVersion;
|
||||
import timber.log.Timber;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
import javax.net.ssl.TrustManager;
|
||||
import javax.net.ssl.X509TrustManager;
|
||||
import java.security.KeyManagementException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
@ -53,6 +52,7 @@ import java.util.concurrent.TimeUnit;
|
||||
*
|
||||
* @author David González Verdugo
|
||||
*/
|
||||
|
||||
public class HttpClient {
|
||||
private static OkHttpClient sOkHttpClient;
|
||||
private static Context sContext;
|
||||
@ -64,66 +64,13 @@ public class HttpClient {
|
||||
try {
|
||||
final X509TrustManager trustManager = new AdvancedX509TrustManager(
|
||||
NetworkUtils.getKnownServersStore(sContext));
|
||||
|
||||
SSLContext sslContext;
|
||||
|
||||
try {
|
||||
sslContext = SSLContext.getInstance("TLSv1.3");
|
||||
} catch (NoSuchAlgorithmException tlsv13Exception) {
|
||||
try {
|
||||
Timber.w("TLSv1.3 is not supported in this device; falling through TLSv1.2");
|
||||
sslContext = SSLContext.getInstance("TLSv1.2");
|
||||
} catch (NoSuchAlgorithmException tlsv12Exception) {
|
||||
try {
|
||||
Timber.w("TLSv1.2 is not supported in this device; falling through TLSv1.1");
|
||||
sslContext = SSLContext.getInstance("TLSv1.1");
|
||||
} catch (NoSuchAlgorithmException tlsv11Exception) {
|
||||
Timber.w("TLSv1.1 is not supported in this device; falling through TLSv1.0");
|
||||
sslContext = SSLContext.getInstance("TLSv1");
|
||||
// should be available in any device; see reference of supported protocols in
|
||||
// http://developer.android.com/reference/javax/net/ssl/SSLSocket.html
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sslContext.init(null, new TrustManager[]{trustManager}, null);
|
||||
|
||||
SSLSocketFactory sslSocketFactory;
|
||||
|
||||
sslSocketFactory = sslContext.getSocketFactory();
|
||||
|
||||
final SSLSocketFactory sslSocketFactory = getNewSslSocketFactory(trustManager);
|
||||
// Automatic cookie handling, NOT PERSISTENT
|
||||
CookieJar cookieJar = new CookieJar() {
|
||||
@Override
|
||||
public void saveFromResponse(HttpUrl url, List<Cookie> cookies) {
|
||||
// Avoid duplicated cookies
|
||||
Set<Cookie> nonDuplicatedCookiesSet = new HashSet<>(cookies);
|
||||
List<Cookie> nonDuplicatedCookiesList = new ArrayList<>(nonDuplicatedCookiesSet);
|
||||
final CookieJar cookieJar = new CookieJarImpl(sCookieStore);
|
||||
|
||||
sCookieStore.put(url.host(), nonDuplicatedCookiesList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Cookie> loadForRequest(HttpUrl url) {
|
||||
List<Cookie> cookies = sCookieStore.get(url.host());
|
||||
return cookies != null ? cookies : new ArrayList<>();
|
||||
}
|
||||
};
|
||||
|
||||
OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder()
|
||||
.addNetworkInterceptor(getLogInterceptor())
|
||||
.protocols(Arrays.asList(Protocol.HTTP_1_1))
|
||||
.readTimeout(HttpConstants.DEFAULT_DATA_TIMEOUT, TimeUnit.MILLISECONDS)
|
||||
.writeTimeout(HttpConstants.DEFAULT_DATA_TIMEOUT, TimeUnit.MILLISECONDS)
|
||||
.connectTimeout(HttpConstants.DEFAULT_CONNECTION_TIMEOUT, TimeUnit.MILLISECONDS)
|
||||
.followRedirects(false)
|
||||
.sslSocketFactory(sslSocketFactory, trustManager)
|
||||
.hostnameVerifier((asdf, usdf) -> true)
|
||||
.cookieJar(cookieJar);
|
||||
// TODO: Not verifying the hostname against certificate. ask owncloud security human if this is ok.
|
||||
//.hostnameVerifier(new BrowserCompatHostnameVerifier());
|
||||
|
||||
sOkHttpClient = clientBuilder.build();
|
||||
sOkHttpClient = buildNewOkHttpClient(sslSocketFactory, trustManager, cookieJar);
|
||||
|
||||
} catch (Exception e) {
|
||||
Timber.e(e, "Could not setup SSL system.");
|
||||
@ -132,6 +79,60 @@ public class HttpClient {
|
||||
return sOkHttpClient;
|
||||
}
|
||||
|
||||
private static SSLContext getSslContext() throws NoSuchAlgorithmException {
|
||||
try {
|
||||
return SSLContext.getInstance(TlsVersion.TLS_1_3.javaName());
|
||||
} catch (NoSuchAlgorithmException tlsv13Exception) {
|
||||
try {
|
||||
Timber.w("TLSv1.3 is not supported in this device; falling through TLSv1.2");
|
||||
return SSLContext.getInstance(TlsVersion.TLS_1_2.javaName());
|
||||
} catch (NoSuchAlgorithmException tlsv12Exception) {
|
||||
try {
|
||||
Timber.w("TLSv1.2 is not supported in this device; falling through TLSv1.1");
|
||||
return SSLContext.getInstance(TlsVersion.TLS_1_1.javaName());
|
||||
} catch (NoSuchAlgorithmException tlsv11Exception) {
|
||||
Timber.w("TLSv1.1 is not supported in this device; falling through TLSv1.0");
|
||||
return SSLContext.getInstance(TlsVersion.TLS_1_0.javaName());
|
||||
// should be available in any device; see reference of supported protocols in
|
||||
// http://developer.android.com/reference/javax/net/ssl/SSLSocket.html
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static SSLSocketFactory getNewSslSocketFactory(X509TrustManager trustManager)
|
||||
throws NoSuchAlgorithmException, KeyManagementException {
|
||||
final SSLContext sslContext = getSslContext();
|
||||
sslContext.init(null, new TrustManager[]{trustManager}, null);
|
||||
return sslContext.getSocketFactory();
|
||||
}
|
||||
|
||||
private static OkHttpClient buildNewOkHttpClient(SSLSocketFactory sslSocketFactory, X509TrustManager trustManager,
|
||||
CookieJar cookieJar) {
|
||||
return new OkHttpClient.Builder()
|
||||
.addNetworkInterceptor(getLogInterceptor())
|
||||
.protocols(Collections.singletonList(Protocol.HTTP_1_1))
|
||||
.readTimeout(HttpConstants.DEFAULT_DATA_TIMEOUT, TimeUnit.MILLISECONDS)
|
||||
.writeTimeout(HttpConstants.DEFAULT_DATA_TIMEOUT, TimeUnit.MILLISECONDS)
|
||||
.connectTimeout(HttpConstants.DEFAULT_CONNECTION_TIMEOUT, TimeUnit.MILLISECONDS)
|
||||
.followRedirects(false)
|
||||
.sslSocketFactory(sslSocketFactory, trustManager)
|
||||
.hostnameVerifier((asdf, usdf) -> true)
|
||||
.cookieJar(cookieJar)
|
||||
.build();
|
||||
}
|
||||
|
||||
public static LogInterceptor getLogInterceptor() {
|
||||
if (sLogInterceptor == null) {
|
||||
sLogInterceptor = new LogInterceptor();
|
||||
}
|
||||
return sLogInterceptor;
|
||||
}
|
||||
|
||||
public static List<Cookie> getCookiesFromUrl(HttpUrl httpUrl) {
|
||||
return sCookieStore.get(httpUrl.host());
|
||||
}
|
||||
|
||||
public Context getContext() {
|
||||
return sContext;
|
||||
}
|
||||
@ -140,17 +141,6 @@ public class HttpClient {
|
||||
sContext = context;
|
||||
}
|
||||
|
||||
public static LogInterceptor getLogInterceptor() {
|
||||
if (sLogInterceptor == null) {
|
||||
sLogInterceptor = new LogInterceptor();
|
||||
}
|
||||
return sLogInterceptor;
|
||||
}
|
||||
|
||||
public List<Cookie> getCookiesFromUrl(HttpUrl httpUrl) {
|
||||
return sCookieStore.get(httpUrl.host());
|
||||
}
|
||||
|
||||
public void clearCookies() {
|
||||
sCookieStore.clear();
|
||||
}
|
||||
|
@ -258,11 +258,6 @@ public abstract class RemoteOperation<T> implements Runnable {
|
||||
|
||||
final RemoteOperationResult resultToSend = runOperation();
|
||||
|
||||
if (mAccount != null && mContext != null) {
|
||||
// Save Client Cookies
|
||||
AccountUtils.saveClient(mClient, mAccount, mContext);
|
||||
}
|
||||
|
||||
if (mListenerHandler != null && mListener != null) {
|
||||
mListenerHandler.post(() ->
|
||||
mListener.onRemoteOperationFinish(RemoteOperation.this, resultToSend));
|
||||
|
@ -25,17 +25,14 @@ package com.owncloud.android.lib.resources.status
|
||||
|
||||
import android.net.Uri
|
||||
import com.owncloud.android.lib.common.OwnCloudClient
|
||||
import com.owncloud.android.lib.common.http.HttpConstants
|
||||
import com.owncloud.android.lib.common.http.methods.nonwebdav.GetMethod
|
||||
import com.owncloud.android.lib.common.operations.RemoteOperation
|
||||
import com.owncloud.android.lib.common.operations.RemoteOperationResult
|
||||
import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode
|
||||
import com.owncloud.android.lib.resources.status.HttpScheme.HTTPS_PREFIX
|
||||
import com.owncloud.android.lib.resources.status.HttpScheme.HTTP_PREFIX
|
||||
import com.owncloud.android.lib.resources.status.HttpScheme.HTTP_SCHEME
|
||||
import org.json.JSONException
|
||||
import org.json.JSONObject
|
||||
import timber.log.Timber
|
||||
import java.net.URL
|
||||
import java.util.concurrent.TimeUnit
|
||||
import javax.net.ssl.SSLException
|
||||
|
||||
/**
|
||||
* Checks if the server is valid
|
||||
@ -45,116 +42,44 @@ import javax.net.ssl.SSLException
|
||||
* @author David González Verdugo
|
||||
* @author Abel García de Prada
|
||||
*/
|
||||
class GetRemoteStatusOperation : RemoteOperation<OwnCloudVersion>() {
|
||||
private lateinit var latestResult: RemoteOperationResult<OwnCloudVersion>
|
||||
class GetRemoteStatusOperation : RemoteOperation<RemoteServerInfo>() {
|
||||
|
||||
override fun run(client: OwnCloudClient): RemoteOperationResult<OwnCloudVersion> {
|
||||
public override fun run(client: OwnCloudClient): RemoteOperationResult<RemoteServerInfo> {
|
||||
client.baseUri = buildFullHttpsUrl(client.baseUri)
|
||||
|
||||
val baseUriStr = client.baseUri.toString()
|
||||
if (baseUriStr.startsWith(HTTP_PREFIX) || baseUriStr.startsWith(
|
||||
HTTPS_PREFIX
|
||||
)) {
|
||||
tryConnection(client)
|
||||
} else {
|
||||
client.baseUri = Uri.parse(HTTPS_PREFIX + baseUriStr)
|
||||
val httpsSuccess = tryConnection(client)
|
||||
if (!httpsSuccess && !latestResult.isSslRecoverableException) {
|
||||
Timber.d("Establishing secure connection failed, trying non secure connection")
|
||||
client.baseUri = Uri.parse(HTTP_PREFIX + baseUriStr)
|
||||
tryConnection(client)
|
||||
}
|
||||
var result = tryToConnect(client)
|
||||
if (!(result.code == ResultCode.OK || result.code == ResultCode.OK_SSL) && !result.isSslRecoverableException) {
|
||||
Timber.d("Establishing secure connection failed, trying non secure connection")
|
||||
client.baseUri = client.baseUri.buildUpon().scheme(HTTP_SCHEME).build()
|
||||
result = tryToConnect(client)
|
||||
}
|
||||
return latestResult
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
private fun tryConnection(client: OwnCloudClient): Boolean {
|
||||
var successfulConnection = false
|
||||
val baseUrlSt = client.baseUri.toString()
|
||||
try {
|
||||
var getMethod = GetMethod(URL(baseUrlSt + OwnCloudClient.STATUS_PATH)).apply {
|
||||
setReadTimeout(TRY_CONNECTION_TIMEOUT, TimeUnit.SECONDS)
|
||||
setConnectionTimeout(TRY_CONNECTION_TIMEOUT, TimeUnit.SECONDS)
|
||||
}
|
||||
client.setFollowRedirects(false)
|
||||
var isRedirectToNonSecureConnection = false
|
||||
var status: Int
|
||||
try {
|
||||
status = client.executeHttpMethod(getMethod)
|
||||
latestResult =
|
||||
if (isSuccess(status)) RemoteOperationResult(ResultCode.OK)
|
||||
else RemoteOperationResult(getMethod)
|
||||
|
||||
} catch (sslE: SSLException) {
|
||||
latestResult = RemoteOperationResult(sslE)
|
||||
return successfulConnection
|
||||
}
|
||||
|
||||
var redirectedLocation = latestResult.redirectedLocation
|
||||
while (!redirectedLocation.isNullOrEmpty() && !latestResult.isSuccess) {
|
||||
isRedirectToNonSecureConnection =
|
||||
isRedirectToNonSecureConnection ||
|
||||
(baseUrlSt.startsWith(HTTPS_PREFIX) && redirectedLocation.startsWith(
|
||||
HTTP_PREFIX
|
||||
))
|
||||
|
||||
getMethod = GetMethod(URL(redirectedLocation)).apply {
|
||||
setReadTimeout(TRY_CONNECTION_TIMEOUT, TimeUnit.SECONDS)
|
||||
setConnectionTimeout(TRY_CONNECTION_TIMEOUT, TimeUnit.SECONDS)
|
||||
}
|
||||
|
||||
status = client.executeHttpMethod(getMethod)
|
||||
latestResult = RemoteOperationResult(getMethod)
|
||||
redirectedLocation = latestResult.redirectedLocation
|
||||
}
|
||||
|
||||
if (isSuccess(status)) {
|
||||
val respJSON = JSONObject(getMethod.getResponseBodyAsString())
|
||||
if (!respJSON.getBoolean(NODE_INSTALLED)) {
|
||||
latestResult = RemoteOperationResult(ResultCode.INSTANCE_NOT_CONFIGURED)
|
||||
} else {
|
||||
val version = respJSON.getString(NODE_VERSION)
|
||||
val ocVersion = OwnCloudVersion(version)
|
||||
// the version object will be returned even if the version is invalid, no error code;
|
||||
// every app will decide how to act if (ocVersion.isVersionValid() == false)
|
||||
latestResult = if (isRedirectToNonSecureConnection) {
|
||||
RemoteOperationResult(ResultCode.OK_REDIRECT_TO_NON_SECURE_CONNECTION)
|
||||
} else {
|
||||
if (baseUrlSt.startsWith(HTTPS_PREFIX)) RemoteOperationResult(ResultCode.OK_SSL)
|
||||
else RemoteOperationResult(ResultCode.OK_NO_SSL)
|
||||
}
|
||||
latestResult.data = ocVersion
|
||||
successfulConnection = true
|
||||
}
|
||||
} else {
|
||||
latestResult = RemoteOperationResult(getMethod)
|
||||
}
|
||||
private fun tryToConnect(client: OwnCloudClient): RemoteOperationResult<RemoteServerInfo> {
|
||||
val baseUrl = client.baseUri.toString()
|
||||
client.setFollowRedirects(false)
|
||||
return try {
|
||||
val requester = StatusRequester()
|
||||
val requestResult = requester.requestAndFollowRedirects(baseUrl, client)
|
||||
requester.handleRequestResult(requestResult, baseUrl)
|
||||
} catch (e: JSONException) {
|
||||
latestResult = RemoteOperationResult(ResultCode.INSTANCE_NOT_CONFIGURED)
|
||||
RemoteOperationResult(ResultCode.INSTANCE_NOT_CONFIGURED)
|
||||
} catch (e: Exception) {
|
||||
latestResult = RemoteOperationResult(e)
|
||||
RemoteOperationResult(e)
|
||||
}
|
||||
when {
|
||||
latestResult.isSuccess -> Timber.i("Connection check at $baseUrlSt successful: ${latestResult.logMessage}")
|
||||
|
||||
latestResult.isException ->
|
||||
Timber.e(latestResult.exception, "Connection check at $baseUrlSt: ${latestResult.logMessage}")
|
||||
|
||||
else -> Timber.e("Connection check at $baseUrlSt failed: ${latestResult.logMessage}")
|
||||
}
|
||||
return successfulConnection
|
||||
}
|
||||
|
||||
private fun isSuccess(status: Int): Boolean = status == HttpConstants.HTTP_OK
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Maximum time to wait for a response from the server when the connection is being tested,
|
||||
* in MILLISECONDs.
|
||||
*/
|
||||
private const val TRY_CONNECTION_TIMEOUT: Long = 5000
|
||||
private const val NODE_INSTALLED = "installed"
|
||||
private const val NODE_VERSION = "version"
|
||||
private const val HTTPS_PREFIX = "https://"
|
||||
private const val HTTP_PREFIX = "http://"
|
||||
fun usesHttpOrHttps(uri: Uri) =
|
||||
uri.toString().startsWith(HTTPS_PREFIX) || uri.toString().startsWith(HTTP_PREFIX)
|
||||
|
||||
fun buildFullHttpsUrl(baseUri: Uri): Uri {
|
||||
if (usesHttpOrHttps(baseUri)) {
|
||||
return baseUri
|
||||
}
|
||||
return Uri.parse("$HTTPS_PREFIX$baseUri")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,32 @@
|
||||
/* ownCloud Android Library is available under MIT license
|
||||
* Copyright (C) 2021 ownCloud GmbH.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.owncloud.android.lib.resources.status
|
||||
|
||||
object HttpScheme {
|
||||
const val HTTP_SCHEME = "http"
|
||||
const val HTTPS_SCHEME = "https"
|
||||
const val HTTP_PREFIX = "$HTTP_SCHEME://"
|
||||
const val HTTPS_PREFIX = "$HTTPS_SCHEME://"
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
/* ownCloud Android Library is available under MIT license
|
||||
* Copyright (C) 2021 ownCloud GmbH.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
package com.owncloud.android.lib.resources.status
|
||||
|
||||
data class RemoteServerInfo(
|
||||
val ownCloudVersion: OwnCloudVersion,
|
||||
val baseUrl: String,
|
||||
val isSecureConnection: Boolean
|
||||
)
|
@ -0,0 +1,156 @@
|
||||
/* ownCloud Android Library is available under MIT license
|
||||
* Copyright (C) 2021 ownCloud GmbH.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.owncloud.android.lib.resources.status
|
||||
|
||||
import com.owncloud.android.lib.common.OwnCloudClient
|
||||
import com.owncloud.android.lib.common.http.HttpConstants
|
||||
import com.owncloud.android.lib.common.http.methods.nonwebdav.GetMethod
|
||||
import com.owncloud.android.lib.common.operations.RemoteOperationResult
|
||||
import com.owncloud.android.lib.resources.status.HttpScheme.HTTPS_SCHEME
|
||||
import org.json.JSONObject
|
||||
import java.net.URL
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
internal class StatusRequester {
|
||||
|
||||
/**
|
||||
* This function is ment to detect if a redirect from a secure to an unsecure connection
|
||||
* was made. If only connections from unsecure connections to unsecure connections were made
|
||||
* this function should not return true, because if the whole redirect chain was unsecure
|
||||
* we assume it was a debug setup.
|
||||
*/
|
||||
fun isRedirectedToNonSecureConnection(
|
||||
redirectedToNonSecureLocationBefore: Boolean,
|
||||
baseUrl: String,
|
||||
redirectedUrl: String
|
||||
) = redirectedToNonSecureLocationBefore
|
||||
|| (baseUrl.startsWith(HTTPS_SCHEME)
|
||||
&& !redirectedUrl.startsWith(HTTPS_SCHEME))
|
||||
|
||||
fun updateLocationWithRedirectPath(oldLocation: String, redirectedLocation: String): String {
|
||||
/** Redirection with different endpoint.
|
||||
* When asking for server.com/status.php and redirected to different.one/, we need to ask different.one/status.php
|
||||
*/
|
||||
if (redirectedLocation.endsWith('/')) {
|
||||
return redirectedLocation.trimEnd('/') + OwnCloudClient.STATUS_PATH
|
||||
}
|
||||
|
||||
if (!redirectedLocation.startsWith("/"))
|
||||
return redirectedLocation
|
||||
val oldLocationURL = URL(oldLocation)
|
||||
return URL(oldLocationURL.protocol, oldLocationURL.host, oldLocationURL.port, redirectedLocation).toString()
|
||||
}
|
||||
|
||||
private fun getGetMethod(url: String): GetMethod {
|
||||
return GetMethod(URL(url)).apply {
|
||||
setReadTimeout(TRY_CONNECTION_TIMEOUT, TimeUnit.SECONDS)
|
||||
setConnectionTimeout(TRY_CONNECTION_TIMEOUT, TimeUnit.SECONDS)
|
||||
}
|
||||
}
|
||||
|
||||
data class RequestResult(
|
||||
val getMethod: GetMethod,
|
||||
val status: Int,
|
||||
val redirectedToUnsecureLocation: Boolean,
|
||||
val lastLocation: String
|
||||
)
|
||||
|
||||
fun requestAndFollowRedirects(baseLocation: String, client: OwnCloudClient): RequestResult {
|
||||
var currentLocation = baseLocation + OwnCloudClient.STATUS_PATH
|
||||
var redirectedToUnsecureLocation = false
|
||||
var status: Int
|
||||
|
||||
while (true) {
|
||||
val getMethod = getGetMethod(currentLocation)
|
||||
|
||||
status = client.executeHttpMethod(getMethod)
|
||||
val result =
|
||||
if (status.isSuccess()) RemoteOperationResult<OwnCloudVersion>(RemoteOperationResult.ResultCode.OK)
|
||||
else RemoteOperationResult(getMethod)
|
||||
|
||||
if (result.redirectedLocation.isNullOrEmpty() || result.isSuccess) {
|
||||
return RequestResult(getMethod, status, redirectedToUnsecureLocation, currentLocation)
|
||||
} else {
|
||||
val nextLocation = updateLocationWithRedirectPath(currentLocation, result.redirectedLocation)
|
||||
redirectedToUnsecureLocation =
|
||||
isRedirectedToNonSecureConnection(
|
||||
redirectedToUnsecureLocation,
|
||||
currentLocation,
|
||||
nextLocation
|
||||
)
|
||||
currentLocation = nextLocation
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun Int.isSuccess() = this == HttpConstants.HTTP_OK
|
||||
|
||||
fun handleRequestResult(
|
||||
requestResult: RequestResult,
|
||||
baseUrl: String
|
||||
): RemoteOperationResult<RemoteServerInfo> {
|
||||
if (!requestResult.status.isSuccess())
|
||||
return RemoteOperationResult(requestResult.getMethod)
|
||||
|
||||
val respJSON = JSONObject(requestResult.getMethod.getResponseBodyAsString() ?: "")
|
||||
if (!respJSON.getBoolean(NODE_INSTALLED))
|
||||
return RemoteOperationResult(RemoteOperationResult.ResultCode.INSTANCE_NOT_CONFIGURED)
|
||||
|
||||
val ocVersion = OwnCloudVersion(respJSON.getString(NODE_VERSION))
|
||||
// the version object will be returned even if the version is invalid, no error code;
|
||||
// every app will decide how to act if (ocVersion.isVersionValid() == false)
|
||||
val result: RemoteOperationResult<RemoteServerInfo> =
|
||||
if (requestResult.redirectedToUnsecureLocation) {
|
||||
RemoteOperationResult(RemoteOperationResult.ResultCode.OK_REDIRECT_TO_NON_SECURE_CONNECTION)
|
||||
} else {
|
||||
if (baseUrl.startsWith(HTTPS_SCHEME)) RemoteOperationResult(RemoteOperationResult.ResultCode.OK_SSL)
|
||||
else RemoteOperationResult(RemoteOperationResult.ResultCode.OK_NO_SSL)
|
||||
}
|
||||
val finalUrl = URL(requestResult.lastLocation)
|
||||
val finalBaseUrl = URL(
|
||||
finalUrl.protocol,
|
||||
finalUrl.host,
|
||||
finalUrl.port,
|
||||
finalUrl.file.dropLastWhile { it != '/' }.trimEnd('/')
|
||||
)
|
||||
|
||||
result.data = RemoteServerInfo(
|
||||
ownCloudVersion = ocVersion,
|
||||
baseUrl = finalBaseUrl.toString(),
|
||||
isSecureConnection = finalBaseUrl.protocol.startsWith(HTTPS_SCHEME)
|
||||
)
|
||||
return result
|
||||
}
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Maximum time to wait for a response from the server when the connection is being tested,
|
||||
* in milliseconds.
|
||||
*/
|
||||
private const val TRY_CONNECTION_TIMEOUT = 5_000L
|
||||
private const val NODE_INSTALLED = "installed"
|
||||
private const val NODE_VERSION = "version"
|
||||
}
|
||||
}
|
@ -25,10 +25,10 @@ package com.owncloud.android.lib.resources.status.services
|
||||
|
||||
import com.owncloud.android.lib.common.OwnCloudClient
|
||||
import com.owncloud.android.lib.common.operations.RemoteOperationResult
|
||||
import com.owncloud.android.lib.resources.status.OwnCloudVersion
|
||||
import com.owncloud.android.lib.resources.status.RemoteServerInfo
|
||||
|
||||
interface ServerInfoService {
|
||||
fun checkPathExistence(path: String, isUserLogged: Boolean, client: OwnCloudClient): RemoteOperationResult<Boolean>
|
||||
|
||||
fun getRemoteStatus(path: String, client: OwnCloudClient): RemoteOperationResult<OwnCloudVersion>
|
||||
fun getRemoteStatus(path: String, client: OwnCloudClient): RemoteOperationResult<RemoteServerInfo>
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ import com.owncloud.android.lib.common.OwnCloudClient
|
||||
import com.owncloud.android.lib.common.operations.RemoteOperationResult
|
||||
import com.owncloud.android.lib.resources.files.CheckPathExistenceRemoteOperation
|
||||
import com.owncloud.android.lib.resources.status.GetRemoteStatusOperation
|
||||
import com.owncloud.android.lib.resources.status.OwnCloudVersion
|
||||
import com.owncloud.android.lib.resources.status.RemoteServerInfo
|
||||
import com.owncloud.android.lib.resources.status.services.ServerInfoService
|
||||
|
||||
class OCServerInfoService : ServerInfoService {
|
||||
@ -41,6 +41,6 @@ class OCServerInfoService : ServerInfoService {
|
||||
override fun getRemoteStatus(
|
||||
path: String,
|
||||
client: OwnCloudClient
|
||||
): RemoteOperationResult<OwnCloudVersion> =
|
||||
): RemoteOperationResult<RemoteServerInfo> =
|
||||
GetRemoteStatusOperation().execute(client)
|
||||
}
|
||||
|
@ -0,0 +1,84 @@
|
||||
/* ownCloud Android Library is available under MIT license
|
||||
* Copyright (C) 2021 ownCloud GmbH.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
package com.owncloud.android.lib
|
||||
|
||||
import com.owncloud.android.lib.common.http.CookieJarImpl
|
||||
import okhttp3.Cookie
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Test
|
||||
|
||||
class CookieJarImplTest {
|
||||
|
||||
private val oldCookies = listOf(COOKIE_A, COOKIE_B_OLD)
|
||||
private val newCookies = listOf(COOKIE_B_NEW)
|
||||
private val updatedCookies = listOf(COOKIE_A, COOKIE_B_NEW)
|
||||
private val cookieStore = hashMapOf(SOME_HOST to oldCookies)
|
||||
|
||||
private val cookieJarImpl = CookieJarImpl(cookieStore)
|
||||
|
||||
@Test
|
||||
fun `contains cookie with name - ok - true`() {
|
||||
assertTrue(cookieJarImpl.containsCookieWithName(oldCookies, COOKIE_B_OLD.name))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `contains cookie with name - ok - false`() {
|
||||
assertFalse(cookieJarImpl.containsCookieWithName(newCookies, COOKIE_A.name))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `get updated cookies - ok`() {
|
||||
val generatedUpdatedCookies = cookieJarImpl.getUpdatedCookies(oldCookies, newCookies)
|
||||
assertEquals(2, generatedUpdatedCookies.size)
|
||||
assertEquals(updatedCookies[0], generatedUpdatedCookies[1])
|
||||
assertEquals(updatedCookies[1], generatedUpdatedCookies[0])
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `store cookie via saveFromResponse - ok`() {
|
||||
cookieJarImpl.saveFromResponse(SOME_URL, newCookies)
|
||||
val generatedUpdatedCookies = cookieStore[SOME_HOST]
|
||||
assertEquals(2, generatedUpdatedCookies?.size)
|
||||
assertEquals(updatedCookies[0], generatedUpdatedCookies?.get(1))
|
||||
assertEquals(updatedCookies[1], generatedUpdatedCookies?.get(0))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `load for request - ok`() {
|
||||
val cookies = cookieJarImpl.loadForRequest(SOME_URL)
|
||||
assertEquals(oldCookies[0], cookies[0])
|
||||
assertEquals(oldCookies[1], cookies[1])
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val SOME_HOST = "some.host.com"
|
||||
val SOME_URL = "https://$SOME_HOST".toHttpUrl()
|
||||
val COOKIE_A = Cookie.parse(SOME_URL, "CookieA=CookieValueA")!!
|
||||
val COOKIE_B_OLD = Cookie.parse(SOME_URL, "CookieB=CookieOldValueB")!!
|
||||
val COOKIE_B_NEW = Cookie.parse(SOME_URL, "CookieB=CookieNewValueB")!!
|
||||
}
|
||||
}
|
@ -0,0 +1,145 @@
|
||||
/* ownCloud Android Library is available under MIT license
|
||||
* Copyright (C) 2021 ownCloud GmbH.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
package com.owncloud.android.lib
|
||||
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import com.owncloud.android.lib.resources.status.GetRemoteStatusOperation
|
||||
import com.owncloud.android.lib.resources.status.HttpScheme.HTTPS_PREFIX
|
||||
import com.owncloud.android.lib.resources.status.HttpScheme.HTTP_PREFIX
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.robolectric.RobolectricTestRunner
|
||||
import org.robolectric.annotation.Config
|
||||
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
@Config(sdk = [Build.VERSION_CODES.O], manifest = Config.NONE)
|
||||
class GetRemoteStatusOperationTest {
|
||||
|
||||
@Test
|
||||
fun `uses http or https - ok - http`() {
|
||||
assertTrue(GetRemoteStatusOperation.usesHttpOrHttps(Uri.parse(HTTP_SOME_OWNCLOUD)))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `uses http or https - ok - https`() {
|
||||
assertTrue(GetRemoteStatusOperation.usesHttpOrHttps(Uri.parse(HTTPS_SOME_OWNCLOUD)))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `uses http or https - ok - no http or https`() {
|
||||
assertFalse(GetRemoteStatusOperation.usesHttpOrHttps(Uri.parse(SOME_OWNCLOUD)))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `build full https url - ok - http`() {
|
||||
assertEquals(
|
||||
Uri.parse(HTTP_SOME_OWNCLOUD),
|
||||
GetRemoteStatusOperation.buildFullHttpsUrl(Uri.parse(HTTP_SOME_OWNCLOUD))
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `build full https url - ok - https`() {
|
||||
assertEquals(
|
||||
Uri.parse(HTTPS_SOME_OWNCLOUD),
|
||||
GetRemoteStatusOperation.buildFullHttpsUrl(Uri.parse(HTTPS_SOME_OWNCLOUD))
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `build full https url - ok - no prefix`() {
|
||||
assertEquals(
|
||||
Uri.parse(HTTPS_SOME_OWNCLOUD),
|
||||
GetRemoteStatusOperation.buildFullHttpsUrl(Uri.parse(SOME_OWNCLOUD))
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `build full https url - ok - no https with subdir`() {
|
||||
assertEquals(
|
||||
Uri.parse(HTTPS_SOME_OWNCLOUD_WITH_SUBDIR),
|
||||
GetRemoteStatusOperation.buildFullHttpsUrl(
|
||||
Uri.parse(HTTPS_SOME_OWNCLOUD_WITH_SUBDIR)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `build full https url - ok - no prefix with subdir`() {
|
||||
assertEquals(
|
||||
Uri.parse(HTTPS_SOME_OWNCLOUD_WITH_SUBDIR),
|
||||
GetRemoteStatusOperation.buildFullHttpsUrl(
|
||||
Uri.parse(SOME_OWNCLOUD_WITH_SUBDIR)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `build full https url - ok - ip`() {
|
||||
assertEquals(Uri.parse(HTTPS_SOME_IP), GetRemoteStatusOperation.buildFullHttpsUrl(Uri.parse(SOME_IP)))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `build full https url - ok - http ip`() {
|
||||
assertEquals(Uri.parse(HTTP_SOME_IP), GetRemoteStatusOperation.buildFullHttpsUrl(Uri.parse(HTTP_SOME_IP)))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `build full https url - ok - ip with port`() {
|
||||
assertEquals(
|
||||
Uri.parse(HTTPS_SOME_IP_WITH_PORT),
|
||||
GetRemoteStatusOperation.buildFullHttpsUrl(Uri.parse(SOME_IP_WITH_PORT))
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `build full https url - ok - ip with http and port`() {
|
||||
assertEquals(
|
||||
Uri.parse(HTTP_SOME_IP_WITH_PORT),
|
||||
GetRemoteStatusOperation.buildFullHttpsUrl(Uri.parse(HTTP_SOME_IP_WITH_PORT))
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val SOME_OWNCLOUD = "some_owncloud.com"
|
||||
const val HTTP_SOME_OWNCLOUD = "$HTTP_PREFIX$SOME_OWNCLOUD"
|
||||
const val HTTPS_SOME_OWNCLOUD = "$HTTPS_PREFIX$SOME_OWNCLOUD"
|
||||
|
||||
const val SOME_OWNCLOUD_WITH_SUBDIR = "some_owncloud.com/subdir"
|
||||
const val HTTP_SOME_OWNCLOUD_WITH_SUBDIR = "$HTTP_PREFIX$SOME_OWNCLOUD_WITH_SUBDIR"
|
||||
const val HTTPS_SOME_OWNCLOUD_WITH_SUBDIR = "$HTTPS_PREFIX$SOME_OWNCLOUD_WITH_SUBDIR"
|
||||
|
||||
const val SOME_IP = "184.123.185.12"
|
||||
const val HTTP_SOME_IP = "$HTTP_PREFIX$SOME_IP"
|
||||
const val HTTPS_SOME_IP = "$HTTPS_PREFIX$SOME_IP"
|
||||
|
||||
const val SOME_IP_WITH_PORT = "184.123.185.12:5678"
|
||||
const val HTTP_SOME_IP_WITH_PORT = "$HTTP_PREFIX$SOME_IP_WITH_PORT"
|
||||
const val HTTPS_SOME_IP_WITH_PORT = "$HTTPS_PREFIX$SOME_IP_WITH_PORT"
|
||||
}
|
||||
}
|
@ -0,0 +1,104 @@
|
||||
/* ownCloud Android Library is available under MIT license
|
||||
* Copyright (C) 2021 ownCloud GmbH.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.owncloud.android.lib
|
||||
|
||||
import com.owncloud.android.lib.resources.status.StatusRequester
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Test
|
||||
|
||||
class StatusRequesterTest {
|
||||
private val requester = StatusRequester()
|
||||
|
||||
@Test
|
||||
fun `update location - ok - absolute path`() {
|
||||
val newLocation = requester.updateLocationWithRedirectPath(TEST_DOMAIN, "$TEST_DOMAIN$SUB_PATH")
|
||||
assertEquals("$TEST_DOMAIN$SUB_PATH", newLocation)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `update location - ok - smaller absolute path`() {
|
||||
val newLocation = requester.updateLocationWithRedirectPath("$TEST_DOMAIN$SUB_PATH", TEST_DOMAIN)
|
||||
assertEquals(TEST_DOMAIN, newLocation)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `update location - ok - relative path`() {
|
||||
val newLocation = requester.updateLocationWithRedirectPath(TEST_DOMAIN, SUB_PATH)
|
||||
assertEquals("$TEST_DOMAIN$SUB_PATH", newLocation)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `update location - ok - replace relative path`() {
|
||||
val newLocation = requester.updateLocationWithRedirectPath("$TEST_DOMAIN/some/other/subdir", SUB_PATH)
|
||||
assertEquals("$TEST_DOMAIN$SUB_PATH", newLocation)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `check redirect to unsecure connection - ok - redirect to http`() {
|
||||
assertTrue(
|
||||
requester.isRedirectedToNonSecureConnection(false, SECURE_DOMAIN, UNSECURE_DOMAIN
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `check redirect to unsecure connection - ko - redirect to https from http`() {
|
||||
assertFalse(
|
||||
requester.isRedirectedToNonSecureConnection(false, UNSECURE_DOMAIN, SECURE_DOMAIN
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `check redirect to unsecure connection - ko - from https to https`() {
|
||||
assertFalse(
|
||||
requester.isRedirectedToNonSecureConnection(false, SECURE_DOMAIN, SECURE_DOMAIN)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `check redirect to unsecure connection - ok - from https to https with previous http`() {
|
||||
assertTrue(
|
||||
requester.isRedirectedToNonSecureConnection(true, SECURE_DOMAIN, SECURE_DOMAIN)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `check redirect to unsecure connection - ok - from http to http`() {
|
||||
assertFalse(
|
||||
requester.isRedirectedToNonSecureConnection(false, UNSECURE_DOMAIN, UNSECURE_DOMAIN)
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val TEST_DOMAIN = "https://cloud.somewhere.com"
|
||||
const val SUB_PATH = "/subdir"
|
||||
|
||||
const val SECURE_DOMAIN = "https://cloud.somewhere.com"
|
||||
const val UNSECURE_DOMAIN = "http://somewhereelse.org"
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user