diff --git a/app/src/main/java/com/owncloud/android/ui/activity/SsoGrantPermissionActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/SsoGrantPermissionActivity.java deleted file mode 100644 index 8c1de631f7f88cee2a67d118e7c2a2c540493c43..0000000000000000000000000000000000000000 --- a/app/src/main/java/com/owncloud/android/ui/activity/SsoGrantPermissionActivity.java +++ /dev/null @@ -1,174 +0,0 @@ -/* - * 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 . - */ - -package com.owncloud.android.ui.activity; - -import static com.nextcloud.android.sso.Constants.DELIMITER; -import static com.nextcloud.android.sso.Constants.EXCEPTION_ACCOUNT_ACCESS_DECLINED; -import static com.nextcloud.android.sso.Constants.EXCEPTION_ACCOUNT_NOT_FOUND; -import static com.nextcloud.android.sso.Constants.NEXTCLOUD_FILES_ACCOUNT; -import static com.nextcloud.android.sso.Constants.NEXTCLOUD_SSO; -import static com.nextcloud.android.sso.Constants.NEXTCLOUD_SSO_EXCEPTION; -import static com.nextcloud.android.sso.Constants.SSO_SERVER_URL; -import static com.nextcloud.android.sso.Constants.SSO_SHARED_PREFERENCE; -import static com.nextcloud.android.sso.Constants.SSO_TOKEN; -import static com.nextcloud.android.sso.Constants.SSO_USER_ID; - -import android.accounts.Account; -import android.accounts.AccountManager; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.SharedPreferences; -import android.os.Bundle; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.app.AppCompatActivity; - -import com.nextcloud.android.utils.EncryptionUtils; -import com.owncloud.android.lib.common.OwnCloudAccount; -import com.owncloud.android.lib.common.accounts.AccountUtils; - -import java.util.Arrays; -import java.util.UUID; -import java.util.logging.Level; - -import at.bitfire.davdroid.R; -import at.bitfire.davdroid.log.Logger; -import at.bitfire.davdroid.util.SsoUtils; - -public class SsoGrantPermissionActivity extends AppCompatActivity { - - private static final String[] ACCEPTED_PACKAGE_LIST = {"foundation.e.notes"}; - - private Account account; - private String packageName; - - @Override - protected void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_sso_grant_permission); - - ComponentName callingActivity = getCallingActivity(); - - if (callingActivity != null) { - packageName = callingActivity.getPackageName(); - account = getIntent().getParcelableExtra(NEXTCLOUD_FILES_ACCOUNT); - validateAndAutoGrandPermission(); - } else { - Logger.INSTANCE.getLog().log(Level.SEVERE, "SsoGrantPermissionActivity: Calling Package is null"); - setResultAndExit(EXCEPTION_ACCOUNT_ACCESS_DECLINED); - } - } - - private void validateAndAutoGrandPermission() { - if (!isValidRequest()) { - Logger.INSTANCE.getLog().log(Level.SEVERE, "SsoGrantPermissionActivity: Invalid request"); - setResultAndExit(EXCEPTION_ACCOUNT_ACCESS_DECLINED); - return; - } - - grantPermission(); - } - - private boolean isValidRequest() { - if (packageName == null || account == null) { - return false; - } - - boolean validPackage = Arrays.asList(ACCEPTED_PACKAGE_LIST) - .contains(packageName); - - if (!validPackage) { - return false; - } - - return Arrays.asList(getAcceptedAccountTypeList()) - .contains(account.type); - } - - private String[] getAcceptedAccountTypeList() { - return new String[]{ - getString(R.string.eelo_account_type) - }; - } - - private void grantPermission() { - String serverUrl = getServerUrl(); - - if (serverUrl == null) { - return; - } - - // create token - String token = UUID.randomUUID().toString().replaceAll("-", ""); - String userId = getUserId(); - - saveToken(token, account.name); - setResultData(token, userId, serverUrl); - finish(); - } - - @NonNull - private String getUserId() { - final AccountManager accountManager = AccountManager.get(this); - final String baseUrl = accountManager.getUserData(account, AccountUtils.Constants.KEY_OC_BASE_URL); - return SsoUtils.INSTANCE.sanitizeUserId(account.name, baseUrl); - } - - private void setResultData(String token, String userId, String serverUrl) { - final Bundle result = new Bundle(); - result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name); - result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type); - result.putString(AccountManager.KEY_AUTHTOKEN, NEXTCLOUD_SSO); - result.putString(SSO_USER_ID, userId); - result.putString(SSO_TOKEN, token); - result.putString(SSO_SERVER_URL, serverUrl); - - Intent data = new Intent(); - data.putExtra(NEXTCLOUD_SSO, result); - setResult(RESULT_OK, data); - } - - @Nullable - private String getServerUrl() { - try { - OwnCloudAccount ocAccount = new OwnCloudAccount(account, this); - return ocAccount.getBaseUri().toString(); - } catch (AccountUtils.AccountNotFoundException e) { - Logger.INSTANCE.getLog().log(Level.SEVERE, "SsoGrantPermissionActivity: Account not found"); - setResultAndExit(EXCEPTION_ACCOUNT_NOT_FOUND); - } - - return null; - } - - private void saveToken(String token, String accountName) { - String hashedTokenWithSalt = EncryptionUtils.generateSHA512(token); - SharedPreferences sharedPreferences = getSharedPreferences(SSO_SHARED_PREFERENCE, Context.MODE_PRIVATE); - SharedPreferences.Editor editor = sharedPreferences.edit(); - editor.putString(packageName + DELIMITER + accountName, hashedTokenWithSalt); - editor.apply(); - } - - private void setResultAndExit(String exception) { - Intent data = new Intent(); - data.putExtra(NEXTCLOUD_SSO_EXCEPTION, exception); - setResult(RESULT_CANCELED, data); - finish(); - } -} \ No newline at end of file diff --git a/app/src/main/java/com/owncloud/android/ui/activity/SsoGrantPermissionActivity.kt b/app/src/main/java/com/owncloud/android/ui/activity/SsoGrantPermissionActivity.kt new file mode 100644 index 0000000000000000000000000000000000000000..ba791f31d1740bc302136cada0f9d25cb778b4db --- /dev/null +++ b/app/src/main/java/com/owncloud/android/ui/activity/SsoGrantPermissionActivity.kt @@ -0,0 +1,86 @@ +/* + * Copyright MURENA SAS 2024 + * 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 . + */ + +package com.owncloud.android.ui.activity + +import android.accounts.Account +import android.content.Intent +import android.os.Build +import android.os.Bundle +import androidx.activity.viewModels +import androidx.appcompat.app.AppCompatActivity +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.flowWithLifecycle +import androidx.lifecycle.lifecycleScope +import at.bitfire.davdroid.R +import com.nextcloud.android.sso.Constants +import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.launch + +@AndroidEntryPoint +class SsoGrantPermissionActivity : AppCompatActivity() { + + private val viewModel: SsoGrantPermissionViewModel by viewModels() + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_sso_grant_permission) + + lifecycleScope.launch { + viewModel.permissionEvent + .flowWithLifecycle( + lifecycle = lifecycle, + minActiveState = Lifecycle.State.CREATED + ).collectLatest { + when (it) { + is SsoGrantPermissionEvent.PermissionGranted -> setSuccessResult(it.bundle) + is SsoGrantPermissionEvent.PermissionDenied -> setCanceledResult(it.errorMessage) + } + } + } + + validateAccount() + } + + private fun validateAccount() { + val account: Account? = + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + intent.getParcelableExtra(Constants.NEXTCLOUD_FILES_ACCOUNT, Account::class.java) + } else { + intent.getParcelableExtra(Constants.NEXTCLOUD_FILES_ACCOUNT) + } + + viewModel.initValidation( + callingActivity = callingActivity, + account = account + ) + } + + private fun setCanceledResult(exception: String) { + val data = Intent() + data.putExtra(Constants.NEXTCLOUD_SSO_EXCEPTION, exception) + setResult(RESULT_CANCELED, data) + finish() + } + + private fun setSuccessResult(result: Bundle) { + val data = Intent() + data.putExtra(Constants.NEXTCLOUD_SSO, result) + setResult(RESULT_OK, data) + finish() + } +} diff --git a/app/src/main/java/com/owncloud/android/ui/activity/SsoGrantPermissionEvent.kt b/app/src/main/java/com/owncloud/android/ui/activity/SsoGrantPermissionEvent.kt new file mode 100644 index 0000000000000000000000000000000000000000..2e69754c8b34bdf4a298347dd46fcfb49ad76e5d --- /dev/null +++ b/app/src/main/java/com/owncloud/android/ui/activity/SsoGrantPermissionEvent.kt @@ -0,0 +1,26 @@ +/* + * Copyright MURENA SAS 2024 + * 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 . + */ + +package com.owncloud.android.ui.activity + +import android.os.Bundle + +sealed class SsoGrantPermissionEvent { + + data class PermissionGranted(val bundle: Bundle) : SsoGrantPermissionEvent() + + data class PermissionDenied(val errorMessage: String) : SsoGrantPermissionEvent() +} diff --git a/app/src/main/java/com/owncloud/android/ui/activity/SsoGrantPermissionViewModel.kt b/app/src/main/java/com/owncloud/android/ui/activity/SsoGrantPermissionViewModel.kt new file mode 100644 index 0000000000000000000000000000000000000000..51ecf4cf014c7b89525b874704d920405e7dccdd --- /dev/null +++ b/app/src/main/java/com/owncloud/android/ui/activity/SsoGrantPermissionViewModel.kt @@ -0,0 +1,172 @@ +/* + * Copyright MURENA SAS 2024 + * 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 . + */ + +package com.owncloud.android.ui.activity + +import android.accounts.Account +import android.accounts.AccountManager +import android.content.ComponentName +import android.content.Context +import android.os.Bundle +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import at.bitfire.davdroid.R +import at.bitfire.davdroid.db.AppDatabase +import at.bitfire.davdroid.log.Logger.log +import at.bitfire.davdroid.util.UserIdFetcher +import com.nextcloud.android.sso.Constants +import com.nextcloud.android.utils.EncryptionUtils +import com.owncloud.android.lib.common.OwnCloudAccount +import com.owncloud.android.lib.common.accounts.AccountUtils +import dagger.hilt.android.lifecycle.HiltViewModel +import dagger.hilt.android.qualifiers.ApplicationContext +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.asSharedFlow +import kotlinx.coroutines.launch +import java.util.UUID +import java.util.logging.Level +import javax.inject.Inject + +@HiltViewModel +class SsoGrantPermissionViewModel @Inject constructor( + @ApplicationContext private val context: Context, + private val database: AppDatabase, +) : ViewModel() { + + private val acceptedAccountTypes = listOf(context.getString(R.string.eelo_account_type)) + private val acceptedPackages = listOf("foundation.e.notes") + + private val _permissionEvent = MutableSharedFlow() + val permissionEvent = _permissionEvent.asSharedFlow() + + fun initValidation(callingActivity: ComponentName?, account: Account?) { + viewModelScope.launch(Dispatchers.IO) { + val packageName = getCallingPackageName(callingActivity) ?: return@launch + validate(packageName, account) + } + } + + private suspend fun emitPermissionDeniedEvent(message: String) { + _permissionEvent.emit( + SsoGrantPermissionEvent.PermissionDenied( + errorMessage = message + ) + ) + } + + private suspend fun getCallingPackageName(callingActivity: ComponentName?): String? { + if (callingActivity != null) { + return callingActivity.packageName + } + + log.log(Level.SEVERE, "SsoGrantPermissionViewModel: Calling Package is null") + emitPermissionDeniedEvent(Constants.EXCEPTION_ACCOUNT_ACCESS_DECLINED) + return null + } + + private suspend fun validate(packageName: String?, account: Account?) { + if (!isValidRequest(packageName, account)) { + log.log(Level.SEVERE, "SsoGrantPermissionViewModel: Invalid request") + emitPermissionDeniedEvent(Constants.EXCEPTION_ACCOUNT_ACCESS_DECLINED) + } + + val serverUrl = getServerUrl(account!!) ?: return + + val token = UUID.randomUUID().toString().replace("-".toRegex(), "") + val userId = getUserId(account) + + saveToken( + token = token, + accountName = userId, + packageName = packageName!! + ) + + passSuccessfulData( + account = account, + token = token, + userId = userId, + serverUrl = serverUrl + ) + + } + + private fun isValidRequest(packageName: String?, account: Account?): Boolean { + if (packageName == null || account == null) { + return false + } + + return acceptedPackages.contains(packageName) && acceptedAccountTypes.contains(account.type) + } + + private suspend fun getServerUrl(account: Account): String? { + try { + val ocAccount = OwnCloudAccount(account, context) + return ocAccount.baseUri.toString() + } catch (e: AccountUtils.AccountNotFoundException) { + log.log(Level.SEVERE, "SsoGrantPermissionViewModel: Account not found") + emitPermissionDeniedEvent(Constants.EXCEPTION_ACCOUNT_NOT_FOUND) + } + + return null + } + + private fun getUserId(account: Account): String { + val accountManager = AccountManager.get(context) + val userId = accountManager.getUserData(account, AccountUtils.Constants.KEY_USER_ID) + + if (!userId.isNullOrBlank()) { + return userId + } + + val principalUrl = + database.serviceDao().getByAccountName(account.name)?.principal?.toString() + ?: return account.name + + return UserIdFetcher.fetch(principalUrl) ?: account.name + } + + private fun saveToken(token: String, accountName: String, packageName: String) { + val hashedTokenWithSalt = EncryptionUtils.generateSHA512(token) + val sharedPreferences = + context.getSharedPreferences(Constants.SSO_SHARED_PREFERENCE, Context.MODE_PRIVATE) + val editor = sharedPreferences.edit() + editor.putString(packageName + Constants.DELIMITER + accountName, hashedTokenWithSalt) + editor.apply() + } + + private suspend fun passSuccessfulData( + account: Account, + token: String, + userId: String, + serverUrl: String + ) { + val result = Bundle().apply { + putString(AccountManager.KEY_ACCOUNT_NAME, account.name) + putString(AccountManager.KEY_ACCOUNT_TYPE, account.type) + putString(AccountManager.KEY_AUTHTOKEN, Constants.NEXTCLOUD_SSO) + putString(Constants.SSO_USER_ID, userId) + putString(Constants.SSO_TOKEN, token) + putString(Constants.SSO_SERVER_URL, serverUrl) + } + + _permissionEvent.emit( + SsoGrantPermissionEvent.PermissionGranted( + bundle = result + ) + ) + } +} diff --git a/app/src/main/kotlin/at/bitfire/davdroid/settings/AccountSettings.kt b/app/src/main/kotlin/at/bitfire/davdroid/settings/AccountSettings.kt index 39dee0e6b47b695cba3bdd2b8056c3e756234d88..bab2fd4ad333cfa8d0eddfa78e068bb47ae23bd7 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/settings/AccountSettings.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/settings/AccountSettings.kt @@ -19,8 +19,8 @@ import at.bitfire.davdroid.resource.LocalAddressBook import at.bitfire.davdroid.syncadapter.AccountUtils import at.bitfire.davdroid.syncadapter.PeriodicSyncWorker import at.bitfire.davdroid.syncadapter.SyncUtils +import at.bitfire.davdroid.util.UserIdFetcher import at.bitfire.davdroid.util.AuthStatePrefUtils -import at.bitfire.davdroid.util.SsoUtils import at.bitfire.davdroid.util.setAndVerifyUserData import at.bitfire.ical4android.TaskProvider import at.bitfire.vcard4android.GroupMethod @@ -158,21 +158,38 @@ class AccountSettings( bundle.putString(COOKIE_KEY, cookies) } - var baseUrl : String? = null if (!url.isNullOrEmpty()) { - baseUrl = AccountUtils.getOwnCloudBaseUrl(url) + val baseUrl = AccountUtils.getOwnCloudBaseUrl(url) bundle.putString(NCAccountUtils.Constants.KEY_OC_BASE_URL, baseUrl) } - addUserIdToBundle(bundle, userName, baseUrl) - addEmailToBundle(bundle, email, userName) + addUserIdToBundle( + bundle = bundle, + url = url, + userName = userName + ) + + addEmailToBundle( + bundle = bundle, + email = email, + userName = userName + ) return bundle } - private fun addUserIdToBundle(bundle: Bundle, userName: String?, baseUrl: String?) { - userName?.let { - val userId = SsoUtils.sanitizeUserId(it, baseUrl) + private fun addUserIdToBundle(bundle: Bundle, url: String?, userName: String?) { + var userId: String? = null + + if (!url.isNullOrBlank()) { + userId = UserIdFetcher.fetch(url) + } + + if (userId.isNullOrBlank()) { + userId = userName + } + + userId?.let { bundle.putString(NCAccountUtils.Constants.KEY_USER_ID, userId) } } diff --git a/app/src/main/kotlin/at/bitfire/davdroid/ui/setup/AccountDetailsFragment.kt b/app/src/main/kotlin/at/bitfire/davdroid/ui/setup/AccountDetailsFragment.kt index 327163806d60e1f24ec16b7344d58a55002ed906..a7cf8c1d96ab02fc96755cbd5b1b9ddad27f8316 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/ui/setup/AccountDetailsFragment.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/ui/setup/AccountDetailsFragment.kt @@ -280,14 +280,17 @@ class AccountDetailsFragment : Fragment() { var baseURL : String? = null if (config.calDAV != null) { - baseURL = config.calDAV.principal.toString() + baseURL = config.calDAV.principal?.toString() + } + + if (baseURL == null) { + baseURL = config.cardDAV?.principal?.toString() } when (activity.intent.getStringExtra(LoginActivity.ACCOUNT_TYPE)) { context.getString(R.string.eelo_account_type) -> { accountType = context.getString(R.string.eelo_account_type) addressBookAccountType = context.getString(R.string.account_type_eelo_address_book) - baseURL = credentials?.serverUri.toString() } context.getString(R.string.google_account_type) -> { accountType = context.getString(R.string.google_account_type) @@ -474,7 +477,5 @@ class AccountDetailsFragment : Fragment() { return serviceId } - } - -} \ No newline at end of file +} diff --git a/app/src/main/kotlin/at/bitfire/davdroid/util/SsoUtils.kt b/app/src/main/kotlin/at/bitfire/davdroid/util/SsoUtils.kt deleted file mode 100644 index 7836933cd3ac308d7cd2bacb19ff7e71b1fe1a07..0000000000000000000000000000000000000000 --- a/app/src/main/kotlin/at/bitfire/davdroid/util/SsoUtils.kt +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright MURENA SAS 2024 - * 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 . - */ - -package at.bitfire.davdroid.util - -import java.net.URI - -object SsoUtils { - - /** - * This method removes the baseUrl's host part from the accountName to retrieve userId - * for ex: accountName: abc@murena.io; baseUrl: https://murena.io; so userID: abc - */ - fun sanitizeUserId(accountName: String, baseUrl: String?): String { - val userId = accountName.trim() - - val host = getHost(baseUrl) ?: return userId - val userNameEndPart = "@$host" - - if (!userId.endsWith(userNameEndPart, ignoreCase = true)) { - return userId - } - - return userId.substring(0, userId.lastIndexOf(userNameEndPart, ignoreCase = true)) - } - - private fun getHost(baseUrl: String?): String? { - if (baseUrl == null) { - return null - } - - val uri = URI.create(baseUrl.trim()) - return uri.host - } -} diff --git a/app/src/main/kotlin/at/bitfire/davdroid/util/UserIdFetcher.kt b/app/src/main/kotlin/at/bitfire/davdroid/util/UserIdFetcher.kt new file mode 100644 index 0000000000000000000000000000000000000000..16c7bf8a991a2442cc7e3e2c13946d0ab399c429 --- /dev/null +++ b/app/src/main/kotlin/at/bitfire/davdroid/util/UserIdFetcher.kt @@ -0,0 +1,45 @@ +/* + * Copyright MURENA SAS 2024 + * 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 . + */ + +package at.bitfire.davdroid.util + +object UserIdFetcher { + + /** + * retrieve the userId from caldav/carddav nextcloud principal url. + * example: if the url is: https://abc.com/remote.php/dav/principals/users/xyz/, then this function will return xyz. + * + * this function will return null, if + * - `/users/` part is missing + */ + fun fetch(principalUrl: String): String? { + val usersPart = "/users/" + + var userId: String? = null + if (principalUrl.contains(usersPart, ignoreCase = true)) { + userId = principalUrl.split(usersPart, ignoreCase = true)[1] + if (userId.endsWith("/")) { + userId = userId.dropLast(1) + } + + if (userId.isBlank()) { + userId = null + } + } + + return userId + } +} diff --git a/app/src/test/kotlin/at/bitfire/davdroid/util/SsoUtilsTest.kt b/app/src/test/kotlin/at/bitfire/davdroid/util/UserIdFetcherTest.kt similarity index 51% rename from app/src/test/kotlin/at/bitfire/davdroid/util/SsoUtilsTest.kt rename to app/src/test/kotlin/at/bitfire/davdroid/util/UserIdFetcherTest.kt index e14b20e9d833216ec27f4f388ed84ff6258ea650..467e86774c8e7052ed46dc51b7b29d6c154c3818 100644 --- a/app/src/test/kotlin/at/bitfire/davdroid/util/SsoUtilsTest.kt +++ b/app/src/test/kotlin/at/bitfire/davdroid/util/UserIdFetcherTest.kt @@ -17,42 +17,38 @@ package at.bitfire.davdroid.util import org.junit.Assert.assertEquals +import org.junit.Assert.assertNull import org.junit.Test -class SsoUtilsTest { +class UserIdFetcherTest { @Test - fun `test sanitizeUserId with empty input`() { - val userId = "" - val expected = "" - val actual = SsoUtils.sanitizeUserId(userId, null) - assertEquals(expected, actual) + fun `test fetch with input without 'users' part`() { + val url = "https://murena.io" + val actual = UserIdFetcher.fetch(url) + assertNull(actual) } @Test - fun `test sanitizeUserId with input without url part`() { - val userId = "username" - val url = "https://murena.io" - val expected = "username" - val actual = SsoUtils.sanitizeUserId(userId, url) - assertEquals(expected, actual) + fun `test fetch with input ends with 'users'`() { + val baseUrl = "https://murena.io/remote.php/dav/principals/users/" + val actual = UserIdFetcher.fetch(baseUrl) + assertNull(actual) } @Test - fun `test sanitizeUserId with case sensitivity`() { - val userId = "User@E.EMAIL@MURENA.io" - val baseUrl = "https://murena.io/" - val expected = "User@E.EMAIL" - val actual = SsoUtils.sanitizeUserId(userId, baseUrl) + fun `test fetch with input with 'users' part`() { + val baseUrl = "https://murena.io/remote.php/dav/principals/users/user@e.email" + val expected = "user@e.email" + val actual = UserIdFetcher.fetch(baseUrl) assertEquals(expected, actual) } @Test - fun `test sanitizeUserId with leading or trailing spaces`() { - val userId = " user@domain.com " - val url = " http://domain.com " - val expected = "user" - val actual = SsoUtils.sanitizeUserId(userId, url) + fun `test fetch with input with 'users' part & ends with slash`() { + val baseUrl = "https://murena.io/remote.php/dav/principals/users/user@e.email/" + val expected = "user@e.email" + val actual = UserIdFetcher.fetch(baseUrl) assertEquals(expected, actual) } }