1
0
mirror of https://github.com/owncloud/android-library.git synced 2025-06-07 16:06:08 +00:00

add update authToken code to connectionValidator

This commit is contained in:
Christian Schabesberger 2021-09-14 14:36:49 +02:00
parent cfd69987e9
commit e27a968ddb
3 changed files with 112 additions and 19 deletions

View File

@ -1,31 +1,37 @@
package com.owncloud.android.lib.common package com.owncloud.android.lib.common
import android.accounts.AccountManager
import android.accounts.AccountsException
import android.content.Context
import com.owncloud.android.lib.common.authentication.OwnCloudCredentials import com.owncloud.android.lib.common.authentication.OwnCloudCredentials
import com.owncloud.android.lib.common.authentication.OwnCloudCredentialsFactory import com.owncloud.android.lib.common.authentication.OwnCloudCredentialsFactory
import com.owncloud.android.lib.common.authentication.OwnCloudCredentialsFactory.OwnCloudAnonymousCredentials
import com.owncloud.android.lib.common.http.HttpConstants import com.owncloud.android.lib.common.http.HttpConstants
import com.owncloud.android.lib.common.http.methods.HttpBaseMethod
import com.owncloud.android.lib.common.operations.RemoteOperationResult import com.owncloud.android.lib.common.operations.RemoteOperationResult
import com.owncloud.android.lib.resources.files.CheckPathExistenceRemoteOperation import com.owncloud.android.lib.resources.files.CheckPathExistenceRemoteOperation
import com.owncloud.android.lib.resources.status.GetRemoteStatusOperation import com.owncloud.android.lib.resources.status.GetRemoteStatusOperation
import com.owncloud.android.lib.resources.status.RemoteServerInfo import com.owncloud.android.lib.resources.status.RemoteServerInfo
import org.apache.commons.lang3.exception.ExceptionUtils import org.apache.commons.lang3.exception.ExceptionUtils
import timber.log.Timber import timber.log.Timber
import java.io.IOException
import java.lang.Exception import java.lang.Exception
class ConnectionValidator ( class ConnectionValidator (
val context: Context,
val clearCookiesOnValidation: Boolean val clearCookiesOnValidation: Boolean
){ ){
fun validate(baseClient: OwnCloudClient): Boolean { fun validate(baseClient: OwnCloudClient, singleSessionManager: SingleSessionManager): Boolean {
try { try {
var validationRetryCount = 0 var validationRetryCount = 0
val client = OwnCloudClient(baseClient.baseUri, null, false) val client = OwnCloudClient(baseClient.baseUri, null, false, singleSessionManager)
if (clearCookiesOnValidation) { if (clearCookiesOnValidation) {
client.clearCookies(); client.clearCookies();
} else { } else {
client.cookiesForBaseUri = baseClient.cookiesForBaseUri client.cookiesForBaseUri = baseClient.cookiesForBaseUri
} }
client.account = baseClient.account
client.credentials = baseClient.credentials client.credentials = baseClient.credentials
while (validationRetryCount < 5) { while (validationRetryCount < 5) {
Timber.d("+++++++++++++++++++++++++++++++++++++ validationRetryCout %d", validationRetryCount) Timber.d("+++++++++++++++++++++++++++++++++++++ validationRetryCout %d", validationRetryCount)
@ -40,7 +46,7 @@ class ConnectionValidator (
} }
// Skip the part where we try to check if we can access the parts where we have to be logged in... if we are not logged in // Skip the part where we try to check if we can access the parts where we have to be logged in... if we are not logged in
if(baseClient.credentials !is OwnCloudCredentialsFactory.OwnCloudAnonymousCredentials) { if(baseClient.credentials !is OwnCloudAnonymousCredentials) {
client.setFollowRedirects(false) client.setFollowRedirects(false)
val contentReply = canAccessRootFolder(client) val contentReply = canAccessRootFolder(client)
if (contentReply.httpCode == HttpConstants.HTTP_OK) { if (contentReply.httpCode == HttpConstants.HTTP_OK) {
@ -51,8 +57,8 @@ class ConnectionValidator (
} }
} else { } else {
failCounter++ failCounter++
if (contentReply.hashCode() == HttpConstants.HTTP_UNAUTHORIZED) { if (contentReply.httpCode == HttpConstants.HTTP_UNAUTHORIZED) {
triggerAuthRefresh() checkUnauthorizedAccess(client, singleSessionManager, contentReply.httpCode)
} }
} }
} }
@ -92,4 +98,89 @@ class ConnectionValidator (
val checkPathExistenceRemoteOperation = CheckPathExistenceRemoteOperation("/", true) val checkPathExistenceRemoteOperation = CheckPathExistenceRemoteOperation("/", true)
return checkPathExistenceRemoteOperation.execute(client) return checkPathExistenceRemoteOperation.execute(client)
} }
/**
* Determines if credentials should be invalidated according the to the HTTPS status
* of a network request just performed.
*
* @param httpStatusCode Result of the last request ran with the 'credentials' belows.
* @return 'True' if credentials should and might be invalidated, 'false' if shouldn't or
* cannot be invalidated with the given arguments.
*/
private fun shouldInvalidateAccountCredentials(credentials: OwnCloudCredentials, account: OwnCloudAccount, httpStatusCode: Int): Boolean {
var shouldInvalidateAccountCredentials = httpStatusCode == HttpConstants.HTTP_UNAUTHORIZED
shouldInvalidateAccountCredentials = shouldInvalidateAccountCredentials and // real credentials
(credentials !is OwnCloudAnonymousCredentials)
// test if have all the needed to effectively invalidate ...
shouldInvalidateAccountCredentials =
shouldInvalidateAccountCredentials and (account.savedAccount != null)
return shouldInvalidateAccountCredentials
}
/**
* Invalidates credentials stored for the given account in the system [AccountManager] and in
* current [SingleSessionManager.getDefaultSingleton] instance.
*
*
* [.shouldInvalidateAccountCredentials] should be called first.
*
*/
private fun invalidateAccountCredentials(account: OwnCloudAccount, credentials: OwnCloudCredentials) {
val am = AccountManager.get(context)
am.invalidateAuthToken(
account.savedAccount.type,
credentials.authToken
)
am.clearPassword(account.savedAccount) // being strict, only needed for Basic Auth credentials
}
/**
* Checks the status code of an execution and decides if should be repeated with fresh credentials.
*
*
* Invalidates current credentials if the request failed as anauthorized.
*
*
* Refresh current credentials if possible, and marks a retry.
*
* @param status
* @param repeatCounter
* @return
*/
private fun checkUnauthorizedAccess(client: OwnCloudClient, singleSessionManager: SingleSessionManager, status: Int): Boolean {
var credentialsWereRefreshed = false
val account = client.account
val credentials = account.credentials
if (shouldInvalidateAccountCredentials(credentials, account, status)) {
invalidateAccountCredentials(account, credentials)
if (credentials.authTokenCanBeRefreshed()) {
try {
account.loadCredentials(context)
// if mAccount.getCredentials().length() == 0 --> refresh failed
client.credentials = account.credentials
credentialsWereRefreshed = true
} catch (e: AccountsException) {
Timber.e(
e, "Error while trying to refresh auth token for %s\ntrace: %s",
account.savedAccount.name,
ExceptionUtils.getStackTrace(e)
)
} catch (e: IOException) {
Timber.e(
e, "Error while trying to refresh auth token for %s\ntrace: %s",
account.savedAccount.name,
ExceptionUtils.getStackTrace(e)
)
}
if (!credentialsWereRefreshed) {
// if credentials are not refreshed, client must be removed
// from the OwnCloudClientManager to prevent it is reused once and again
singleSessionManager.removeClientFor(account)
}
}
// else: onExecute will finish with status 401
}
return credentialsWereRefreshed
}
} }

View File

@ -59,7 +59,6 @@ public class OwnCloudClient extends HttpClient {
public static final String STATUS_PATH = "/status.php"; public static final String STATUS_PATH = "/status.php";
private static final String WEBDAV_UPLOADS_PATH_4_0 = "/remote.php/dav/uploads/"; private static final String WEBDAV_UPLOADS_PATH_4_0 = "/remote.php/dav/uploads/";
private static final int MAX_REDIRECTIONS_COUNT = 5; private static final int MAX_REDIRECTIONS_COUNT = 5;
private static final int MAX_REPEAT_COUNT_WITH_FRESH_CREDENTIALS = 1;
private static int sIntanceCounter = 0; private static int sIntanceCounter = 0;
private OwnCloudCredentials mCredentials = null; private OwnCloudCredentials mCredentials = null;
@ -80,12 +79,13 @@ public class OwnCloudClient extends HttpClient {
private boolean mFollowRedirects = false; private boolean mFollowRedirects = false;
public OwnCloudClient(Uri baseUri, ConnectionValidator connectionValidator, boolean synchronizeRequests) { public OwnCloudClient(Uri baseUri, ConnectionValidator connectionValidator, boolean synchronizeRequests, SingleSessionManager singleSessionManager) {
if (baseUri == null) { if (baseUri == null) {
throw new IllegalArgumentException("Parameter 'baseUri' cannot be NULL"); throw new IllegalArgumentException("Parameter 'baseUri' cannot be NULL");
} }
mBaseUri = baseUri; mBaseUri = baseUri;
mSynchronizeRequests = synchronizeRequests; mSynchronizeRequests = synchronizeRequests;
mSingleSessionManager = singleSessionManager;
mInstanceNumber = sIntanceCounter++; mInstanceNumber = sIntanceCounter++;
Timber.d("#" + mInstanceNumber + "Creating OwnCloudClient"); Timber.d("#" + mInstanceNumber + "Creating OwnCloudClient");
@ -134,8 +134,10 @@ public class OwnCloudClient extends HttpClient {
stacklog(status, method); stacklog(status, method);
if (mConnectionValidator != null && if (mConnectionValidator != null &&
status == HttpConstants.HTTP_MOVED_TEMPORARILY) { (status == HttpConstants.HTTP_MOVED_TEMPORARILY ||
retry = mConnectionValidator.validate(this); // retry on success fail on no success (!(mCredentials instanceof OwnCloudAnonymousCredentials) &&
status == HttpConstants.HTTP_UNAUTHORIZED))) {
retry = mConnectionValidator.validate(this, mSingleSessionManager); // retry on success fail on no success
} else if (mFollowRedirects) { } else if (mFollowRedirects) {
status = followRedirection(method).getLastStatus(); status = followRedirection(method).getLastStatus();
} }
@ -145,8 +147,8 @@ public class OwnCloudClient extends HttpClient {
if (repeatWithFreshCredentials) { if (repeatWithFreshCredentials) {
repeatCounter++; repeatCounter++;
} }
*/ */
} while (retry); } while (retry);
return status; return status;
@ -163,6 +165,7 @@ public class OwnCloudClient extends HttpClient {
"\nMethod: " + method.toString() + "\nMethod: " + method.toString() +
"\nUrl: " + method.getHttpUrl() + "\nUrl: " + method.getHttpUrl() +
"\nCookeis: " + getCookiesForBaseUri().toString() + "\nCookeis: " + getCookiesForBaseUri().toString() +
"\nCredentials type: " + mCredentials.getClass().toString() +
"\ntrace: " + ExceptionUtils.getStackTrace(e) + "\ntrace: " + ExceptionUtils.getStackTrace(e) +
"---------------------------"); "---------------------------");
} }
@ -336,8 +339,8 @@ public class OwnCloudClient extends HttpClient {
} }
public List<Cookie> getCookiesForBaseUri() { public List<Cookie> getCookiesForBaseUri() {
return getOkHttpClient().cookieJar().loadForRequest( return getOkHttpClient().cookieJar().loadForRequest(
HttpUrl.parse(mBaseUri.toString())); HttpUrl.parse(mBaseUri.toString()));
} }
public OwnCloudVersion getOwnCloudVersion() { public OwnCloudVersion getOwnCloudVersion() {
@ -374,7 +377,7 @@ public class OwnCloudClient extends HttpClient {
invalidateAccountCredentials(); invalidateAccountCredentials();
if (getCredentials().authTokenCanBeRefreshed() && if (getCredentials().authTokenCanBeRefreshed() &&
repeatCounter < MAX_REPEAT_COUNT_WITH_FRESH_CREDENTIALS) { repeatCounter < 1) {
try { try {
mAccount.loadCredentials(getContext()); mAccount.loadCredentials(getContext());
// if mAccount.getCredentials().length() == 0 --> refresh failed // if mAccount.getCredentials().length() == 0 --> refresh failed

View File

@ -77,9 +77,8 @@ public class SingleSessionManager {
sUserAgent = userAgent; sUserAgent = userAgent;
} }
private static OwnCloudClient createOwnCloudClient(Uri uri, Context context, boolean followRedirects, ConnectionValidator connectionValidator) { private static OwnCloudClient createOwnCloudClient(Uri uri, Context context, ConnectionValidator connectionValidator, SingleSessionManager singleSessionManager) {
OwnCloudClient client = new OwnCloudClient(uri, connectionValidator, true); OwnCloudClient client = new OwnCloudClient(uri, connectionValidator, true, singleSessionManager);
client.setFollowRedirects(followRedirects);
HttpClient.setContext(context); HttpClient.setContext(context);
return client; return client;
@ -132,8 +131,8 @@ public class SingleSessionManager {
client = createOwnCloudClient( client = createOwnCloudClient(
account.getBaseUri(), account.getBaseUri(),
context.getApplicationContext(), context.getApplicationContext(),
true, connectionValidator,
connectionValidator); // TODO remove dependency on OwnCloudClientFactory this);
//the next two lines are a hack because okHttpclient is used as a singleton instead of being an //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 //injected instance that can be deleted when required