From 15f27b712e69e7ebe3abc858732a6cb514aa8bab Mon Sep 17 00:00:00 2001 From: Armin Date: Sat, 18 May 2024 13:20:12 +0200 Subject: [PATCH 1/3] Add logout support --- .../cyface/synchronization/WiFiSurveyor.java | 4 +-- .../synchronization/AuthStateManager.kt | 35 ++++++++++++++++++- .../synchronization/CyfaceAuthenticator.kt | 2 +- .../CyfaceAuthenticatorService.kt | 1 - .../synchronization/CyfaceSyncService.kt | 5 ++- .../de/cyface/synchronization/OAuth2.kt | 26 ++++++++------ 6 files changed, 57 insertions(+), 16 deletions(-) diff --git a/synchronization/src/main/java/de/cyface/synchronization/WiFiSurveyor.java b/synchronization/src/main/java/de/cyface/synchronization/WiFiSurveyor.java index de48aa980..4088ff41b 100644 --- a/synchronization/src/main/java/de/cyface/synchronization/WiFiSurveyor.java +++ b/synchronization/src/main/java/de/cyface/synchronization/WiFiSurveyor.java @@ -374,8 +374,8 @@ public void makeAccountSyncable(@NonNull final Account account, boolean enabled) * @return The only Account existing */ public Account getAccount() { - final AccountManager accountManager = AccountManager.get(context.get()); - final Account[] cyfaceAccounts = accountManager.getAccountsByType(accountType); + final var accountManager = AccountManager.get(context.get()); + final var cyfaceAccounts = accountManager.getAccountsByType(accountType); if (cyfaceAccounts.length == 0) { throw new IllegalStateException("No cyface account exists."); } diff --git a/synchronization/src/main/kotlin/de/cyface/synchronization/AuthStateManager.kt b/synchronization/src/main/kotlin/de/cyface/synchronization/AuthStateManager.kt index 79dad5c27..036f76cfb 100644 --- a/synchronization/src/main/kotlin/de/cyface/synchronization/AuthStateManager.kt +++ b/synchronization/src/main/kotlin/de/cyface/synchronization/AuthStateManager.kt @@ -23,6 +23,7 @@ import net.openid.appauth.AuthorizationResponse import net.openid.appauth.RegistrationResponse import net.openid.appauth.TokenResponse import org.json.JSONException +import java.io.File import java.lang.ref.WeakReference import java.util.concurrent.atomic.AtomicReference import java.util.concurrent.locks.ReentrantLock @@ -32,7 +33,7 @@ import java.util.concurrent.locks.ReentrantLock * This stores the instance in a shared preferences file, and provides thread-safe access and * mutation. */ -class AuthStateManager private constructor(context: Context) { +class AuthStateManager private constructor(private val context: Context) { private val mPrefs: SharedPreferences private val mPrefsLock: ReentrantLock private val mCurrentAuthState: AtomicReference @@ -130,6 +131,38 @@ class AuthStateManager private constructor(context: Context) { } } + /** + * Removes the shared preferences file, as clearing the state did not work. + * + * This was added in [LEIP-233] to fix the bug where the shared preferences were not cleared + * and "credentials invalid" was shown after re-login during upload. + */ + /*@AnyThread + fun clearState() { + mPrefsLock.lock() + try { + val editor = mPrefs.edit() + editor.clear() // Clears all data in the SharedPreferences + check(editor.commit()) { "Failed to clear state from shared prefs" } + mCurrentAuthState.set(null) // Reset the current auth state reference + } finally { + mPrefsLock.unlock() + } + }*/ + @AnyThread + fun deletePreferencesFile() { + mPrefsLock.lock() + try { + val file = File(context.filesDir.parentFile, "shared_prefs/$STORE_NAME.xml") + if (file.exists()) { + file.delete() + } + mCurrentAuthState.set(null) + } finally { + mPrefsLock.unlock() + } + } + companion object { private val INSTANCE_REF = AtomicReference(WeakReference(null)) private const val TAG = "AuthStateManager" diff --git a/synchronization/src/main/kotlin/de/cyface/synchronization/CyfaceAuthenticator.kt b/synchronization/src/main/kotlin/de/cyface/synchronization/CyfaceAuthenticator.kt index 989a34ee6..6de87bac2 100644 --- a/synchronization/src/main/kotlin/de/cyface/synchronization/CyfaceAuthenticator.kt +++ b/synchronization/src/main/kotlin/de/cyface/synchronization/CyfaceAuthenticator.kt @@ -50,7 +50,7 @@ import kotlinx.coroutines.runBlocking class CyfaceAuthenticator(private val context: Context) : AbstractAccountAuthenticator(context), LoginActivityProvider { - private var auth: OAuth2 = OAuth2(context, settings) + var auth: OAuth2 = OAuth2(context, settings, "CyfaceAuthenticator") override fun editProperties( response: AccountAuthenticatorResponse, diff --git a/synchronization/src/main/kotlin/de/cyface/synchronization/CyfaceAuthenticatorService.kt b/synchronization/src/main/kotlin/de/cyface/synchronization/CyfaceAuthenticatorService.kt index 24248d9f3..7f12b7ce9 100644 --- a/synchronization/src/main/kotlin/de/cyface/synchronization/CyfaceAuthenticatorService.kt +++ b/synchronization/src/main/kotlin/de/cyface/synchronization/CyfaceAuthenticatorService.kt @@ -21,7 +21,6 @@ package de.cyface.synchronization import android.app.Service import android.content.Intent import android.os.IBinder -import android.util.Log /** * The Android service used to communicate with the Stub Authenticator. This has been implemented as described in diff --git a/synchronization/src/main/kotlin/de/cyface/synchronization/CyfaceSyncService.kt b/synchronization/src/main/kotlin/de/cyface/synchronization/CyfaceSyncService.kt index 50b485e8c..8924f7c60 100644 --- a/synchronization/src/main/kotlin/de/cyface/synchronization/CyfaceSyncService.kt +++ b/synchronization/src/main/kotlin/de/cyface/synchronization/CyfaceSyncService.kt @@ -19,7 +19,10 @@ package de.cyface.synchronization import android.app.Service +import android.content.ComponentName +import android.content.Context import android.content.Intent +import android.content.ServiceConnection import android.os.IBinder import de.cyface.uploader.DefaultUploader import de.cyface.utils.Validate @@ -46,7 +49,7 @@ class CyfaceSyncService : Service() { syncAdapter = SyncAdapter( applicationContext, true, - OAuth2(applicationContext, CyfaceAuthenticator.settings), + OAuth2(applicationContext, CyfaceAuthenticator.settings, "CyfaceSyncService"), DefaultUploader(collectorApi), ) } diff --git a/synchronization/src/main/kotlin/de/cyface/synchronization/OAuth2.kt b/synchronization/src/main/kotlin/de/cyface/synchronization/OAuth2.kt index 1acca884b..2bc391223 100644 --- a/synchronization/src/main/kotlin/de/cyface/synchronization/OAuth2.kt +++ b/synchronization/src/main/kotlin/de/cyface/synchronization/OAuth2.kt @@ -22,7 +22,6 @@ import android.accounts.Account import android.accounts.AccountManager import android.content.ContentResolver import android.content.Context -import android.content.Intent import android.os.Build import android.os.Bundle import android.util.Log @@ -38,7 +37,6 @@ import net.openid.appauth.AuthState import net.openid.appauth.AuthorizationException import net.openid.appauth.AuthorizationResponse import net.openid.appauth.AuthorizationService -import net.openid.appauth.AuthorizationServiceConfiguration import net.openid.appauth.ClientAuthentication import net.openid.appauth.EndSessionRequest import net.openid.appauth.TokenRequest @@ -57,7 +55,7 @@ import org.json.JSONObject * @param context The context to load settings and accounts from. * @param settings The settings which store the user preferences. */ -class OAuth2(context: Context, settings: SynchronizationSettings) : Auth { +class OAuth2(context: Context, settings: SynchronizationSettings, private val caller: String) : Auth { /** * The service used for authorization. @@ -84,6 +82,7 @@ class OAuth2(context: Context, settings: SynchronizationSettings) : Auth { //Handler().postDelayed({signOut()}, 2000) //return }*/ + Log.i(TAG, "OAuth2 init ($caller)") authService = AuthorizationService( context, AppAuthConfiguration.Builder() @@ -259,7 +258,7 @@ class OAuth2(context: Context, settings: SynchronizationSettings) : Auth { fun signOut() { // discard the authorization and token state, but retain the configuration and // dynamic client registration (if applicable), to save from retrieving them again. - val currentState: AuthState = stateManager.current + val currentState = stateManager.current if (currentState.authorizationServiceConfiguration != null) { // Replace the state with a fresh `AuthState` val clearedState = AuthState(currentState.authorizationServiceConfiguration!!) @@ -267,16 +266,23 @@ class OAuth2(context: Context, settings: SynchronizationSettings) : Auth { clearedState.update(currentState.lastRegistrationResponse) } stateManager.replace(clearedState) + // FIXME: completely delete AuthState.xml in sharedPreferences to fix credentials + // incorrect bug after logout and login during upload + stateManager.deletePreferencesFile()//clearState() + } else { + Log.w(TAG, "No authorization service configuration to sign out") } + + // Invalidate all tokens to ensure they are no longer used + authService.dispose() } - // Keep: login is currently just deactivated because it's buggy + // FIXME: Keep: login is currently just deactivated because it's buggy fun endSession(activity: FragmentActivity) { - val currentState: AuthState = stateManager.current - val config: AuthorizationServiceConfiguration = - currentState.authorizationServiceConfiguration!! + val currentState = stateManager.current + val config = currentState.authorizationServiceConfiguration!! if (config.endSessionEndpoint != null) { - val endSessionIntent: Intent = authService.getEndSessionRequestIntent( + val endSessionIntent = authService.getEndSessionRequestIntent( EndSessionRequest.Builder(config) .setIdTokenHint(currentState.idToken) .setPostLogoutRedirectUri(configuration.endSessionRedirectUri) @@ -310,4 +316,4 @@ class OAuth2(context: Context, settings: SynchronizationSettings) : Auth { .put("https_required", true) } } -} \ No newline at end of file +} From 04aa740b6d7a42561625b9723bcf7d0c3a531aec Mon Sep 17 00:00:00 2001 From: Armin Date: Sat, 18 May 2024 13:35:34 +0200 Subject: [PATCH 2/3] Cleanup --- .../cyface/synchronization/WiFiSurveyor.java | 4 +--- .../synchronization/AuthStateManager.kt | 12 +++--------- .../synchronization/CyfaceAuthenticator.kt | 4 +--- .../CyfaceAuthenticatorService.kt | 2 -- .../synchronization/CyfaceSyncService.kt | 7 +------ .../de/cyface/synchronization/OAuth2.kt | 19 ++++++++----------- 6 files changed, 14 insertions(+), 34 deletions(-) diff --git a/synchronization/src/main/java/de/cyface/synchronization/WiFiSurveyor.java b/synchronization/src/main/java/de/cyface/synchronization/WiFiSurveyor.java index 4088ff41b..559f9a8a8 100644 --- a/synchronization/src/main/java/de/cyface/synchronization/WiFiSurveyor.java +++ b/synchronization/src/main/java/de/cyface/synchronization/WiFiSurveyor.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2021 Cyface GmbH + * Copyright 2017-2024 Cyface GmbH * * This file is part of the Cyface SDK for Android. * @@ -49,8 +49,6 @@ * * @author Klemens Muthmann * @author Armin Schnabel - * @version 7.2.1 - * @since 2.0.0 */ public class WiFiSurveyor extends BroadcastReceiver { diff --git a/synchronization/src/main/kotlin/de/cyface/synchronization/AuthStateManager.kt b/synchronization/src/main/kotlin/de/cyface/synchronization/AuthStateManager.kt index 036f76cfb..e713486ed 100644 --- a/synchronization/src/main/kotlin/de/cyface/synchronization/AuthStateManager.kt +++ b/synchronization/src/main/kotlin/de/cyface/synchronization/AuthStateManager.kt @@ -34,15 +34,9 @@ import java.util.concurrent.locks.ReentrantLock * mutation. */ class AuthStateManager private constructor(private val context: Context) { - private val mPrefs: SharedPreferences - private val mPrefsLock: ReentrantLock - private val mCurrentAuthState: AtomicReference - - init { - mPrefs = context.getSharedPreferences(STORE_NAME, Context.MODE_PRIVATE) - mPrefsLock = ReentrantLock() - mCurrentAuthState = AtomicReference() - } + private val mPrefs: SharedPreferences = context.getSharedPreferences(STORE_NAME, Context.MODE_PRIVATE) + private val mPrefsLock: ReentrantLock = ReentrantLock() + private val mCurrentAuthState: AtomicReference = AtomicReference() @get:AnyThread val current: AuthState diff --git a/synchronization/src/main/kotlin/de/cyface/synchronization/CyfaceAuthenticator.kt b/synchronization/src/main/kotlin/de/cyface/synchronization/CyfaceAuthenticator.kt index 6de87bac2..c0b5c5fae 100644 --- a/synchronization/src/main/kotlin/de/cyface/synchronization/CyfaceAuthenticator.kt +++ b/synchronization/src/main/kotlin/de/cyface/synchronization/CyfaceAuthenticator.kt @@ -1,5 +1,5 @@ /* - * Copyright 2018-2023 Cyface GmbH + * Copyright 2018-2024 Cyface GmbH * * This file is part of the Cyface SDK for Android. * @@ -44,8 +44,6 @@ import kotlinx.coroutines.runBlocking * * @author Klemens Muthmann * @author Armin Schnabel - * @version 5.1.1 - * @since 2.0.0 */ class CyfaceAuthenticator(private val context: Context) : AbstractAccountAuthenticator(context), LoginActivityProvider { diff --git a/synchronization/src/main/kotlin/de/cyface/synchronization/CyfaceAuthenticatorService.kt b/synchronization/src/main/kotlin/de/cyface/synchronization/CyfaceAuthenticatorService.kt index 7f12b7ce9..049a58d9b 100644 --- a/synchronization/src/main/kotlin/de/cyface/synchronization/CyfaceAuthenticatorService.kt +++ b/synchronization/src/main/kotlin/de/cyface/synchronization/CyfaceAuthenticatorService.kt @@ -28,8 +28,6 @@ import android.os.IBinder * * @author Klemens Muthmann * @author Armin Schnabel - * @version 1.0.7 - * @since 2.0.0 */ class CyfaceAuthenticatorService : Service() { /** diff --git a/synchronization/src/main/kotlin/de/cyface/synchronization/CyfaceSyncService.kt b/synchronization/src/main/kotlin/de/cyface/synchronization/CyfaceSyncService.kt index 8924f7c60..538efe679 100644 --- a/synchronization/src/main/kotlin/de/cyface/synchronization/CyfaceSyncService.kt +++ b/synchronization/src/main/kotlin/de/cyface/synchronization/CyfaceSyncService.kt @@ -1,5 +1,5 @@ /* - * Copyright 2017-2023 Cyface GmbH + * Copyright 2017-2024 Cyface GmbH * * This file is part of the Cyface SDK for Android. * @@ -19,10 +19,7 @@ package de.cyface.synchronization import android.app.Service -import android.content.ComponentName -import android.content.Context import android.content.Intent -import android.content.ServiceConnection import android.os.IBinder import de.cyface.uploader.DefaultUploader import de.cyface.utils.Validate @@ -37,8 +34,6 @@ import kotlinx.coroutines.runBlocking * * @author Armin Schnabel * @author Klemens Muthmann - * @version 1.0.9 - * @since 2.0.0 */ class CyfaceSyncService : Service() { diff --git a/synchronization/src/main/kotlin/de/cyface/synchronization/OAuth2.kt b/synchronization/src/main/kotlin/de/cyface/synchronization/OAuth2.kt index 2bc391223..b356fe8ff 100644 --- a/synchronization/src/main/kotlin/de/cyface/synchronization/OAuth2.kt +++ b/synchronization/src/main/kotlin/de/cyface/synchronization/OAuth2.kt @@ -1,5 +1,5 @@ /* - * Copyright 2023 Cyface GmbH + * Copyright 2023-2024 Cyface GmbH * * This file is part of the Cyface SDK for Android. * @@ -50,12 +50,10 @@ import org.json.JSONObject * accounts, and handling sessions. * * @author Armin Schnabel - * @version 1.0.0 - * @since 7.9.0 * @param context The context to load settings and accounts from. * @param settings The settings which store the user preferences. */ -class OAuth2(context: Context, settings: SynchronizationSettings, private val caller: String) : Auth { +class OAuth2(context: Context, settings: SynchronizationSettings, caller: String) : Auth { /** * The service used for authorization. @@ -65,17 +63,14 @@ class OAuth2(context: Context, settings: SynchronizationSettings, private val ca /** * The authorization state. */ - private var stateManager: AuthStateManager + private var stateManager: AuthStateManager = AuthStateManager.getInstance(context) /** * The configuration of the OAuth 2 endpoint to authorize against. */ - private var configuration: Configuration + private var configuration: Configuration = Configuration.getInstance(context, settings) init { - // Authorization - stateManager = AuthStateManager.getInstance(context) - configuration = Configuration.getInstance(context, settings) /*if (config.hasConfigurationChanged()) { //throw IllegalArgumentException("config changed (SyncAdapter)") Toast.makeText(context, "Ignoring: config changed (SyncAdapter)", Toast.LENGTH_SHORT).show() @@ -274,10 +269,12 @@ class OAuth2(context: Context, settings: SynchronizationSettings, private val ca } // Invalidate all tokens to ensure they are no longer used - authService.dispose() + dispose() } - // FIXME: Keep: login is currently just deactivated because it's buggy + /** + * Sends the end session request to the auth service to sign out the user. + */ fun endSession(activity: FragmentActivity) { val currentState = stateManager.current val config = currentState.authorizationServiceConfiguration!! From 086bc4241a1c9aa928389d38b412136653845d08 Mon Sep 17 00:00:00 2001 From: Armin Date: Sat, 18 May 2024 14:31:29 +0200 Subject: [PATCH 3/3] Remove preferences deletion as this does not help --- .../synchronization/AuthStateManager.kt | 33 ------------------- .../de/cyface/synchronization/OAuth2.kt | 7 ++-- 2 files changed, 4 insertions(+), 36 deletions(-) diff --git a/synchronization/src/main/kotlin/de/cyface/synchronization/AuthStateManager.kt b/synchronization/src/main/kotlin/de/cyface/synchronization/AuthStateManager.kt index e713486ed..ee292566f 100644 --- a/synchronization/src/main/kotlin/de/cyface/synchronization/AuthStateManager.kt +++ b/synchronization/src/main/kotlin/de/cyface/synchronization/AuthStateManager.kt @@ -23,7 +23,6 @@ import net.openid.appauth.AuthorizationResponse import net.openid.appauth.RegistrationResponse import net.openid.appauth.TokenResponse import org.json.JSONException -import java.io.File import java.lang.ref.WeakReference import java.util.concurrent.atomic.AtomicReference import java.util.concurrent.locks.ReentrantLock @@ -125,38 +124,6 @@ class AuthStateManager private constructor(private val context: Context) { } } - /** - * Removes the shared preferences file, as clearing the state did not work. - * - * This was added in [LEIP-233] to fix the bug where the shared preferences were not cleared - * and "credentials invalid" was shown after re-login during upload. - */ - /*@AnyThread - fun clearState() { - mPrefsLock.lock() - try { - val editor = mPrefs.edit() - editor.clear() // Clears all data in the SharedPreferences - check(editor.commit()) { "Failed to clear state from shared prefs" } - mCurrentAuthState.set(null) // Reset the current auth state reference - } finally { - mPrefsLock.unlock() - } - }*/ - @AnyThread - fun deletePreferencesFile() { - mPrefsLock.lock() - try { - val file = File(context.filesDir.parentFile, "shared_prefs/$STORE_NAME.xml") - if (file.exists()) { - file.delete() - } - mCurrentAuthState.set(null) - } finally { - mPrefsLock.unlock() - } - } - companion object { private val INSTANCE_REF = AtomicReference(WeakReference(null)) private const val TAG = "AuthStateManager" diff --git a/synchronization/src/main/kotlin/de/cyface/synchronization/OAuth2.kt b/synchronization/src/main/kotlin/de/cyface/synchronization/OAuth2.kt index b356fe8ff..e584f5128 100644 --- a/synchronization/src/main/kotlin/de/cyface/synchronization/OAuth2.kt +++ b/synchronization/src/main/kotlin/de/cyface/synchronization/OAuth2.kt @@ -261,9 +261,10 @@ class OAuth2(context: Context, settings: SynchronizationSettings, caller: String clearedState.update(currentState.lastRegistrationResponse) } stateManager.replace(clearedState) - // FIXME: completely delete AuthState.xml in sharedPreferences to fix credentials - // incorrect bug after logout and login during upload - stateManager.deletePreferencesFile()//clearState() + + // [LEIP-233] Completely deleting the sharedPreferences/AuthState.xml file does not + // fix the `Credentials incorrect` error after re-login (app restart required) + //stateManager.deletePreferencesFile() } else { Log.w(TAG, "No authorization service configuration to sign out") }