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

Commit 87d4d537 authored by Jonathan Klee's avatar Jonathan Klee
Browse files

Add /e/ accounts

parent 8b410bcd
Loading
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
import org.gradle.internal.impldep.org.junit.experimental.categories.Categories.CategoryFilter.exclude
import java.io.FileInputStream
import java.util.Properties

@@ -289,6 +290,11 @@ dependencies {
    implementation(libs.okhttp.logging)
    implementation(libs.openid.appauth)
    implementation(libs.unifiedpush)
    implementation(libs.nextcloud.android.lib) {
        exclude(group="com.github.bitfireAT", module="dav4jvm")
        exclude(group="org.ogce", module="xpp3")
        exclude(group="com.squareup.okhttp3")
    }

    // for tests
    androidTestImplementation(libs.androidx.arch.core.testing)
+24 −0
Original line number Diff line number Diff line
@@ -257,6 +257,30 @@
                android:resource="@xml/contacts"/>
        </service>

        <!-- account type "Eelo" -->
        <service
            android:name=".sync.account.EeloAccountAuthenticatorService"
            android:exported="false">
            <intent-filter>
                <action android:name="android.accounts.AccountAuthenticator" />
            </intent-filter>

            <meta-data
                android:name="android.accounts.AccountAuthenticator"
                android:resource="@xml/eelo_account_authenticator" />
        </service>
        <service
            android:name=".sync.account.EeloAddressBookAuthenticatorService"
            android:exported="false">
            <intent-filter>
                <action android:name="android.accounts.AccountAuthenticator" />
            </intent-filter>

            <meta-data
                android:name="android.accounts.AccountAuthenticator"
                android:resource="@xml/account_authenticator_eelo_address_book" />
        </service>

        <!-- provider to share debug info/logs -->
        <provider
            android:name="androidx.core.content.FileProvider"
+32 −0
Original line number Diff line number Diff line
/*
 * Copyright MURENA SAS 2023
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

package at.bitfire.davdroid

import net.openid.appauth.ClientAuthentication
import net.openid.appauth.ClientSecretBasic
import net.openid.appauth.NoClientAuthentication

object OpenIdUtils {

    fun getClientAuthentication(secret: String?): ClientAuthentication {
        if (secret == null) {
            return NoClientAuthentication.INSTANCE
        }

        return ClientSecretBasic(secret)
    }
}
+1 −1
Original line number Diff line number Diff line
@@ -356,9 +356,9 @@ class AccountSettings @AssistedInject constructor(

        /** Stores the tasks sync interval (in seconds) so that it can be set again when the provider is switched */
        const val KEY_SYNC_INTERVAL_TASKS = "sync_interval_tasks"

        const val KEY_USERNAME = "user_name"
        const val KEY_CERTIFICATE_ALIAS = "certificate_alias"
        const val KEY_CLIENT_SECRET = "client_secret"

        const val CREDENTIALS_LOCK = "login_credentials_lock"
        const val CREDENTIALS_LOCK_NO_LOCK = 0
+165 −0
Original line number Diff line number Diff line
/***************************************************************************************************
 * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
 **************************************************************************************************/

package at.bitfire.davdroid.syncadapter

import android.accounts.Account
import android.accounts.AccountManager
import android.content.Context
import android.os.Bundle
import at.bitfire.davdroid.R
import at.bitfire.davdroid.settings.AccountSettings
import at.bitfire.davdroid.sync.account.setAndVerifyUserData
import com.owncloud.android.lib.common.accounts.AccountUtils

object AccountUtils {

    /**
     * Creates an account and makes sure the user data are set correctly.
     *
     * @param context  operating context
     * @param account  account to create
     * @param userData user data to set
     *
     * @return whether the account has been created
     *
     * @throws IllegalArgumentException when user data contains non-String values
     * @throws IllegalStateException if user data can't be set
     */
    fun createAccount(
        context: Context,
        account: Account,
        userData: Bundle,
        password: String? = null
    ): Boolean {
        // validate user data
        for (key in userData.keySet()) {
            userData.get(key)?.let { entry ->
                if (entry !is String)
                    throw IllegalArgumentException("userData[$key] is ${entry::class.java} (expected: String)")
            }
        }

        // create account
        val manager = AccountManager.get(context)
        if (!manager.addAccountExplicitly(account, password, userData))
            return false

        // Android seems to lose the initial user data sometimes, so set it a second time if that happens
        // https://forums.bitfire.at/post/11644
        if (!verifyUserData(context, account, userData))
            for (key in userData.keySet())
                manager.setAndVerifyUserData(account, key, userData.getString(key))

        if (!verifyUserData(context, account, userData))
            throw IllegalStateException("Android doesn't store user data in account")

        return true
    }

    private fun verifyUserData(context: Context, account: Account, userData: Bundle): Boolean {
        val accountManager = AccountManager.get(context)
        userData.keySet().forEach { key ->
            val stored = accountManager.getUserData(account, key)
            val expected = userData.getString(key)
            if (stored != expected) {
                return false
            }
        }
        return true
    }

    fun getMainAccountTypes(context: Context) =
        listOf(
            context.getString(R.string.account_type),
            context.getString(R.string.eelo_account_type),
            context.getString(R.string.google_account_type),
            context.getString(R.string.yahoo_account_type)
        )

    fun getMainAccounts(context: Context): List<Account> {
        val accountManager = AccountManager.get(context)
        val accounts = mutableListOf<Account>()

        getMainAccountTypes(context)
            .forEach {
                accounts.addAll(accountManager.getAccountsByType(it))
            }

        return accounts
    }

    fun getAddressBookAccountTypes(context: Context) =
        listOf(
            context.getString(R.string.account_type_address_book),
            context.getString(R.string.account_type_eelo_address_book),
            context.getString(R.string.account_type_google_address_book),
            context.getString(R.string.account_type_yahoo_address_book)
        )

    fun getAddressBookAccounts(context: Context): List<Account> {
        val accountManager = AccountManager.get(context)
        val accounts = mutableListOf<Account>()

        getAddressBookAccountTypes(context)
            .forEach {
                accounts.addAll(accountManager.getAccountsByType(it))
            }

        return accounts
    }

    fun getOpenIdMainAccountTypes(context: Context) =
        listOf(
            context.getString(R.string.eelo_account_type),
            context.getString(R.string.google_account_type),
            context.getString(R.string.yahoo_account_type)
        )

    fun getOpenIdMainAccounts(context: Context): List<Account> {
        val accountManager = AccountManager.get(context)
        val accounts = mutableListOf<Account>()

        getOpenIdMainAccountTypes(context)
            .forEach {
                accounts.addAll(accountManager.getAccountsByType(it))
            }

        return accounts
    }

    fun getOwnCloudBaseUrl(baseURL: String): String {
        if (baseURL.contains("/remote.php")) {
            return baseURL.split("/remote.php")[0]
        }

        return baseURL
    }

    fun getAccount(context: Context, userName: String?, requestedBaseUrl: String?): Account? {
        if (userName == null || requestedBaseUrl == null) {
            return null
        }

        val baseUrl = getOwnCloudBaseUrl(requestedBaseUrl)

        val accountManager = AccountManager.get(context.applicationContext)

        val accounts = getMainAccounts(context)

        for(account in accounts) {
            val name = accountManager.getUserData(account, AccountSettings.KEY_USERNAME)
            if (name != userName) {
                continue
            }

            val url = accountManager.getUserData(account, AccountUtils.Constants.KEY_OC_BASE_URL)
            if (url != null && getOwnCloudBaseUrl(url) == baseUrl) {
                return account
            }
        }

        return null
    }
}
Loading