Change vTokenSchemeMapping to vHeaders with configurable header name

This commit is contained in:
Mikołaj Pich 2024-05-22 19:35:18 +02:00
parent bc52de44f9
commit b8ff96f5fd
No known key found for this signature in database
8 changed files with 89 additions and 41 deletions

View file

@ -18,7 +18,7 @@ ext {
moshi = "1.13.0" moshi = "1.13.0"
} }
version = "2.6.9" version = "2.6.10-SNAPSHOT"
group = "io.github.wulkanowy" group = "io.github.wulkanowy"
nexusPublishing { nexusPublishing {

View file

@ -630,6 +630,27 @@ internal val ApiEndpointsVTokenMap = mapOf(
"Usuniete" to "6fb7f517-7287-41c3-8d30-64e654f647a7", "Usuniete" to "6fb7f517-7287-41c3-8d30-64e654f647a7",
"UsunieteSkrzynka" to "937cd3ba-6fa9-4e44-bb99-e4fd282803c2", "UsunieteSkrzynka" to "937cd3ba-6fa9-4e44-bb99-e4fd282803c2",
), ),
"uonetplus-uczenplus" to mapOf(
"Context" to "cbdc1e7c-612f-4b7d-911d-c76701a0bd41",
"Frekwencja" to "04921511-8b5f-44fc-8d2c-d9c5157393d1",
"Usprawiedliwienia" to "0e579c35-e3e0-466c-ae2c-82b2b972097f",
"FrekwencjaStatystyki" to "39dccbf3-8a04-41b1-a48d-8c056dff702c",
"ZarejestrowaneUrzadzenia" to "06b04925-c003-4a53-8f9e-b720e368862a",
"Zebrania" to "968f86f2-92a0-4f6e-b22c-4791e3362554",
"RealizacjaZajec" to "3e1c3fb2-1b8a-4579-b7d8-ea2cfa263d42",
"SprawdzianyZadaniaDomowe" to "bf7d0726-7979-4aee-89e2-9b74bbe0820d",
"SprawdzianSzczegoly" to "3dcff009-c0ad-4b7f-95eb-18be7e3b9019",
"ZadanieDomoweSzczegoly" to "2d8b6288-1f13-4ca7-bb6f-334de9a663d0",
"PlanZajec" to "acd3f4c1-f366-41a4-b2a1-c3543d173ec7",
"DniWolne" to "f28b5242-c33a-4ef3-8c3d-e8fb4d39f801",
"Uwagi" to "1a0d8d8b-de01-4fe7-ae28-8fed2d68e9b5",
"Nauczyciele" to "ce89218b-4609-4e10-92aa-217521e5d99a",
"Informacje" to "984449a8-907b-4488-9f90-8f0c6e56c03c",
"DaneUcznia" to "64348eca-82d1-442a-acc3-4886ca5daa33",
"UczenZdjecie" to "4ed033b1-f8f4-4ca1-b277-60be0f80ab0f",
"OkresyKlasyfikacyjne" to "6ff9273a-fb69-417d-aca9-d60ecceaad26",
"Oceny" to "86f8dd7b-e144-42d9-a389-0648503a479d",
),
), ),
"24.04.0008.58830" to mapOf( "24.04.0008.58830" to mapOf(
"uonetplus-wiadomosciplus" to mapOf( "uonetplus-wiadomosciplus" to mapOf(
@ -644,24 +665,38 @@ internal val ApiEndpointsVTokenMap = mapOf(
), ),
) )
internal val ApiEndpointsVTokenSchemeMap = mapOf( internal val ApiEndpointsVHeaders = mapOf(
"24.04.0003.58698" to mapOf( "24.04.0003.58698" to mapOf(
"uonetplus-wiadomosciplus" to "{UUID}-{name}-{appCustomerDb}-{appVersion}", "uonetplus-wiadomosciplus" to mapOf(
"V-Token" to "{UUID}-{name}-{appCustomerDb}-{appVersion}",
),
), ),
"24.04.0004.58722" to mapOf( "24.04.0004.58722" to mapOf(
"uonetplus-wiadomosciplus" to "{UUID}-{appCustomerDb}-{appVersion}", "uonetplus-wiadomosciplus" to mapOf(
"V-Token" to "{UUID}-{appCustomerDb}-{appVersion}",
),
), ),
"24.04.0005.58736" to mapOf( "24.04.0005.58736" to mapOf(
"uonetplus-wiadomosciplus" to "{UUID}-{appCustomerDb}-{appVersion}-{apiKey}", "uonetplus-wiadomosciplus" to mapOf(
"V-Token" to "{UUID}-{appCustomerDb}-{appVersion}-{apiKey}",
),
), ),
"24.04.0006.58753" to mapOf( "24.04.0006.58753" to mapOf(
"uonetplus-wiadomosciplus" to "{UUID}-{appCustomerDb}-{appVersion}-{apiKey}", "uonetplus-wiadomosciplus" to mapOf(
"V-Token" to "{UUID}-{appCustomerDb}-{appVersion}-{apiKey}",
),
), ),
"24.04.0007.58773" to mapOf( "24.04.0007.58773" to mapOf(
"uonetplus-wiadomosciplus" to "{UUID}-{appCustomerDb}-{appVersion}-{apiKey}", "uonetplus-wiadomosciplus" to mapOf(
"uonetplus-uczenplus" to "{UUID}-{appCustomerDb}-{appVersion}-{apiKey}", "V-Token" to "{UUID}-{appCustomerDb}-{appVersion}-{apiKey}",
),
"uonetplus-uczenplus" to mapOf(
"V-Token" to "{UUID}-{appCustomerDb}-{appVersion}-{apiKey}",
),
), ),
"24.04.0008.58830" to mapOf( "24.04.0008.58830" to mapOf(
"uonetplus-wiadomosciplus" to "{UUID}-{appCustomerDb}-{appCustomerDbSig}-{appVersion}-{apiKey}", "uonetplus-wiadomosciplus" to mapOf(
"V-Apitoken" to "{UUID}-{appCustomerDb}-{appCustomerDbSig}-{appVersion}-{apiKey}",
),
), ),
) )

View file

@ -224,10 +224,10 @@ class Scrapper {
vTokenMap = value vTokenMap = value
} }
var vTokenSchemeMapping: Map<String, Map<String, String>> var vHeaders: Map<String, Map<String, Map<String, String>>>
get() = vTokenSchemeMap get() = vHeadersMap
set(value) { set(value) {
vTokenSchemeMap = value vHeadersMap = value
} }
var vParamsEvaluation: suspend () -> EvaluateHandler var vParamsEvaluation: suspend () -> EvaluateHandler
@ -239,7 +239,7 @@ class Scrapper {
internal companion object { internal companion object {
var endpointsMap: Map<String, Map<String, Map<String, String>>> = ApiEndpointsMap var endpointsMap: Map<String, Map<String, Map<String, String>>> = ApiEndpointsMap
var vTokenMap: Map<String, Map<String, Map<String, String>>> = ApiEndpointsVTokenMap var vTokenMap: Map<String, Map<String, Map<String, String>>> = ApiEndpointsVTokenMap
var vTokenSchemeMap: Map<String, Map<String, String>> = ApiEndpointsVTokenSchemeMap var vHeadersMap: Map<String, Map<String, Map<String, String>>> = ApiEndpointsVHeaders
var vParamsRun: suspend () -> EvaluateHandler = { object : EvaluateHandler {} } var vParamsRun: suspend () -> EvaluateHandler = { object : EvaluateHandler {} }
} }

View file

@ -15,7 +15,6 @@ import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive import kotlinx.serialization.json.jsonPrimitive
import okhttp3.HttpUrl import okhttp3.HttpUrl
import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.Request
import org.jsoup.Jsoup import org.jsoup.Jsoup
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
@ -307,13 +306,22 @@ internal suspend fun getModuleHeadersFromDocument(document: Document): ModuleHea
) )
} }
internal fun Request.Builder.attachVToken(moduleHost: String, url: HttpUrl, headers: ModuleHeaders?): Request.Builder { internal fun getVHeaders(moduleHost: String, url: HttpUrl, headers: ModuleHeaders?): Map<String, String> {
val vToken = url.getMatchedVToken(moduleHost, headers) ?: return this val vHeaders = Scrapper.vHeadersMap[headers?.appVersion] ?: ApiEndpointsVHeaders[headers?.appVersion]
addHeader("V-Apitoken", vToken)
return this return vHeaders?.get(moduleHost).orEmpty().mapNotNull { (key, scheme) ->
val headerValue = url.getMatchedVHeader(
moduleHost = moduleHost,
domainSchema = scheme,
headers = headers,
)
if (headerValue != null) {
key to headerValue
} else null
}.toMap()
} }
internal fun HttpUrl.getMatchedVToken(moduleHost: String, headers: ModuleHeaders?): String? { private fun HttpUrl.getMatchedVHeader(moduleHost: String, domainSchema: String?, headers: ModuleHeaders?): String? {
val pathSegmentIndex = getPathIndexByModuleHost(moduleHost) val pathSegmentIndex = getPathIndexByModuleHost(moduleHost)
val pathKey = pathSegments.getOrNull(pathSegmentIndex) val pathKey = pathSegments.getOrNull(pathSegmentIndex)
val mappedUuid = (Scrapper.vTokenMap[headers?.appVersion] ?: ApiEndpointsVTokenMap[headers?.appVersion]) val mappedUuid = (Scrapper.vTokenMap[headers?.appVersion] ?: ApiEndpointsVTokenMap[headers?.appVersion])
@ -321,17 +329,19 @@ internal fun HttpUrl.getMatchedVToken(moduleHost: String, headers: ModuleHeaders
?.get(pathKey) ?.get(pathKey)
?: return null ?: return null
return getVToken(mappedUuid, headers, moduleHost) return getVToken(
uuid = mappedUuid,
headers = headers,
domainSchema = domainSchema,
)
} }
private val vTokenSchemeKeysRegex = "\\{([^{}]+)\\}".toRegex() private val vTokenSchemeKeysRegex = "\\{([^{}]+)\\}".toRegex()
private fun getVToken(uuid: String, headers: ModuleHeaders?, moduleHost: String): String? { private fun getVToken(uuid: String, headers: ModuleHeaders?, domainSchema: String?): String? {
if (uuid.isBlank()) return null if (uuid.isBlank()) return null
val schemeToSubstitute = (Scrapper.vTokenSchemeMap[headers?.appVersion] ?: ApiEndpointsVTokenSchemeMap[headers?.appVersion]) val schemeToSubstitute = domainSchema ?: "{UUID}-{appCustomerDb}-{appCustomerDbSig}-{appVersion}-{apiKey}"
?.get(moduleHost)
?: "{UUID}-{appCustomerDb}-{appCustomerDbSig}-{appVersion}-{apiKey}"
val vTokenEncoded = runCatching { val vTokenEncoded = runCatching {
vTokenSchemeKeysRegex.replace(schemeToSubstitute) { vTokenSchemeKeysRegex.replace(schemeToSubstitute) {

View file

@ -9,10 +9,10 @@ import io.github.wulkanowy.sdk.scrapper.Scrapper.LoginType.ADFSLight
import io.github.wulkanowy.sdk.scrapper.Scrapper.LoginType.ADFSLightCufs import io.github.wulkanowy.sdk.scrapper.Scrapper.LoginType.ADFSLightCufs
import io.github.wulkanowy.sdk.scrapper.Scrapper.LoginType.ADFSLightScoped import io.github.wulkanowy.sdk.scrapper.Scrapper.LoginType.ADFSLightScoped
import io.github.wulkanowy.sdk.scrapper.Scrapper.LoginType.STANDARD import io.github.wulkanowy.sdk.scrapper.Scrapper.LoginType.STANDARD
import io.github.wulkanowy.sdk.scrapper.attachVToken
import io.github.wulkanowy.sdk.scrapper.exception.VulcanClientError import io.github.wulkanowy.sdk.scrapper.exception.VulcanClientError
import io.github.wulkanowy.sdk.scrapper.exception.VulcanServerError import io.github.wulkanowy.sdk.scrapper.exception.VulcanServerError
import io.github.wulkanowy.sdk.scrapper.getModuleHeadersFromDocument import io.github.wulkanowy.sdk.scrapper.getModuleHeadersFromDocument
import io.github.wulkanowy.sdk.scrapper.getVHeaders
import io.github.wulkanowy.sdk.scrapper.isAnyMappingAvailable import io.github.wulkanowy.sdk.scrapper.isAnyMappingAvailable
import io.github.wulkanowy.sdk.scrapper.login.LoginModuleResult import io.github.wulkanowy.sdk.scrapper.login.LoginModuleResult
import io.github.wulkanowy.sdk.scrapper.login.LoginResult import io.github.wulkanowy.sdk.scrapper.login.LoginResult
@ -176,7 +176,14 @@ internal class AutoLoginInterceptor(
addHeader("X-V-RequestVerificationToken", it.token) addHeader("X-V-RequestVerificationToken", it.token)
addHeader("X-V-AppGuid", it.appGuid) addHeader("X-V-AppGuid", it.appGuid)
addHeader("X-V-AppVersion", it.appVersion) addHeader("X-V-AppVersion", it.appVersion)
attachVToken(moduleHost, url, headers)
getVHeaders(
moduleHost = moduleHost,
url = url,
headers = headers,
).forEach { (key, headerValue) ->
addHeader(key, headerValue)
}
} }
} }
.url(mappedUrl) .url(mappedUrl)

View file

@ -3,10 +3,10 @@ package io.github.wulkanowy.sdk.scrapper.repository
import io.github.wulkanowy.sdk.scrapper.Scrapper import io.github.wulkanowy.sdk.scrapper.Scrapper
import io.github.wulkanowy.sdk.scrapper.exception.ScrapperException import io.github.wulkanowy.sdk.scrapper.exception.ScrapperException
import io.github.wulkanowy.sdk.scrapper.exception.StudentGraduateException import io.github.wulkanowy.sdk.scrapper.exception.StudentGraduateException
import io.github.wulkanowy.sdk.scrapper.getMatchedVToken
import io.github.wulkanowy.sdk.scrapper.getModuleHeadersFromDocument import io.github.wulkanowy.sdk.scrapper.getModuleHeadersFromDocument
import io.github.wulkanowy.sdk.scrapper.getNormalizedSymbol import io.github.wulkanowy.sdk.scrapper.getNormalizedSymbol
import io.github.wulkanowy.sdk.scrapper.getScriptParam import io.github.wulkanowy.sdk.scrapper.getScriptParam
import io.github.wulkanowy.sdk.scrapper.getVHeaders
import io.github.wulkanowy.sdk.scrapper.interceptor.StudentModuleHost import io.github.wulkanowy.sdk.scrapper.interceptor.StudentModuleHost
import io.github.wulkanowy.sdk.scrapper.interceptor.StudentPlusModuleHost import io.github.wulkanowy.sdk.scrapper.interceptor.StudentPlusModuleHost
import io.github.wulkanowy.sdk.scrapper.interceptor.handleErrors import io.github.wulkanowy.sdk.scrapper.interceptor.handleErrors
@ -41,7 +41,6 @@ import org.jsoup.nodes.Element
import org.jsoup.parser.Parser import org.jsoup.parser.Parser
import org.jsoup.select.Elements import org.jsoup.select.Elements
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import pl.droidsonroids.jspoon.Jspoon
import kotlin.io.encoding.ExperimentalEncodingApi import kotlin.io.encoding.ExperimentalEncodingApi
@Suppress("UnnecessaryOptInAnnotation") @Suppress("UnnecessaryOptInAnnotation")
@ -63,10 +62,6 @@ internal class RegisterRepository(
private val logger = LoggerFactory.getLogger(this::class.java) private val logger = LoggerFactory.getLogger(this::class.java)
} }
private val certificateAdapter by lazy {
Jspoon.create().adapter(CertificateResponse::class.java)
}
suspend fun getUserSubjects(): RegisterUser { suspend fun getUserSubjects(): RegisterUser {
val symbolLoginType = getLoginType(startSymbol.getNormalizedSymbol()) val symbolLoginType = getLoginType(startSymbol.getNormalizedSymbol())
val certificateResponse = getCert(symbolLoginType) val certificateResponse = getCert(symbolLoginType)
@ -350,21 +345,21 @@ internal class RegisterRepository(
val moduleHeaders = getModuleHeadersFromDocument(homepage) val moduleHeaders = getModuleHeadersFromDocument(homepage)
val baseStudentPlus = url.generate(UrlGenerator.Site.STUDENT_PLUS) val baseStudentPlus = url.generate(UrlGenerator.Site.STUDENT_PLUS)
val contextUrl = (baseStudentPlus + "api/Context").toHttpUrl() val contextUrl = (baseStudentPlus + "api/Context").toHttpUrl()
val contextVToken = contextUrl.getMatchedVToken(StudentPlusModuleHost, moduleHeaders) val contextVHeaders = getVHeaders(StudentPlusModuleHost, contextUrl, moduleHeaders)
val mappedContextUrl = contextUrl.mapModuleUrl(StudentPlusModuleHost, moduleHeaders.appVersion) val mappedContextUrl = contextUrl.mapModuleUrl(StudentPlusModuleHost, moduleHeaders.appVersion)
val semestersUrl = (baseStudentPlus + "api/OkresyKlasyfikacyjne").toHttpUrl() val semestersUrl = (baseStudentPlus + "api/OkresyKlasyfikacyjne").toHttpUrl()
val semestersVToken = semestersUrl.getMatchedVToken(StudentPlusModuleHost, moduleHeaders) val semesterVHeaders = getVHeaders(StudentPlusModuleHost, semestersUrl, moduleHeaders)
val mappedSemestersUrl = semestersUrl.mapModuleUrl(StudentPlusModuleHost, moduleHeaders.appVersion) val mappedSemestersUrl = semestersUrl.mapModuleUrl(StudentPlusModuleHost, moduleHeaders.appVersion)
return studentPlus return studentPlus
.getContextByUrl(vToken = contextVToken, url = mappedContextUrl.toString()).students .getContextByUrl(vHeaders = contextVHeaders, url = mappedContextUrl.toString()).students
.map { contextStudent -> .map { contextStudent ->
val semesters = runCatching { val semesters = runCatching {
when { when {
contextStudent.isAuthorizationRequired -> emptyList() contextStudent.isAuthorizationRequired -> emptyList()
else -> studentPlus.getSemestersByUrl( else -> studentPlus.getSemestersByUrl(
vToken = semestersVToken, vHeaders = semesterVHeaders,
url = mappedSemestersUrl.toString(), url = mappedSemestersUrl.toString(),
key = contextStudent.key, key = contextStudent.key,
diaryId = contextStudent.registerId, diaryId = contextStudent.registerId,

View file

@ -26,6 +26,7 @@ import retrofit2.Response
import retrofit2.http.Body import retrofit2.http.Body
import retrofit2.http.GET import retrofit2.http.GET
import retrofit2.http.Header import retrofit2.http.Header
import retrofit2.http.HeaderMap
import retrofit2.http.POST import retrofit2.http.POST
import retrofit2.http.Query import retrofit2.http.Query
import retrofit2.http.Url import retrofit2.http.Url
@ -36,13 +37,13 @@ internal interface StudentPlusService {
@GET @GET
suspend fun getContextByUrl( suspend fun getContextByUrl(
@Header("V-Apitoken") vToken: String?, @HeaderMap vHeaders: Map<String, String>,
@Url url: String, @Url url: String,
): ContextResponse ): ContextResponse
@GET @GET
suspend fun getSemestersByUrl( suspend fun getSemestersByUrl(
@Header("V-Apitoken") vToken: String?, @HeaderMap vHeaders: Map<String, String>,
@Url url: String, @Url url: String,
@Query("key") key: String, @Query("key") key: String,
@Query("idDziennik") diaryId: Int, @Query("idDziennik") diaryId: Int,

View file

@ -248,10 +248,10 @@ class Sdk {
scrapper.vTokenMapping = value scrapper.vTokenMapping = value
} }
var vTokenSchemeMapping var vHeaders
get() = scrapper.vTokenSchemeMapping get() = scrapper.vHeaders
set(value) { set(value) {
scrapper.vTokenSchemeMapping = value scrapper.vHeaders = value
} }
var vParamsEvaluation: suspend () -> EvaluateHandler var vParamsEvaluation: suspend () -> EvaluateHandler