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

Commit e4844060 authored by mitulsheth's avatar mitulsheth
Browse files

feat: Murena Workspace Login Part 1 of 11

parent 9f5ba835
Loading
Loading
Loading
Loading
Loading
+34 −2
Original line number Diff line number Diff line
@@ -43,7 +43,8 @@ import javax.inject.Singleton
data class MurenaWorkspaceConfig(
    val webDavUrl: String,
    val username: String,
    val credentials: MurenaAuthToken
    val credentials: MurenaAuthToken,
    val expectedHost: String? = null
)

object MurenaWorkspaceAccount {
@@ -55,6 +56,7 @@ object MurenaWorkspaceAccount {
    const val KEY_USER_ID = "oc_id"
    const val KEY_USED_QUOTA = "used_quota"
    const val KEY_TOTAL_QUOTA = "total_quota"
    const val KEY_EXPECTED_HOST = "expected_host"
    const val AUTH_TOKEN_TYPE = "oauth2-access-token"

    private const val DEFAULT_WEBDAV_FILES_PREFIX = "/remote.php/dav/files/"
@@ -203,6 +205,7 @@ interface MurenaAccountStore {
    fun getAccount(account: MurenaAccount): Account?
    fun getEmail(account: Account): String?
    fun getUserData(account: Account, key: String): String?
    fun setUserData(account: Account, key: String, value: String)
}

@Singleton
@@ -254,6 +257,14 @@ class AndroidMurenaAccountStore @Inject constructor(
            null
        }
    }

    override fun setUserData(account: Account, key: String, value: String) {
        try {
            accountManager.setUserData(account, key, value)
        } catch (exception: SecurityException) {
            Log.w(TAG, "Missing permission to write userData($key)", exception)
        }
    }
}

interface MurenaCredentialProvider {
@@ -432,6 +443,26 @@ class DefaultMurenaWorkspaceConfigFactory @Inject constructor(
            }
        }

        val currentHost = Uri.parse(baseUrl).host
        if (currentHost != null) {
            val expectedHost = accountStore.getUserData(
                account, MurenaWorkspaceAccount.KEY_EXPECTED_HOST
            )
            if (expectedHost == null) {
                accountStore.setUserData(
                    account, MurenaWorkspaceAccount.KEY_EXPECTED_HOST, currentHost
                )
            } else if (!currentHost.equals(expectedHost, ignoreCase = true)) {
                return Result.Error(MurenaSyncError.UNTRUSTED_HOST).also {
                    Log.w(
                        TAG,
                        "Murena Workspace host changed from $expectedHost to $currentHost " +
                            "for ${account.name.redactedAccountName()}"
                    )
                }
            }
        }

        val userId = accountStore.getUserData(account, MurenaWorkspaceAccount.KEY_USER_ID)
            ?: accountStore.getUserData(account, MurenaWorkspaceAccount.KEY_USERNAME)
            ?: fallbackWorkspaceUserId(account).also {
@@ -452,7 +483,8 @@ class DefaultMurenaWorkspaceConfigFactory @Inject constructor(
            MurenaWorkspaceConfig(
                webDavUrl = MurenaWorkspaceAccount.buildWebDavUrl(baseUrl, userId),
                username = userId,
                credentials = credentials
                credentials = credentials,
                expectedHost = currentHost
            )
        )
    }
+1 −0
Original line number Diff line number Diff line
@@ -56,6 +56,7 @@ class MurenaSyncWorker(
                MurenaSyncError.ACCOUNT_NOT_FOUND,
                MurenaSyncError.NO_BASE_URL,
                MurenaSyncError.INSECURE_BASE_URL,
                MurenaSyncError.UNTRUSTED_HOST,
                MurenaSyncError.AUTH_TOKEN_UNAVAILABLE,
                MurenaSyncError.UNAUTHORIZED,
                MurenaSyncError.TLS_ERROR,
+18 −3
Original line number Diff line number Diff line
@@ -147,12 +147,27 @@ class OkHttpMurenaDriveDataSource @Inject constructor(

    private fun MurenaWorkspaceConfig.validateSecureWebDavUrl(): Result.Error<MurenaSyncError>? {
        val parsedUrl = webDavUrl.toHttpUrlOrNull()
        if (parsedUrl?.isHttps == true) return null
            ?: return Result.Error(MurenaSyncError.INSECURE_BASE_URL).also {
                Log.w(TAG, "Blocked Murena sync request to unparseable WebDAV URL")
            }

        if (!parsedUrl.isHttps) {
            Log.w(TAG, "Blocked Murena sync request to insecure WebDAV URL")
            return Result.Error(MurenaSyncError.INSECURE_BASE_URL)
        }

        if (expectedHost != null && !parsedUrl.host.equals(expectedHost, ignoreCase = true)) {
            Log.w(
                TAG,
                "Blocked Murena sync request to unexpected host=${parsedUrl.host}" +
                    " expected=$expectedHost"
            )
            return Result.Error(MurenaSyncError.UNTRUSTED_HOST)
        }

        return null
    }

    private fun Request.Builder.applyWriteCondition(
        writeCondition: RemoteFavoritesWriteCondition
    ): Request.Builder {
+1 −0
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ enum class MurenaSyncError : Error {
    ACCOUNT_NOT_FOUND,
    NO_BASE_URL,
    INSECURE_BASE_URL,
    UNTRUSTED_HOST,
    AUTH_TOKEN_UNAVAILABLE,
    UNAUTHORIZED,
    NO_INTERNET,
+1 −0
Original line number Diff line number Diff line
@@ -332,6 +332,7 @@
    <string name="murena_sync_tls_error">Murena Workspace connection has a TLS or certificate error.</string>
    <string name="murena_sync_timeout">Murena Workspace request timed out.</string>
    <string name="murena_sync_connectivity">Could not reach Murena Workspace.</string>
    <string name="murena_sync_host_untrusted">Murena Workspace server host changed. Re-add your account to trust the new server.</string>
    <string name="murena_sync_unknown_error">Favorites sync failed.</string>
    <string name="murena_sync_complete">Favorites sync complete.</string>
</resources>