1
1
package com.owncloud.android.lib.common
2
2
3
+ import android.accounts.AccountManager
4
+ import android.accounts.AccountsException
5
+ import android.content.Context
3
6
import com.owncloud.android.lib.common.authentication.OwnCloudCredentials
4
7
import com.owncloud.android.lib.common.authentication.OwnCloudCredentialsFactory
8
+ import com.owncloud.android.lib.common.authentication.OwnCloudCredentialsFactory.OwnCloudAnonymousCredentials
5
9
import com.owncloud.android.lib.common.http.HttpConstants
6
- import com.owncloud.android.lib.common.http.methods.HttpBaseMethod
7
10
import com.owncloud.android.lib.common.operations.RemoteOperationResult
8
11
import com.owncloud.android.lib.resources.files.CheckPathExistenceRemoteOperation
9
12
import com.owncloud.android.lib.resources.status.GetRemoteStatusOperation
10
13
import com.owncloud.android.lib.resources.status.RemoteServerInfo
11
14
import org.apache.commons.lang3.exception.ExceptionUtils
12
15
import timber.log.Timber
16
+ import java.io.IOException
13
17
import java.lang.Exception
14
18
15
19
class ConnectionValidator (
20
+ val context : Context ,
16
21
val clearCookiesOnValidation : Boolean
17
22
){
18
23
19
- fun validate (baseClient : OwnCloudClient ): Boolean {
24
+ fun validate (baseClient : OwnCloudClient , singleSessionManager : SingleSessionManager ): Boolean {
20
25
try {
21
26
var validationRetryCount = 0
22
- val client = OwnCloudClient (baseClient.baseUri, null , false )
27
+ val client = OwnCloudClient (baseClient.baseUri, null , false , singleSessionManager )
23
28
if (clearCookiesOnValidation) {
24
29
client.clearCookies();
25
30
} else {
26
31
client.cookiesForBaseUri = baseClient.cookiesForBaseUri
27
32
}
28
33
34
+ client.account = baseClient.account
29
35
client.credentials = baseClient.credentials
30
36
while (validationRetryCount < 5 ) {
31
37
Timber .d(" +++++++++++++++++++++++++++++++++++++ validationRetryCout %d" , validationRetryCount)
@@ -40,7 +46,7 @@ class ConnectionValidator (
40
46
}
41
47
42
48
// 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
43
- if (baseClient.credentials !is OwnCloudCredentialsFactory . OwnCloudAnonymousCredentials ) {
49
+ if (baseClient.credentials !is OwnCloudAnonymousCredentials ) {
44
50
client.setFollowRedirects(false )
45
51
val contentReply = canAccessRootFolder(client)
46
52
if (contentReply.httpCode == HttpConstants .HTTP_OK ) {
@@ -51,8 +57,8 @@ class ConnectionValidator (
51
57
}
52
58
} else {
53
59
failCounter++
54
- if (contentReply.hashCode() == HttpConstants .HTTP_UNAUTHORIZED ) {
55
- triggerAuthRefresh( )
60
+ if (contentReply.httpCode == HttpConstants .HTTP_UNAUTHORIZED ) {
61
+ checkUnauthorizedAccess(client, singleSessionManager, contentReply.httpCode )
56
62
}
57
63
}
58
64
}
@@ -92,4 +98,89 @@ class ConnectionValidator (
92
98
val checkPathExistenceRemoteOperation = CheckPathExistenceRemoteOperation (" /" , true )
93
99
return checkPathExistenceRemoteOperation.execute(client)
94
100
}
101
+
102
+ /* *
103
+ * Determines if credentials should be invalidated according the to the HTTPS status
104
+ * of a network request just performed.
105
+ *
106
+ * @param httpStatusCode Result of the last request ran with the 'credentials' belows.
107
+ * @return 'True' if credentials should and might be invalidated, 'false' if shouldn't or
108
+ * cannot be invalidated with the given arguments.
109
+ */
110
+ private fun shouldInvalidateAccountCredentials (credentials : OwnCloudCredentials , account : OwnCloudAccount , httpStatusCode : Int ): Boolean {
111
+ var shouldInvalidateAccountCredentials = httpStatusCode == HttpConstants .HTTP_UNAUTHORIZED
112
+ shouldInvalidateAccountCredentials = shouldInvalidateAccountCredentials and // real credentials
113
+ (credentials !is OwnCloudAnonymousCredentials )
114
+
115
+ // test if have all the needed to effectively invalidate ...
116
+ shouldInvalidateAccountCredentials =
117
+ shouldInvalidateAccountCredentials and (account.savedAccount != null )
118
+ return shouldInvalidateAccountCredentials
119
+ }
120
+
121
+ /* *
122
+ * Invalidates credentials stored for the given account in the system [AccountManager] and in
123
+ * current [SingleSessionManager.getDefaultSingleton] instance.
124
+ *
125
+ *
126
+ * [.shouldInvalidateAccountCredentials] should be called first.
127
+ *
128
+ */
129
+ private fun invalidateAccountCredentials (account : OwnCloudAccount , credentials : OwnCloudCredentials ) {
130
+ val am = AccountManager .get(context)
131
+ am.invalidateAuthToken(
132
+ account.savedAccount.type,
133
+ credentials.authToken
134
+ )
135
+ am.clearPassword(account.savedAccount) // being strict, only needed for Basic Auth credentials
136
+ }
137
+
138
+ /* *
139
+ * Checks the status code of an execution and decides if should be repeated with fresh credentials.
140
+ *
141
+ *
142
+ * Invalidates current credentials if the request failed as anauthorized.
143
+ *
144
+ *
145
+ * Refresh current credentials if possible, and marks a retry.
146
+ *
147
+ * @param status
148
+ * @param repeatCounter
149
+ * @return
150
+ */
151
+ private fun checkUnauthorizedAccess (client : OwnCloudClient , singleSessionManager : SingleSessionManager , status : Int ): Boolean {
152
+ var credentialsWereRefreshed = false
153
+ val account = client.account
154
+ val credentials = account.credentials
155
+ if (shouldInvalidateAccountCredentials(credentials, account, status)) {
156
+ invalidateAccountCredentials(account, credentials)
157
+ if (credentials.authTokenCanBeRefreshed()) {
158
+ try {
159
+ account.loadCredentials(context)
160
+ // if mAccount.getCredentials().length() == 0 --> refresh failed
161
+ client.credentials = account.credentials
162
+ credentialsWereRefreshed = true
163
+ } catch (e: AccountsException ) {
164
+ Timber .e(
165
+ e, " Error while trying to refresh auth token for %s\n trace: %s" ,
166
+ account.savedAccount.name,
167
+ ExceptionUtils .getStackTrace(e)
168
+ )
169
+ } catch (e: IOException ) {
170
+ Timber .e(
171
+ e, " Error while trying to refresh auth token for %s\n trace: %s" ,
172
+ account.savedAccount.name,
173
+ ExceptionUtils .getStackTrace(e)
174
+ )
175
+ }
176
+ if (! credentialsWereRefreshed) {
177
+ // if credentials are not refreshed, client must be removed
178
+ // from the OwnCloudClientManager to prevent it is reused once and again
179
+ singleSessionManager.removeClientFor(account)
180
+ }
181
+ }
182
+ // else: onExecute will finish with status 401
183
+ }
184
+ return credentialsWereRefreshed
185
+ }
95
186
}
0 commit comments