Implement URL input format checks
This commit is contained in:
@@ -1,10 +1,14 @@
|
|||||||
package gq.kirmanak.mealient.data.auth.impl
|
package gq.kirmanak.mealient.data.auth.impl
|
||||||
|
|
||||||
|
import android.net.Uri
|
||||||
|
import androidx.annotation.VisibleForTesting
|
||||||
import gq.kirmanak.mealient.data.auth.AuthDataSource
|
import gq.kirmanak.mealient.data.auth.AuthDataSource
|
||||||
import gq.kirmanak.mealient.data.auth.AuthRepo
|
import gq.kirmanak.mealient.data.auth.AuthRepo
|
||||||
import gq.kirmanak.mealient.data.auth.AuthStorage
|
import gq.kirmanak.mealient.data.auth.AuthStorage
|
||||||
|
import gq.kirmanak.mealient.data.auth.impl.AuthenticationError.MalformedUrl
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
|
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@@ -18,7 +22,7 @@ class AuthRepoImpl @Inject constructor(
|
|||||||
baseUrl: String
|
baseUrl: String
|
||||||
) {
|
) {
|
||||||
Timber.v("authenticate() called with: username = $username, password = $password, baseUrl = $baseUrl")
|
Timber.v("authenticate() called with: username = $username, password = $password, baseUrl = $baseUrl")
|
||||||
val url = if (baseUrl.startsWith("http")) baseUrl else "https://$baseUrl"
|
val url = parseBaseUrl(baseUrl)
|
||||||
val accessToken = dataSource.authenticate(username, password, url)
|
val accessToken = dataSource.authenticate(username, password, url)
|
||||||
Timber.d("authenticate result is $accessToken")
|
Timber.d("authenticate result is $accessToken")
|
||||||
storage.storeAuthData(accessToken, url)
|
storage.storeAuthData(accessToken, url)
|
||||||
@@ -40,4 +44,17 @@ class AuthRepoImpl @Inject constructor(
|
|||||||
Timber.v("logout() called")
|
Timber.v("logout() called")
|
||||||
storage.clearAuthData()
|
storage.clearAuthData()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
fun parseBaseUrl(baseUrl: String): String = try {
|
||||||
|
val withScheme = Uri.parse(baseUrl).let {
|
||||||
|
if (it.scheme == null) it.buildUpon().scheme("https").build()
|
||||||
|
else it
|
||||||
|
}.toString()
|
||||||
|
withScheme.toHttpUrl().toString()
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
Timber.e(e, "authenticate: can't parse base url $baseUrl")
|
||||||
|
throw MalformedUrl(e)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -4,4 +4,5 @@ sealed class AuthenticationError(cause: Throwable) : RuntimeException(cause) {
|
|||||||
class Unauthorized(cause: Throwable) : AuthenticationError(cause)
|
class Unauthorized(cause: Throwable) : AuthenticationError(cause)
|
||||||
class NoServerConnection(cause: Throwable) : AuthenticationError(cause)
|
class NoServerConnection(cause: Throwable) : AuthenticationError(cause)
|
||||||
class NotMealie(cause: Throwable) : AuthenticationError(cause)
|
class NotMealie(cause: Throwable) : AuthenticationError(cause)
|
||||||
|
class MalformedUrl(cause: Throwable) : AuthenticationError(cause)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -85,9 +85,13 @@ class AuthenticationFragment : Fragment() {
|
|||||||
is Unauthorized -> getString(R.string.fragment_authentication_credentials_incorrect)
|
is Unauthorized -> getString(R.string.fragment_authentication_credentials_incorrect)
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
urlInputLayout.error = when (it.exceptionOrNull()) {
|
urlInputLayout.error = when (val exception = it.exceptionOrNull()) {
|
||||||
is NoServerConnection -> getString(R.string.fragment_authentication_no_connection)
|
is NoServerConnection -> getString(R.string.fragment_authentication_no_connection)
|
||||||
is NotMealie -> getString(R.string.fragment_authentication_unexpected_response)
|
is NotMealie -> getString(R.string.fragment_authentication_unexpected_response)
|
||||||
|
is MalformedUrl -> {
|
||||||
|
val cause = exception.cause?.message ?: exception.message
|
||||||
|
getString(R.string.fragment_authentication_url_invalid, cause)
|
||||||
|
}
|
||||||
is Unauthorized, null -> null
|
is Unauthorized, null -> null
|
||||||
else -> getString(R.string.fragment_authentication_unknown_error)
|
else -> getString(R.string.fragment_authentication_unknown_error)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,4 +21,5 @@
|
|||||||
<string name="fragment_authentication_no_connection">Ошибка подключения, проверьте адрес.</string>
|
<string name="fragment_authentication_no_connection">Ошибка подключения, проверьте адрес.</string>
|
||||||
<string name="fragment_authentication_unexpected_response">Неожиданный ответ. Это Mealie?</string>
|
<string name="fragment_authentication_unexpected_response">Неожиданный ответ. Это Mealie?</string>
|
||||||
<string name="fragment_authentication_unknown_error">Что-то пошло не так, попробуйте еще раз.</string>
|
<string name="fragment_authentication_unknown_error">Что-то пошло не так, попробуйте еще раз.</string>
|
||||||
|
<string name="fragment_authentication_url_invalid">Проверьте формат URL: %s</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -23,4 +23,5 @@
|
|||||||
<string name="fragment_authentication_no_connection">Can\'t connect, check address.</string>
|
<string name="fragment_authentication_no_connection">Can\'t connect, check address.</string>
|
||||||
<string name="fragment_authentication_unexpected_response">Unexpected response. Is it Mealie?</string>
|
<string name="fragment_authentication_unexpected_response">Unexpected response. Is it Mealie?</string>
|
||||||
<string name="fragment_authentication_unknown_error">Something went wrong, please try again.</string>
|
<string name="fragment_authentication_unknown_error">Something went wrong, please try again.</string>
|
||||||
|
<string name="fragment_authentication_url_invalid">Check URL format: %s</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -2,6 +2,7 @@ package gq.kirmanak.mealient.data.auth.impl
|
|||||||
|
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
import dagger.hilt.android.testing.HiltAndroidTest
|
import dagger.hilt.android.testing.HiltAndroidTest
|
||||||
|
import gq.kirmanak.mealient.data.auth.impl.AuthenticationError.MalformedUrl
|
||||||
import gq.kirmanak.mealient.data.auth.impl.AuthenticationError.Unauthorized
|
import gq.kirmanak.mealient.data.auth.impl.AuthenticationError.Unauthorized
|
||||||
import gq.kirmanak.mealient.test.AuthImplTestData.TEST_PASSWORD
|
import gq.kirmanak.mealient.test.AuthImplTestData.TEST_PASSWORD
|
||||||
import gq.kirmanak.mealient.test.AuthImplTestData.TEST_TOKEN
|
import gq.kirmanak.mealient.test.AuthImplTestData.TEST_TOKEN
|
||||||
@@ -50,4 +51,29 @@ class AuthRepoImplTest : MockServerTest() {
|
|||||||
subject.authenticate(TEST_USERNAME, TEST_PASSWORD, serverUrl)
|
subject.authenticate(TEST_USERNAME, TEST_PASSWORD, serverUrl)
|
||||||
assertThat(subject.getBaseUrl()).isEqualTo(serverUrl)
|
assertThat(subject.getBaseUrl()).isEqualTo(serverUrl)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test(expected = MalformedUrl::class)
|
||||||
|
fun `when baseUrl has ftp scheme then throws`() {
|
||||||
|
subject.parseBaseUrl("ftp://test")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `when baseUrl scheme has one slash then corrects`() {
|
||||||
|
assertThat(subject.parseBaseUrl("https:/test")).isEqualTo("https://test/")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `when baseUrl is single word then appends scheme and slash`() {
|
||||||
|
assertThat(subject.parseBaseUrl("test")).isEqualTo("https://test/")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `when baseUrl is host appends scheme and slash`() {
|
||||||
|
assertThat(subject.parseBaseUrl("google.com")).isEqualTo("https://google.com/")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `when baseUrl is correct then doesn't change`() {
|
||||||
|
assertThat(subject.parseBaseUrl("https://google.com/")).isEqualTo("https://google.com/")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user