Loading feature/account/accountmanager/build.gradle.kts +1 −0 Original line number Diff line number Diff line Loading @@ -7,6 +7,7 @@ android { } dependencies { implementation(projects.legacy.account) implementation(projects.core.common) implementation(projects.mail.common) implementation(projects.feature.autodiscovery.api) Loading feature/account/accountmanager/src/main/kotlin/app/k9mail/feature/account/accountmanager/AccountManagerConstants.kt +1 −0 Original line number Diff line number Diff line Loading @@ -24,6 +24,7 @@ object AccountManagerConstants { const val ACCOUNT_EMAIL_ADDRESS_KEY = "email_address" const val MAIL_CONTENT_AUTHORITY = "foundation.e.mail.provider.AppContentProvider" const val KEY_AUTH_STATE = "auth_state" const val AUTH_TOKEN_TYPE = "oauth2-access-token" val ACCOUNT_TYPES = listOf(EELO_ACCOUNT_TYPE, GOOGLE_ACCOUNT_TYPE, YAHOO_ACCOUNT_TYPE) Loading feature/account/accountmanager/src/main/kotlin/app/k9mail/feature/account/accountmanager/AccountManagerHelper.kt 0 → 100644 +151 −0 Original line number Diff line number Diff line package app.k9mail.feature.account.accountmanager import android.accounts.Account import android.accounts.AccountManager import android.content.Context import com.fsck.k9.logging.Timber import com.fsck.k9.mail.AuthType import com.fsck.k9.mail.oauth.AuthStateStorage import app.k9mail.legacy.account.Account as kAccount import app.k9mail.legacy.account.AccountManager as kAccountManager object AccountManagerHelper { /** * Update oAuthState for google accounts which is logged in using accountManager * @return is the update operation successful or not */ fun updateOAuthState(context: Context, accountManager: kAccountManager, account: kAccount?): Boolean { // check params if (account == null) { Timber.w("updating OAuthState failed, account is null") return false } // validation if (account.incomingServerSettings.authenticationType != AuthType.XOAUTH2) { Timber.w("updating oAuthState failed, not oauth2 authType") return false } val osAccountManager = AccountManager.get(context) var result = false AccountManagerConstants.ACCOUNT_TYPES.forEach { val openIdAccount = retrieveOpenIdAccountFromAccountManager(osAccountManager, it, account.email) if (openIdAccount != null) { updateOAuthState(account, osAccountManager, openIdAccount, accountManager) result = true return@forEach } } return result } fun updateOAuthState(context: Context, authStateStorage: AuthStateStorage): Boolean { // check params if (authStateStorage.getEmail().isNullOrBlank()) { Timber.w("updating OAuthState failed, email is null") return false } val osAccountManager = AccountManager.get(context) var result = false AccountManagerConstants.ACCOUNT_TYPES.forEach { val openIdAccount = retrieveOpenIdAccountFromAccountManager(osAccountManager, it, authStateStorage.getEmail()) if (openIdAccount != null) { updateOAuthState(authStateStorage, openIdAccount, osAccountManager) result = true return@forEach } } return result } private fun updateOAuthState( account: kAccount, osAccountManager: AccountManager, openIdAccount: Account?, accountManager: kAccountManager, ) { account.oAuthState = osAccountManager.getUserData(openIdAccount, AccountManagerConstants.KEY_AUTH_STATE) accountManager.saveAccount(account) } private fun updateOAuthState( authStateStorage: AuthStateStorage, openIdAccount: Account?, osAccountManager: AccountManager, ) { val authState = osAccountManager.getUserData(openIdAccount, AccountManagerConstants.KEY_AUTH_STATE) authStateStorage.updateAuthorizationState(authorizationState = authState) } // If token is updated by mail, also update the accountManager fun updateAccountInAccountManager( context: Context?, account: Account?, authState: String?, accessToken: String?, ) { if (context == null || account == null || authState == null || accessToken == null) { Timber.w("updating account for accountManager failed, invalid param.") return } val accountManager = AccountManager.get(context) accountManager.setAuthToken(account, AccountManagerConstants.AUTH_TOKEN_TYPE, accessToken) accountManager.setUserData(account, AccountManagerConstants.KEY_AUTH_STATE, authState) } fun retrieveOpenIdAccountFromAccountManager(context: Context?, email: String?): Account? { if (context == null) { Timber.w("retrieve google accounts from accountManager failed, null context.") return null } val accountManager = AccountManager.get(context) var resultAccount: Account? = null AccountManagerConstants.ACCOUNT_TYPES.forEach { resultAccount = retrieveOpenIdAccountFromAccountManager(accountManager, it, email) if (resultAccount != null) { return@forEach } } return resultAccount } private fun retrieveOpenIdAccountFromAccountManager( accountManager: AccountManager?, accountType: String, email: String?, ): Account? { if (accountManager == null || email.isNullOrEmpty()) { Timber.w("retrieve $accountType account from accountManager failed, invalid param") return null } val openIdAccounts = accountManager.getAccountsByType(accountType) for (openIdAccount in openIdAccounts) { val emailId = accountManager.getUserData(openIdAccount, AccountManagerConstants.ACCOUNT_EMAIL_ADDRESS_KEY) if (!email.equals(emailId, ignoreCase = true)) { continue } val authState = accountManager.getUserData(openIdAccount, AccountManagerConstants.KEY_AUTH_STATE) if (authState.isNullOrEmpty()) { return null } return openIdAccount } return null } } feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/data/InMemoryAccountStateRepository.kt +4 −0 Original line number Diff line number Diff line Loading @@ -18,6 +18,10 @@ class InMemoryAccountStateRepository( return state } override fun getEmail(): String? { return state.emailAddress } override fun setState(accountState: AccountState) { state = accountState } Loading feature/account/server/validation/src/test/kotlin/app/k9mail/feature/account/server/validation/domain/usecase/FakeAuthStateStorage.kt +4 −0 Original line number Diff line number Diff line Loading @@ -10,4 +10,8 @@ class FakeAuthStateStorage( override fun updateAuthorizationState(authorizationState: String?) { this.authorizationState = authorizationState } override fun getEmail(): String? { return null } } Loading
feature/account/accountmanager/build.gradle.kts +1 −0 Original line number Diff line number Diff line Loading @@ -7,6 +7,7 @@ android { } dependencies { implementation(projects.legacy.account) implementation(projects.core.common) implementation(projects.mail.common) implementation(projects.feature.autodiscovery.api) Loading
feature/account/accountmanager/src/main/kotlin/app/k9mail/feature/account/accountmanager/AccountManagerConstants.kt +1 −0 Original line number Diff line number Diff line Loading @@ -24,6 +24,7 @@ object AccountManagerConstants { const val ACCOUNT_EMAIL_ADDRESS_KEY = "email_address" const val MAIL_CONTENT_AUTHORITY = "foundation.e.mail.provider.AppContentProvider" const val KEY_AUTH_STATE = "auth_state" const val AUTH_TOKEN_TYPE = "oauth2-access-token" val ACCOUNT_TYPES = listOf(EELO_ACCOUNT_TYPE, GOOGLE_ACCOUNT_TYPE, YAHOO_ACCOUNT_TYPE) Loading
feature/account/accountmanager/src/main/kotlin/app/k9mail/feature/account/accountmanager/AccountManagerHelper.kt 0 → 100644 +151 −0 Original line number Diff line number Diff line package app.k9mail.feature.account.accountmanager import android.accounts.Account import android.accounts.AccountManager import android.content.Context import com.fsck.k9.logging.Timber import com.fsck.k9.mail.AuthType import com.fsck.k9.mail.oauth.AuthStateStorage import app.k9mail.legacy.account.Account as kAccount import app.k9mail.legacy.account.AccountManager as kAccountManager object AccountManagerHelper { /** * Update oAuthState for google accounts which is logged in using accountManager * @return is the update operation successful or not */ fun updateOAuthState(context: Context, accountManager: kAccountManager, account: kAccount?): Boolean { // check params if (account == null) { Timber.w("updating OAuthState failed, account is null") return false } // validation if (account.incomingServerSettings.authenticationType != AuthType.XOAUTH2) { Timber.w("updating oAuthState failed, not oauth2 authType") return false } val osAccountManager = AccountManager.get(context) var result = false AccountManagerConstants.ACCOUNT_TYPES.forEach { val openIdAccount = retrieveOpenIdAccountFromAccountManager(osAccountManager, it, account.email) if (openIdAccount != null) { updateOAuthState(account, osAccountManager, openIdAccount, accountManager) result = true return@forEach } } return result } fun updateOAuthState(context: Context, authStateStorage: AuthStateStorage): Boolean { // check params if (authStateStorage.getEmail().isNullOrBlank()) { Timber.w("updating OAuthState failed, email is null") return false } val osAccountManager = AccountManager.get(context) var result = false AccountManagerConstants.ACCOUNT_TYPES.forEach { val openIdAccount = retrieveOpenIdAccountFromAccountManager(osAccountManager, it, authStateStorage.getEmail()) if (openIdAccount != null) { updateOAuthState(authStateStorage, openIdAccount, osAccountManager) result = true return@forEach } } return result } private fun updateOAuthState( account: kAccount, osAccountManager: AccountManager, openIdAccount: Account?, accountManager: kAccountManager, ) { account.oAuthState = osAccountManager.getUserData(openIdAccount, AccountManagerConstants.KEY_AUTH_STATE) accountManager.saveAccount(account) } private fun updateOAuthState( authStateStorage: AuthStateStorage, openIdAccount: Account?, osAccountManager: AccountManager, ) { val authState = osAccountManager.getUserData(openIdAccount, AccountManagerConstants.KEY_AUTH_STATE) authStateStorage.updateAuthorizationState(authorizationState = authState) } // If token is updated by mail, also update the accountManager fun updateAccountInAccountManager( context: Context?, account: Account?, authState: String?, accessToken: String?, ) { if (context == null || account == null || authState == null || accessToken == null) { Timber.w("updating account for accountManager failed, invalid param.") return } val accountManager = AccountManager.get(context) accountManager.setAuthToken(account, AccountManagerConstants.AUTH_TOKEN_TYPE, accessToken) accountManager.setUserData(account, AccountManagerConstants.KEY_AUTH_STATE, authState) } fun retrieveOpenIdAccountFromAccountManager(context: Context?, email: String?): Account? { if (context == null) { Timber.w("retrieve google accounts from accountManager failed, null context.") return null } val accountManager = AccountManager.get(context) var resultAccount: Account? = null AccountManagerConstants.ACCOUNT_TYPES.forEach { resultAccount = retrieveOpenIdAccountFromAccountManager(accountManager, it, email) if (resultAccount != null) { return@forEach } } return resultAccount } private fun retrieveOpenIdAccountFromAccountManager( accountManager: AccountManager?, accountType: String, email: String?, ): Account? { if (accountManager == null || email.isNullOrEmpty()) { Timber.w("retrieve $accountType account from accountManager failed, invalid param") return null } val openIdAccounts = accountManager.getAccountsByType(accountType) for (openIdAccount in openIdAccounts) { val emailId = accountManager.getUserData(openIdAccount, AccountManagerConstants.ACCOUNT_EMAIL_ADDRESS_KEY) if (!email.equals(emailId, ignoreCase = true)) { continue } val authState = accountManager.getUserData(openIdAccount, AccountManagerConstants.KEY_AUTH_STATE) if (authState.isNullOrEmpty()) { return null } return openIdAccount } return null } }
feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/data/InMemoryAccountStateRepository.kt +4 −0 Original line number Diff line number Diff line Loading @@ -18,6 +18,10 @@ class InMemoryAccountStateRepository( return state } override fun getEmail(): String? { return state.emailAddress } override fun setState(accountState: AccountState) { state = accountState } Loading
feature/account/server/validation/src/test/kotlin/app/k9mail/feature/account/server/validation/domain/usecase/FakeAuthStateStorage.kt +4 −0 Original line number Diff line number Diff line Loading @@ -10,4 +10,8 @@ class FakeAuthStateStorage( override fun updateAuthorizationState(authorizationState: String?) { this.authorizationState = authorizationState } override fun getEmail(): String? { return null } }