From 3ad9e5da1fc548f8398d2bf9f21ae8d1f302633b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Thu, 25 Feb 2021 19:29:06 +0100 Subject: [PATCH] [Vulcan/Web] Update web login to work with FSLogin Realms. --- app/build.gradle | 2 +- .../pl/szczodrzynski/edziennik/Extensions.kt | 18 +++++- .../data/api/edziennik/vulcan/DataVulcan.kt | 55 ++++--------------- .../vulcan/login/VulcanLoginWebMain.kt | 45 +++++++-------- .../edziennik/data/api/szkolny/SzkolnyApi.kt | 4 +- .../data/api/szkolny/SzkolnyService.kt | 6 +- .../edziennik/data/db/entity/LoginStore.kt | 1 + .../ui/modules/login/LoginFormFragment.kt | 7 +-- .../edziennik/ui/modules/login/LoginInfo.kt | 13 ++--- .../login/LoginPlatformListFragment.kt | 8 +-- 10 files changed, 65 insertions(+), 94 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 7ee81956..b6bab7f2 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -211,7 +211,7 @@ dependencies { implementation 'com.qifan.powerpermission:powerpermission:1.3.0' implementation 'com.qifan.powerpermission:powerpermission-coroutines:1.3.0' - implementation 'com.github.kuba2k2.FSLogin:lib:master-SNAPSHOT' + implementation 'com.github.kuba2k2.FSLogin:lib:2.0.0' implementation 'pl.droidsonroids:jspoon:1.3.2' implementation "com.squareup.retrofit2:converter-scalars:2.8.1" implementation "pl.droidsonroids.retrofit2:converter-jspoon:1.3.2" diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt b/app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt index a34f6af5..71febada 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt @@ -43,10 +43,9 @@ import androidx.viewpager.widget.ViewPager import com.google.android.gms.security.ProviderInstaller import com.google.android.material.button.MaterialButton import com.google.android.material.dialog.MaterialAlertDialogBuilder +import com.google.gson.* import com.google.gson.JsonArray -import com.google.gson.JsonElement import com.google.gson.JsonObject -import com.google.gson.JsonParser import im.wangchao.mhttp.Response import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.delay @@ -703,6 +702,21 @@ fun JsonObject(vararg properties: Pair): JsonObject { } } +fun JsonObject.toBundle(): Bundle { + return Bundle().also { + for ((key, value) in this.entrySet()) { + when (value) { + is JsonObject -> it.putBundle(key, value.toBundle()) + is JsonPrimitive -> when { + value.isString -> it.putString(key, value.asString) + value.isBoolean -> it.putBoolean(key, value.asBoolean) + value.isNumber -> it.putInt(key, value.asInt) + } + } + } + } +} + fun JsonArray(vararg properties: Any?): JsonArray { return JsonArray().apply { for (property in properties) { diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/DataVulcan.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/DataVulcan.kt index 649991d6..665943c6 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/DataVulcan.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/DataVulcan.kt @@ -4,6 +4,7 @@ package pl.szczodrzynski.edziennik.data.api.edziennik.vulcan +import com.google.gson.JsonObject import pl.szczodrzynski.edziennik.* import pl.szczodrzynski.edziennik.data.api.LOGIN_METHOD_VULCAN_API import pl.szczodrzynski.edziennik.data.api.LOGIN_METHOD_VULCAN_HEBE @@ -14,13 +15,13 @@ import pl.szczodrzynski.edziennik.data.db.entity.LoginStore import pl.szczodrzynski.edziennik.data.db.entity.Profile import pl.szczodrzynski.edziennik.data.db.entity.Team import pl.szczodrzynski.edziennik.utils.Utils +import pl.szczodrzynski.fslogin.realm.RealmData class DataVulcan(app: App, profile: Profile?, loginStore: LoginStore) : Data(app, profile, loginStore) { fun isWebMainLoginValid() = symbol.isNotNullNorEmpty() && (webExpiryTime[symbol]?.toLongOrNull() ?: 0) - 30 > currentTimeUnix() && webAuthCookie[symbol].isNotNullNorEmpty() - && webHost.isNotNullNorEmpty() - && webType.isNotNullNorEmpty() + && webRealmData != null fun isApiLoginValid() = currentSemesterEndDate-30 > currentTimeUnix() && apiFingerprint[symbol].isNotNullNorEmpty() && apiPrivateKey[symbol].isNotNullNorEmpty() @@ -296,49 +297,15 @@ class DataVulcan(app: App, profile: Profile?, loginStore: LoginStore) : Data(app \/ \/ \___|_.__/ |_| |_____/ |______\___/ \__, |_|_| |_| __/ | |__*/ - /** - * Federation Services login type. - * This might be one of: cufs, adfs, adfslight. - */ - var webType: String? - get() { mWebType = mWebType ?: loginStore.getLoginData("webType", null); return mWebType } - set(value) { loginStore.putLoginData("webType", value); mWebType = value } - private var mWebType: String? = null + var webRealmData: RealmData? + get() { mWebRealmData = mWebRealmData ?: loginStore.getLoginData("webRealmData", JsonObject()).let { + app.gson.fromJson(it, RealmData::class.java) + }; return mWebRealmData } + set(value) { loginStore.putLoginData("webRealmData", app.gson.toJsonTree(value) as JsonObject); mWebRealmData = value } + private var mWebRealmData: RealmData? = null - /** - * Web server providing the federation services login. - * If this is present, WEB_MAIN login is considered as available. - */ - var webHost: String? - get() { mWebHost = mWebHost ?: loginStore.getLoginData("webHost", null); return mWebHost } - set(value) { loginStore.putLoginData("webHost", value); mWebHost = value } - private var mWebHost: String? = null - - /** - * An ID used in ADFS & ADFSLight login types. - */ - var webAdfsId: String? - get() { mWebAdfsId = mWebAdfsId ?: loginStore.getLoginData("webAdfsId", null); return mWebAdfsId } - set(value) { loginStore.putLoginData("webAdfsId", value); mWebAdfsId = value } - private var mWebAdfsId: String? = null - - /** - * A domain override for ADFS Light. - */ - var webAdfsDomain: String? - get() { mWebAdfsDomain = mWebAdfsDomain ?: loginStore.getLoginData("webAdfsDomain", null); return mWebAdfsDomain } - set(value) { loginStore.putLoginData("webAdfsDomain", value); mWebAdfsDomain = value } - private var mWebAdfsDomain: String? = null - - var webIsHttpCufs: Boolean - get() { mWebIsHttpCufs = mWebIsHttpCufs ?: loginStore.getLoginData("webIsHttpCufs", false); return mWebIsHttpCufs ?: false } - set(value) { loginStore.putLoginData("webIsHttpCufs", value); mWebIsHttpCufs = value } - private var mWebIsHttpCufs: Boolean? = null - - var webIsScopedAdfs: Boolean - get() { mWebIsScopedAdfs = mWebIsScopedAdfs ?: loginStore.getLoginData("webIsScopedAdfs", false); return mWebIsScopedAdfs ?: false } - set(value) { loginStore.putLoginData("webIsScopedAdfs", value); mWebIsScopedAdfs = value } - private var mWebIsScopedAdfs: Boolean? = null + val webHost + get() = webRealmData?.host var webEmail: String? get() { mWebEmail = mWebEmail ?: loginStore.getLoginData("webEmail", null); return mWebEmail } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/login/VulcanLoginWebMain.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/login/VulcanLoginWebMain.kt index 896f64e8..2b62eb14 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/login/VulcanLoginWebMain.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/login/VulcanLoginWebMain.kt @@ -13,7 +13,7 @@ import pl.szczodrzynski.edziennik.getString import pl.szczodrzynski.edziennik.isNotNullNorEmpty import pl.szczodrzynski.edziennik.utils.models.Date import pl.szczodrzynski.fslogin.FSLogin -import pl.szczodrzynski.fslogin.realm.CufsRealm +import pl.szczodrzynski.fslogin.realm.toRealm class VulcanLoginWebMain(val data: DataVulcan, val onSuccess: () -> Unit) { companion object { @@ -30,8 +30,7 @@ class VulcanLoginWebMain(val data: DataVulcan, val onSuccess: () -> Unit) { } else { if (data.symbol.isNotNullNorEmpty() - && data.webType.isNotNullNorEmpty() - && data.webHost.isNotNullNorEmpty() + && data.webRealmData != null && (data.webEmail.isNotNullNorEmpty() || data.webUsername.isNotNullNorEmpty()) && data.webPassword.isNotNullNorEmpty()) { try { @@ -56,32 +55,28 @@ class VulcanLoginWebMain(val data: DataVulcan, val onSuccess: () -> Unit) { data.symbol = getString("symbol") remove("symbol") } + // 4.6 - form inputs renamed + if (has("email")) { + data.webEmail = getString("email") + remove("email") + } + if (has("username")) { + data.webUsername = getString("username") + remove("username") + } + if (has("password")) { + data.webPassword = getString("password") + remove("password") + } + } + + if (data.symbol == null && data.webRealmData != null) { + data.symbol = data.webRealmData?.symbol } } private fun loginWithCredentials(): Boolean { - val realm = when (data.webType) { - "cufs" -> CufsRealm( - host = data.webHost ?: return false, - symbol = data.symbol ?: "default", - httpCufs = data.webIsHttpCufs - ) - "adfs" -> CufsRealm( - host = data.webHost ?: return false, - symbol = data.symbol ?: "default", - httpCufs = data.webIsHttpCufs - ).toAdfsRealm(id = data.webAdfsId ?: return false) - "adfslight" -> CufsRealm( - host = data.webHost ?: return false, - symbol = data.symbol ?: "default", - httpCufs = data.webIsHttpCufs - ).toAdfsLightRealm( - id = data.webAdfsId ?: return false, - domain = data.webAdfsDomain ?: "adfslight", - isScoped = data.webIsScopedAdfs - ) - else -> return false - } + val realm = data.webRealmData?.toRealm() ?: return false val certificate = web.readCertificate()?.let { web.parseCertificate(it) } if (certificate != null && Date.fromIso(certificate.expiryDate) > System.currentTimeMillis()) { diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/szkolny/SzkolnyApi.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/szkolny/SzkolnyApi.kt index 042e63fe..0a8e90a0 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/szkolny/SzkolnyApi.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/szkolny/SzkolnyApi.kt @@ -348,8 +348,8 @@ class SzkolnyApi(val app: App) : CoroutineScope { } @Throws(Exception::class) - fun getPlatforms(registerName: String): List { - val response = api.appLoginPlatforms(registerName).execute() + fun getRealms(registerName: String): List { + val response = api.fsLoginRealms(registerName).execute() return parseResponse(response) } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/szkolny/SzkolnyService.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/szkolny/SzkolnyService.kt index 6046e4da..3fe78910 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/szkolny/SzkolnyService.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/szkolny/SzkolnyService.kt @@ -33,12 +33,12 @@ interface SzkolnyService { @POST("feedbackMessage") fun feedbackMessage(@Body request: FeedbackMessageRequest): Call> - @GET("appLogin/platforms/{registerName}") - fun appLoginPlatforms(@Path("registerName") registerName: String): Call>> - @GET("firebase/token/{registerName}") fun firebaseToken(@Path("registerName") registerName: String): Call> @GET("registerAvailability") fun registerAvailability(): Call>> + + @GET("fsLogin/{registerName}") + fun fsLoginRealms(@Path("registerName") registerName: String): Call>> } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/LoginStore.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/LoginStore.kt index 1ec410b5..192bd1f6 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/LoginStore.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/LoginStore.kt @@ -59,6 +59,7 @@ class LoginStore( is Long -> putLoginData(key, o) is Float -> putLoginData(key, o) is Boolean -> putLoginData(key, o) + is Bundle -> putLoginData(key, o.toJsonObject()) } } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginFormFragment.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginFormFragment.kt index e223d5fc..8776f355 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginFormFragment.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginFormFragment.kt @@ -14,7 +14,6 @@ import androidx.core.view.isVisible import androidx.core.widget.addTextChangedListener import androidx.fragment.app.Fragment import com.google.android.material.textfield.TextInputLayout -import com.google.gson.JsonParser import com.mikepenz.iconics.IconicsDrawable import com.mikepenz.iconics.utils.paddingDp import com.mikepenz.iconics.utils.sizeDp @@ -72,7 +71,7 @@ class LoginFormFragment : Fragment(), CoroutineScope { val platformGuideText = arguments?.getString("platformGuideText") val platformDescription = arguments?.getString("platformDescription") val platformFormFields = arguments?.getString("platformFormFields")?.split(";") - val platformApiData = arguments?.getString("platformApiData")?.let { JsonParser().parse(it)?.asJsonObject } + val platformRealmData = arguments?.getString("platformRealmData")?.toJsonObject() b.title.setText(R.string.login_form_title_format, app.getString(register.registerName)) b.subTitle.text = platformName ?: app.getString(mode.name) @@ -159,9 +158,7 @@ class LoginFormFragment : Fragment(), CoroutineScope { payload.putBoolean("fakeLogin", true) } - platformApiData?.entrySet()?.forEach { - payload.putString(it.key, it.value.asString) - } + payload.putBundle("webRealmData", platformRealmData?.toBundle()) var hasErrors = false credentials.forEach { (credential, b) -> diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginInfo.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginInfo.kt index 13fa6123..94399c0e 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginInfo.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginInfo.kt @@ -6,12 +6,12 @@ package pl.szczodrzynski.edziennik.ui.modules.login import androidx.annotation.DrawableRes import androidx.annotation.StringRes -import com.google.gson.JsonObject import com.mikepenz.iconics.typeface.IIcon import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.data.api.* import pl.szczodrzynski.edziennik.ui.modules.grades.models.ExpandableItemModel +import pl.szczodrzynski.fslogin.realm.RealmData object LoginInfo { @@ -191,9 +191,9 @@ object LoginInfo { isDevOnly = true, isPlatformSelection = true, credentials = listOf( - getEmailCredential("webEmail"), + getEmailCredential("email"), FormField( - keyName = "webUsername", + keyName = "username", name = R.string.login_hint_username, icon = CommunityMaterial.Icon.cmd_account_outline, emptyText = R.string.login_error_no_username, @@ -203,7 +203,7 @@ object LoginInfo { validationRegex = "[A-Z]{7}[0-9]+", caseMode = FormField.CaseMode.UPPER_CASE ), - getPasswordCredential("webPassword") + getPasswordCredential("password") ), errorCodes = mapOf() ) @@ -407,15 +407,12 @@ object LoginInfo { data class Platform( val id: Int, - val loginType: Int, - val loginMode: Int, val name: String, val description: String?, - val guideText: String?, val icon: String, val screenshot: String?, val formFields: List, - val apiData: JsonObject + val realmData: RealmData ) open class BaseCredential( diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginPlatformListFragment.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginPlatformListFragment.kt index ad5fb3d9..988d1bde 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginPlatformListFragment.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginPlatformListFragment.kt @@ -60,12 +60,12 @@ class LoginPlatformListFragment : Fragment(), CoroutineScope { adapter = LoginPlatformAdapter(activity) { platform -> nav.navigate(R.id.loginFormFragment, Bundle( - "loginType" to platform.loginType, - "loginMode" to platform.loginMode, + "loginType" to loginType, + "loginMode" to loginMode, "platformName" to platform.name, "platformDescription" to platform.description, "platformFormFields" to platform.formFields.joinToString(";"), - "platformApiData" to platform.apiData.toString() + "platformRealmData" to app.gson.toJson(platform.realmData) ), activity.navOptions) } @@ -96,7 +96,7 @@ class LoginPlatformListFragment : Fragment(), CoroutineScope { val platforms = LoginInfo.platformList[mode.name] ?: run { api.runCatching(activity) { - getPlatforms(register.internalName) + getRealms(register.internalName) } ?: run { nav.navigateUp() return@launch