Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d1a4f4bb authored by Fahim M. Choudhury's avatar Fahim M. Choudhury
Browse files

Merge branch '3377-murena-oauth-re-authenticate' into 'main'

fix: add support for re-authentication in Murena account settings

See merge request !158
parents d1f45eac 7b1f5333
Loading
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -31,6 +31,7 @@ import at.bitfire.davdroid.db.AppDatabase
import at.bitfire.davdroid.log.Logger
import at.bitfire.davdroid.resource.LocalAddressBook
import at.bitfire.davdroid.settings.AccountSettings
import at.bitfire.davdroid.ui.account.SettingsActivity
import at.bitfire.davdroid.ui.setup.LoginActivity
import at.bitfire.davdroid.util.AuthStatePrefUtils
import dagger.hilt.EntryPoint
@@ -149,6 +150,16 @@ abstract class DefaultAccountAuthenticatorService : Service(), OnAccountsUpdateL
                    LoginActivity.USERNAME_HINT,
                    it.getString(LoginActivity.USERNAME_HINT)
                )

                intent.putExtra(
                    SettingsActivity.EXTRA_IS_RE_AUTHENTICATING,
                    it.getBoolean(SettingsActivity.EXTRA_IS_RE_AUTHENTICATING)
                )

                intent.putExtra(
                    SettingsActivity.EXTRA_ACCOUNT_NAME_HINT,
                    it.getString(SettingsActivity.EXTRA_ACCOUNT_NAME_HINT)
                )
            }

            val bundle = Bundle(1)
+16 −3
Original line number Diff line number Diff line
@@ -62,6 +62,8 @@ class SettingsActivity: AppCompatActivity() {

    companion object {
        const val EXTRA_ACCOUNT = "account"
        const val EXTRA_IS_RE_AUTHENTICATING = "is_re_authenticating"
        const val EXTRA_ACCOUNT_NAME_HINT = "account_name_hint"
    }

    private val account by lazy { intent.getParcelableExtra<Account>(EXTRA_ACCOUNT) ?: throw IllegalArgumentException("EXTRA_ACCOUNT must be set") }
@@ -127,9 +129,20 @@ class SettingsActivity: AppCompatActivity() {
        }

        private fun launchSetup(): Boolean {
            AccountManager.get(context).addAccount(account.type,
                null, null, null, activity, null,
                null)
            val bundle = Bundle().apply {
                putBoolean(EXTRA_IS_RE_AUTHENTICATING, true)
                putString(EXTRA_ACCOUNT_NAME_HINT, account.name)
            }

            AccountManager.get(context).addAccount(
                /* accountType = */ account.type,
                /* authTokenType = */ null,
                /* requiredFeatures = */ null,
                /* addAccountOptions = */ bundle,
                /* activity = */ activity,
                /* callback = */ null,
                /* handler = */ null
            )
            return true
        }

+55 −1
Original line number Diff line number Diff line
@@ -343,7 +343,7 @@ class AccountDetailsFragment : Fragment() {
                    }
                }

                val account = accountToUpdate ?: Account(name, accountType)
                val account = getOrCreateAccount(name, accountType, accountToUpdate)

                val userData = AccountSettings.initialUserData(credentials, baseURL, config.cookies, config.calDAV?.emails?.firstOrNull())

@@ -482,6 +482,60 @@ class AccountDetailsFragment : Fragment() {
            return result
        }

        /**
         * Gets or creates an account based on the provided parameters and existing accounts.
         *
         * @param accountName The name for the account
         * @param accountType The type of account
         * @param accountToUpdate An existing account to update, if any
         * @return The account to use
         */
        private fun getOrCreateAccount(
            accountName: String,
            accountType: String,
            accountToUpdate: Account?
        ): Account {
            if (accountToUpdate != null) {
                return accountToUpdate
            }

            val existingAccount = findExistingMurenaAccount()

            // If a Murena account exists and matches the requested account name, use it
            if (existingAccount != null && isMurenaAccountMatchingRequestedName(
                    existingAccount,
                    accountName
                )
            ) {
                return existingAccount
            }

            return Account(accountName, accountType)
        }

        private fun findExistingMurenaAccount(): Account? {
            return AccountUtils.getOpenIdMainAccounts(context)
                .firstOrNull {
                    it.type == context.getString(R.string.eelo_account_type)
                            && AccountUtils.isMurenaAccount(context, it)
                }
        }

        /**
         * Determines if an existing Murena account should be used based on the account name.
         */
        private fun isMurenaAccountMatchingRequestedName(
            murenaAccount: Account,
            requestedAccountName: String
        ): Boolean {
            if (!murenaAccount.name.contains("@")) {
                return false
            }

            val accountName = murenaAccount.name.substringBefore("@")
            return accountName == requestedAccountName
        }

        private fun updateAuthState(
            userData: Bundle,
            accountManager: AccountManager,
+39 −12
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ import at.bitfire.davdroid.log.Logger
import at.bitfire.davdroid.murenasso.MurenaSsoMigrationPreferences
import at.bitfire.davdroid.servicedetection.DavResourceFinder
import at.bitfire.davdroid.ui.DebugInfoActivity
import at.bitfire.davdroid.ui.account.SettingsActivity
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import java.lang.ref.WeakReference
import java.net.URI
@@ -35,18 +36,28 @@ class DetectConfigurationFragment: Fragment() {
    private val loginModel by activityViewModels<LoginModel>()
    private val model by viewModels<DetectConfigurationModel>()


    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val accountType = requireActivity().intent.getStringExtra(LoginActivity.ACCOUNT_TYPE)

        if (!MurenaSsoMigrationPreferences.isSsoMigrationRunning(requireContext())) {
            if (model.blockProceedWithLogin(accountType)) {
        val intent = requireActivity().intent

        val accountType = intent.getStringExtra(LoginActivity.ACCOUNT_TYPE)
        val isReAuthenticating =
            intent.getBooleanExtra(SettingsActivity.EXTRA_IS_RE_AUTHENTICATING, false)
        val isSsoMigrationRunning =
            MurenaSsoMigrationPreferences.isSsoMigrationRunning(requireContext())
        val isOAuthFlowComplete =
            intent.getBooleanExtra(LoginActivity.OPENID_AUTH_FLOW_COMPLETE, false)

        if (shouldBlockLogin(
                accountType,
                isReAuthenticating,
                isSsoMigrationRunning,
                isOAuthFlowComplete
            )
        ) {
            ECloudAccountHelper.showMultipleECloudAccountNotAcceptedDialog(requireActivity())
            return
        }
        }

        val blockOnUnauthorizedException = (accountType == getString(R.string.eelo_account_type)) && !EeloAuthenticatorModel.ENABLE_OIDC_SUPPORT

@@ -60,18 +71,22 @@ class DetectConfigurationFragment: Fragment() {
            parentFragmentManager.popBackStack()

            if (result.calDAV != null || result.cardDAV != null) {
                requireActivity().intent.putExtra(LoginActivity.RETRY_ON_401, false)
                intent.putExtra(LoginActivity.RETRY_ON_401, false)

                parentFragmentManager.beginTransaction()
                    .replace(android.R.id.content, AccountDetailsFragment())
                    .addToBackStack(null)
                    .commit()
            } else if(requireActivity().intent.getBooleanExtra(LoginActivity.RETRY_ON_401, false) && loginModel.configuration?.encountered401 == true) {
            } else if (intent.getBooleanExtra(
                    LoginActivity.RETRY_ON_401,
                    false
                ) && loginModel.configuration?.encountered401 == true
            ) {
                // murena account has encounters 401, most-probably user put wrong accountId (ex: abc@murena.io instead of abc@e.email)
                // do nothing, EeloAuthenticatorFragment will retry with another time with another user email
                return@observe
            } else {
                requireActivity().intent.putExtra(LoginActivity.RETRY_ON_401, false)
                intent.putExtra(LoginActivity.RETRY_ON_401, false)

                parentFragmentManager.beginTransaction()
                    .add(NothingDetectedFragment(), null)
@@ -80,6 +95,18 @@ class DetectConfigurationFragment: Fragment() {
        }
    }

    private fun shouldBlockLogin(
        accountType: String?,
        isReAuthenticating: Boolean,
        isSsoMigrationRunning: Boolean,
        isOAuthFlowComplete: Boolean
    ): Boolean {
        return !isOAuthFlowComplete &&
                !isReAuthenticating &&
                !isSsoMigrationRunning &&
                model.blockProceedWithLogin(accountType)
    }

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View =
            inflater.inflate(R.layout.detect_configuration, container, false)!!

+23 −1
Original line number Diff line number Diff line
@@ -39,6 +39,7 @@ import at.bitfire.davdroid.databinding.FragmentEeloAuthenticatorBinding
import at.bitfire.davdroid.db.Credentials
import at.bitfire.davdroid.murenasso.MurenaSsoMigrationPreferences
import at.bitfire.davdroid.ui.ShowUrlActivity
import at.bitfire.davdroid.ui.account.SettingsActivity
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.textfield.TextInputEditText
import com.google.android.material.textfield.TextInputLayout
@@ -59,6 +60,14 @@ class EeloAuthenticatorFragment : Fragment() {
    private lateinit var passwordEditText: TextInputEditText
    private lateinit var passwordHolder: View

    private val isReAuthenticating by lazy {
        requireActivity().intent.getBooleanExtra(SettingsActivity.EXTRA_IS_RE_AUTHENTICATING, false)
    }

    private val userNameHint by lazy {
        requireActivity().intent.getStringExtra(SettingsActivity.EXTRA_ACCOUNT_NAME_HINT)
    }

    private fun isNetworkAvailable(): Boolean {
        val connectivityManager = requireActivity().getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
        val activeNetworkInfo = connectivityManager.activeNetworkInfo
@@ -120,9 +129,16 @@ class EeloAuthenticatorFragment : Fragment() {

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        if (isReAuthenticating) {
            userIdEditText.setText(userNameHint)
            return
        }

        if (MurenaSsoMigrationPreferences.isSsoMigrationRunning(requireContext())) {
            return
        }

        if (model.blockProceedWithLogin()) {
            ECloudAccountHelper.showMultipleECloudAccountNotAcceptedDialog(requireActivity())
        }
@@ -219,7 +235,13 @@ class EeloAuthenticatorFragment : Fragment() {
        val password = passwordEditText.text.toString()

        if (handleOpenIdAuth && userId.isNotBlank()) {
            requireActivity().intent.putExtra(LoginActivity.USERNAME_HINT, userIdEditText.text.toString())
            requireActivity().intent.apply {
                val userNameHint =
                    if (!userNameHint.isNullOrBlank()) userNameHint else userIdEditText.text.toString()

                putExtra(LoginActivity.USERNAME_HINT, userNameHint)
                putExtra(SettingsActivity.EXTRA_IS_RE_AUTHENTICATING, isReAuthenticating)
            }
            navigate(MurenaOpenIdAuthFragment())
        } else if (userId.isNotBlank() && password.isNotBlank() && validate()) {
            navigate(DetectConfigurationFragment())
Loading