mirror of
https://github.com/szkolny-eu/szkolny-android.git
synced 2025-01-31 05:48:19 +01:00
[Vulcan/Web] Update web login to work with FSLogin Realms.
This commit is contained in:
parent
459bbf78b2
commit
3ad9e5da1f
@ -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"
|
||||
|
@ -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<String, Any?>): 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) {
|
||||
|
@ -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 }
|
||||
|
@ -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()) {
|
||||
|
@ -348,8 +348,8 @@ class SzkolnyApi(val app: App) : CoroutineScope {
|
||||
}
|
||||
|
||||
@Throws(Exception::class)
|
||||
fun getPlatforms(registerName: String): List<LoginInfo.Platform> {
|
||||
val response = api.appLoginPlatforms(registerName).execute()
|
||||
fun getRealms(registerName: String): List<LoginInfo.Platform> {
|
||||
val response = api.fsLoginRealms(registerName).execute()
|
||||
|
||||
return parseResponse(response)
|
||||
}
|
||||
|
@ -33,12 +33,12 @@ interface SzkolnyService {
|
||||
@POST("feedbackMessage")
|
||||
fun feedbackMessage(@Body request: FeedbackMessageRequest): Call<ApiResponse<FeedbackMessageResponse>>
|
||||
|
||||
@GET("appLogin/platforms/{registerName}")
|
||||
fun appLoginPlatforms(@Path("registerName") registerName: String): Call<ApiResponse<List<LoginInfo.Platform>>>
|
||||
|
||||
@GET("firebase/token/{registerName}")
|
||||
fun firebaseToken(@Path("registerName") registerName: String): Call<ApiResponse<String>>
|
||||
|
||||
@GET("registerAvailability")
|
||||
fun registerAvailability(): Call<ApiResponse<Map<String, RegisterAvailabilityStatus>>>
|
||||
|
||||
@GET("fsLogin/{registerName}")
|
||||
fun fsLoginRealms(@Path("registerName") registerName: String): Call<ApiResponse<List<LoginInfo.Platform>>>
|
||||
}
|
||||
|
@ -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())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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) ->
|
||||
|
@ -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<String>,
|
||||
val apiData: JsonObject
|
||||
val realmData: RealmData
|
||||
)
|
||||
|
||||
open class BaseCredential(
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user