diff --git a/android/src/main/kotlin/com/onegini/mobile/sdk/flutter/FlutterOneWelcomeSdkComponent.kt b/android/src/main/kotlin/com/onegini/mobile/sdk/flutter/FlutterOneWelcomeSdkComponent.kt index 7b452cbd..6f847e58 100644 --- a/android/src/main/kotlin/com/onegini/mobile/sdk/flutter/FlutterOneWelcomeSdkComponent.kt +++ b/android/src/main/kotlin/com/onegini/mobile/sdk/flutter/FlutterOneWelcomeSdkComponent.kt @@ -1,10 +1,11 @@ package com.onegini.mobile.sdk.flutter +import com.onegini.mobile.sdk.flutter.module.FacadeModule import com.onegini.mobile.sdk.flutter.module.FlutterOneWelcomeSdkModule import dagger.Component import javax.inject.Singleton -@Component(modules = [FlutterOneWelcomeSdkModule::class]) +@Component(modules = [FlutterOneWelcomeSdkModule::class, FacadeModule::class]) @Singleton interface FlutterOneWelcomeSdkComponent { diff --git a/android/src/main/kotlin/com/onegini/mobile/sdk/flutter/OneWelcomeWrapperErrors.kt b/android/src/main/kotlin/com/onegini/mobile/sdk/flutter/OneWelcomeWrapperErrors.kt index 710d1211..9f1bb7b7 100644 --- a/android/src/main/kotlin/com/onegini/mobile/sdk/flutter/OneWelcomeWrapperErrors.kt +++ b/android/src/main/kotlin/com/onegini/mobile/sdk/flutter/OneWelcomeWrapperErrors.kt @@ -20,7 +20,6 @@ enum class OneWelcomeWrapperErrors(val code: Int, val message: String) { IDENTITY_PROVIDER_NOT_FOUND(8005, "The requested identity provider is not found"), QR_CODE_HAS_NO_DATA(8006, "QR-code does not have data"), METHOD_TO_CALL_NOT_FOUND(8007, "Method to call not found"), - URL_CANT_BE_NULL(8008, "Url can not be null"), MALFORMED_URL(8009, "Incorrect url format"), PREFERRED_AUTHENTICATOR_ERROR(8010, "Something went wrong when setting the preferred authenticator"), ONEWELCOME_SDK_NOT_INITIALIZED(8012, "OneWelcomeSDK is not initialized"), diff --git a/android/src/main/kotlin/com/onegini/mobile/sdk/flutter/PigeonInterface.kt b/android/src/main/kotlin/com/onegini/mobile/sdk/flutter/PigeonInterface.kt index d32d8031..9742485e 100644 --- a/android/src/main/kotlin/com/onegini/mobile/sdk/flutter/PigeonInterface.kt +++ b/android/src/main/kotlin/com/onegini/mobile/sdk/flutter/PigeonInterface.kt @@ -35,6 +35,7 @@ import com.onegini.mobile.sdk.flutter.useCases.FingerprintAuthenticationRequestD import com.onegini.mobile.sdk.flutter.useCases.FingerprintFallbackToPinUseCase import com.onegini.mobile.sdk.flutter.useCases.GetAccessTokenUseCase import com.onegini.mobile.sdk.flutter.useCases.GetAllAuthenticatorsUseCase +import com.onegini.mobile.sdk.flutter.useCases.GetAppToWebSingleSignOnUseCase import com.onegini.mobile.sdk.flutter.useCases.GetAuthenticatedUserProfileUseCase import com.onegini.mobile.sdk.flutter.useCases.GetIdentityProvidersUseCase import com.onegini.mobile.sdk.flutter.useCases.GetNotRegisteredAuthenticatorsUseCase @@ -80,6 +81,8 @@ open class PigeonInterface : UserClientApi, ResourceMethodApi { @Inject lateinit var getAllAuthenticatorsUseCase: GetAllAuthenticatorsUseCase @Inject + lateinit var getAppToWebSingleSignOnUseCase: GetAppToWebSingleSignOnUseCase + @Inject lateinit var getAuthenticatedUserProfileUseCase: GetAuthenticatedUserProfileUseCase @Inject lateinit var getIdentityProvidersUseCase: GetIdentityProvidersUseCase @@ -204,28 +207,7 @@ open class PigeonInterface : UserClientApi, ResourceMethodApi { override fun getAppToWebSingleSignOn(url: String, callback: (Result) -> Unit) { - // TODO NEEDS OWN USE CASE; https://onewelcome.atlassian.net/browse/FP-62 - if (!Patterns.WEB_URL.matcher(url).matches()) { - callback(Result.failure(SdkError(OneWelcomeWrapperErrors.MALFORMED_URL).pigeonError())) - return - } - val targetUri: Uri = Uri.parse(url) - - oneginiSDK.oneginiClient.userClient.getAppToWebSingleSignOn( - targetUri, - object : OneginiAppToWebSingleSignOnHandler { - override fun onSuccess(oneginiAppToWebSingleSignOn: OneginiAppToWebSingleSignOn) { - callback(Result.success(OWAppToWebSingleSignOn(oneginiAppToWebSingleSignOn.token, oneginiAppToWebSingleSignOn.redirectUrl.toString()))) - } - - override fun onError(oneginiSingleSignOnError: OneginiAppToWebSingleSignOnError) { - callback(Result.failure(SdkError( - code = oneginiSingleSignOnError.errorType, - message = oneginiSingleSignOnError.message - ).pigeonError())) - } - } - ) + getAppToWebSingleSignOnUseCase(url, callback) } override fun getAccessToken(callback: (Result) -> Unit) { diff --git a/android/src/main/kotlin/com/onegini/mobile/sdk/flutter/facade/UriFacade.kt b/android/src/main/kotlin/com/onegini/mobile/sdk/flutter/facade/UriFacade.kt new file mode 100644 index 00000000..f1101d65 --- /dev/null +++ b/android/src/main/kotlin/com/onegini/mobile/sdk/flutter/facade/UriFacade.kt @@ -0,0 +1,8 @@ +package com.onegini.mobile.sdk.flutter.facade + +import android.net.Uri + +interface UriFacade { + fun parse(string: String): Uri + fun withAppendedPath(baseUri: Uri, pathSegment: String): Uri +} diff --git a/android/src/main/kotlin/com/onegini/mobile/sdk/flutter/facade/UriFacadeImpl.kt b/android/src/main/kotlin/com/onegini/mobile/sdk/flutter/facade/UriFacadeImpl.kt new file mode 100644 index 00000000..a1fa14a7 --- /dev/null +++ b/android/src/main/kotlin/com/onegini/mobile/sdk/flutter/facade/UriFacadeImpl.kt @@ -0,0 +1,17 @@ + +package com.onegini.mobile.sdk.flutter.facade + +import javax.inject.Singleton +import javax.inject.Inject +import android.net.Uri + +@Singleton +class UriFacadeImpl @Inject constructor() : UriFacade { + override fun parse(string: String): Uri { + return Uri.parse(string) + } + + override fun withAppendedPath(baseUri: Uri, pathSegment: String): Uri { + return Uri.withAppendedPath(baseUri, pathSegment) + } +} diff --git a/android/src/main/kotlin/com/onegini/mobile/sdk/flutter/module/FacadeModule.kt b/android/src/main/kotlin/com/onegini/mobile/sdk/flutter/module/FacadeModule.kt new file mode 100644 index 00000000..61b4ea37 --- /dev/null +++ b/android/src/main/kotlin/com/onegini/mobile/sdk/flutter/module/FacadeModule.kt @@ -0,0 +1,13 @@ +package com.onegini.mobile.sdk.flutter.module + +import com.onegini.mobile.sdk.flutter.facade.UriFacade +import com.onegini.mobile.sdk.flutter.facade.UriFacadeImpl +import dagger.Binds +import dagger.Module + +@Module +interface FacadeModule { + + @Binds + fun bindUriFacade(uriFacade: UriFacadeImpl): UriFacade +} diff --git a/android/src/main/kotlin/com/onegini/mobile/sdk/flutter/useCases/GetAppToWebSingleSignOnUseCase.kt b/android/src/main/kotlin/com/onegini/mobile/sdk/flutter/useCases/GetAppToWebSingleSignOnUseCase.kt new file mode 100644 index 00000000..49cfbcce --- /dev/null +++ b/android/src/main/kotlin/com/onegini/mobile/sdk/flutter/useCases/GetAppToWebSingleSignOnUseCase.kt @@ -0,0 +1,44 @@ +package com.onegini.mobile.sdk.flutter.useCases + +import com.onegini.mobile.sdk.android.handlers.OneginiAppToWebSingleSignOnHandler +import com.onegini.mobile.sdk.android.handlers.error.OneginiAppToWebSingleSignOnError +import com.onegini.mobile.sdk.android.model.OneginiAppToWebSingleSignOn +import com.onegini.mobile.sdk.flutter.OneginiSDK +import com.onegini.mobile.sdk.flutter.facade.UriFacade +import com.onegini.mobile.sdk.flutter.helpers.SdkError +import com.onegini.mobile.sdk.flutter.pigeonPlugin.OWAppToWebSingleSignOn +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class GetAppToWebSingleSignOnUseCase @Inject constructor(private val oneginiSDK: OneginiSDK, private val uriFacade: UriFacade) { + operator fun invoke(url: String, callback: (Result) -> Unit) { + val targetUri = uriFacade.parse(url) + oneginiSDK.oneginiClient.userClient.getAppToWebSingleSignOn( + targetUri, + object : OneginiAppToWebSingleSignOnHandler { + override fun onSuccess(oneginiAppToWebSingleSignOn: OneginiAppToWebSingleSignOn) { + callback( + Result.success( + OWAppToWebSingleSignOn( + oneginiAppToWebSingleSignOn.token, + oneginiAppToWebSingleSignOn.redirectUrl.toString() + ) + ) + ) + } + + override fun onError(oneginiSingleSignOnError: OneginiAppToWebSingleSignOnError) { + callback( + Result.failure( + SdkError( + code = oneginiSingleSignOnError.errorType, + message = oneginiSingleSignOnError.message + ).pigeonError() + ) + ) + } + } + ) + } +} diff --git a/android/src/test/java/com/onegini/mobile/sdk/GetAppToWebSingleSignOnUseCaseTests.kt b/android/src/test/java/com/onegini/mobile/sdk/GetAppToWebSingleSignOnUseCaseTests.kt new file mode 100644 index 00000000..55674431 --- /dev/null +++ b/android/src/test/java/com/onegini/mobile/sdk/GetAppToWebSingleSignOnUseCaseTests.kt @@ -0,0 +1,113 @@ +package com.onegini.mobile.sdk + +import android.net.Uri +import com.google.gson.Gson +import com.onegini.mobile.sdk.android.handlers.OneginiAppToWebSingleSignOnHandler +import com.onegini.mobile.sdk.android.handlers.OneginiPinValidationHandler +import com.onegini.mobile.sdk.android.handlers.error.OneginiAppToWebSingleSignOnError +import com.onegini.mobile.sdk.android.model.OneginiAppToWebSingleSignOn +import com.onegini.mobile.sdk.flutter.OneWelcomeWrapperErrors.* +import com.onegini.mobile.sdk.flutter.OneginiSDK +import com.onegini.mobile.sdk.flutter.SdkErrorAssert +import com.onegini.mobile.sdk.flutter.facade.UriFacade +import com.onegini.mobile.sdk.flutter.pigeonPlugin.FlutterError +import com.onegini.mobile.sdk.flutter.pigeonPlugin.OWAppToWebSingleSignOn +import com.onegini.mobile.sdk.flutter.useCases.GetAppToWebSingleSignOnUseCase +import io.flutter.plugin.common.MethodCall +import io.flutter.plugin.common.MethodChannel +import junit.framework.TestCase.assertEquals +import org.junit.Assert +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Answers +import org.mockito.Mock +import org.mockito.junit.MockitoJUnitRunner +import org.mockito.kotlin.any +import org.mockito.kotlin.argumentCaptor +import org.mockito.kotlin.eq +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever + +@RunWith(MockitoJUnitRunner::class) +class GetAppToWebSingleSignOnUseCaseTests { + + @Mock(answer = Answers.RETURNS_DEEP_STUBS) + lateinit var oneginiSdk: OneginiSDK + + @Mock + lateinit var callbackMock: (Result) -> Unit + + + @Mock + private lateinit var uriFacade: UriFacade + + // We need to deep stub here to mock a uri object's .toString() as we cant pass a uriFacade into the OneginiAppToWebSingleSignOn + @Mock(answer = Answers.RETURNS_DEEP_STUBS) + private lateinit var oneginiAppToWebSingleSignOn: OneginiAppToWebSingleSignOn + + @Mock + private lateinit var oneginiAppToWebSingleSignOnError: OneginiAppToWebSingleSignOnError + + @Mock + private lateinit var parsedUri: Uri + + private val correctUri = "https://login-mobile.test.onegini.com/personal/dashboard" + private val mockedTokenString = "mockedToken" + private val mockedRedirectUrlString = "mockedRedirectUrl" + + lateinit var getAppToWebSingleSignOnUseCase: GetAppToWebSingleSignOnUseCase + @Before + fun setup() { + getAppToWebSingleSignOnUseCase = GetAppToWebSingleSignOnUseCase(oneginiSdk, uriFacade) + } + + @Test + fun `When oginini getAppToWebSingleSignOn calls onSuccess on the handler, Then promise should resolve with a OWAppToWebSingleSignOn with the token and url`() { + mockParseUri(correctUri) + mockSingleSignOnObject() + whenever(oneginiSdk.oneginiClient.userClient.getAppToWebSingleSignOn(any(), any())).thenAnswer { + it.getArgument(1).onSuccess(oneginiAppToWebSingleSignOn) + } + + getAppToWebSingleSignOnUseCase(correctUri, callbackMock) + + argumentCaptor>().apply { + verify(callbackMock).invoke(capture()) + Assert.assertEquals(firstValue.getOrNull()?.token, oneginiAppToWebSingleSignOn.token) + Assert.assertEquals(firstValue.getOrNull()?.redirectUrl, oneginiAppToWebSingleSignOn.redirectUrl.toString()) + } + } + + @Test + fun `When oginini getAppToWebSingleSignOn calls onError on the handler, Then result should fail with the error message and code`() { + mockParseUri(correctUri) + whenSSOReturnsError() + + getAppToWebSingleSignOnUseCase(correctUri, callbackMock) + + + argumentCaptor>().apply { + verify(callbackMock).invoke(capture()) + val expected = FlutterError(oneginiAppToWebSingleSignOnError.errorType.toString(), oneginiAppToWebSingleSignOnError.message) + SdkErrorAssert.assertEquals(expected, firstValue.exceptionOrNull()) + } + } + + private fun mockSingleSignOnObject() { + whenever(oneginiAppToWebSingleSignOn.token).thenReturn(mockedTokenString) + whenever(oneginiAppToWebSingleSignOn.redirectUrl.toString()).thenReturn(mockedRedirectUrlString) + } + + private fun whenSSOReturnsError() { + whenever(oneginiAppToWebSingleSignOnError.errorType).thenReturn(1000) + whenever(oneginiAppToWebSingleSignOnError.message).thenReturn("message") + whenever(oneginiSdk.oneginiClient.userClient.getAppToWebSingleSignOn(any(), any())).thenAnswer { + it.getArgument(1).onError(oneginiAppToWebSingleSignOnError) + } + } + + private fun mockParseUri(uri: String) { + whenever(uriFacade.parse(uri)).thenReturn(parsedUri) + } +}