From 287093148183f5b39f51ad8504bbb22b89cd172a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Wed, 25 Sep 2019 21:28:04 +0200 Subject: [PATCH] [APIv2/Librus] Update Librus Messages login method. --- .../szczodrzynski/edziennik/api/v2/Errors.kt | 3 ++ .../edziennik/api/v2/LoginMethods.kt | 6 +-- .../edziennik/api/v2/librus/LibrusTest.kt | 14 +++-- .../api/v2/librus/data/DataLibrus.kt | 22 +++++++- .../api/v2/librus/login/LoginLibrus.kt | 3 +- .../v2/librus/login/LoginLibrusMessages.kt | 53 +++++++++++++++++-- .../v2/librus/login/LoginLibrusSynergia.kt | 20 +++---- .../internal/cookie/PersistentCookieJar.java | 41 ++++++++------ 8 files changed, 124 insertions(+), 38 deletions(-) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Errors.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Errors.kt index 4c89af8a..e28d1afe 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Errors.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Errors.kt @@ -80,6 +80,9 @@ const val ERROR_LIBRUS_API_NOTES_NOT_ACTIVE = 151 const val ERROR_LOGIN_LIBRUS_SYNERGIA_NO_TOKEN = 152 const val ERROR_LOGIN_LIBRUS_SYNERGIA_TOKEN_INVALID = 153 const val ERROR_LOGIN_LIBRUS_SYNERGIA_NO_SESSION_ID = 154 +const val ERROR_LIBRUS_MESSAGES_ACCESS_DENIED = 155 +const val ERROR_LIBRUS_SYNERGIA_ACCESS_DENIED = 156 +const val ERROR_LOGIN_LIBRUS_MESSAGES_NO_SESSION_ID = 157 const val EXCEPTION_LOGIN_LIBRUS_API_TOKEN = 901 const val EXCEPTION_LOGIN_LIBRUS_PORTAL_TOKEN = 902 diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/LoginMethods.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/LoginMethods.kt index 26a39c7c..3e5c1fe6 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/LoginMethods.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/LoginMethods.kt @@ -26,9 +26,9 @@ const val LOGIN_MODE_VULCAN_WEB = 0 // LOGIN METHODS const val LOGIN_METHOD_NOT_NEEDED = -1 -const val LOGIN_METHOD_LIBRUS_PORTAL = 100// 0 -const val LOGIN_METHOD_LIBRUS_API = 200// 1 -const val LOGIN_METHOD_LIBRUS_SYNERGIA = 300 // 2 +const val LOGIN_METHOD_LIBRUS_PORTAL = 100 +const val LOGIN_METHOD_LIBRUS_API = 200 +const val LOGIN_METHOD_LIBRUS_SYNERGIA = 300 const val LOGIN_METHOD_LIBRUS_MESSAGES = 400 const val LOGIN_METHOD_MOBIDZIENNIK_API = 100 const val LOGIN_METHOD_IDZIENNIK_WEB = 100 diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusTest.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusTest.kt index bb009d86..c13369f4 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusTest.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusTest.kt @@ -23,15 +23,23 @@ class LibrusTest(val app: App) { } val profile = Profile(1, "Profil", "xd", 1).apply { - putStudentData("accountLogin", "1234567") - //putStudentData("accountPassword", "zaq1@WSX") + //putStudentData("accountLogin", "1234567") //putStudentData("accountCode", LIBRUS_JST_DEMO_CODE) //putStudentData("accountPin", LIBRUS_JST_DEMO_PIN) + + putStudentData("accountLogin", "1234567") + + putStudentData("accountToken", "token") + putStudentData("accountTokenTime", 1569523077) } val loginStore = LoginStore(1, LOGIN_TYPE_LIBRUS, JsonObject().apply { addProperty("email", "test@example.com") addProperty("password", "zaq1@WSX") + + addProperty("accessToken", "token") + addProperty("refreshToken", "refresh") + addProperty("tokenExpiryTime", 1569523077) }).also { it.mode = LOGIN_MODE_LIBRUS_EMAIL } @@ -54,7 +62,7 @@ class LibrusTest(val app: App) { } } - LoginLibrus(data, LOGIN_METHOD_LIBRUS_SYNERGIA) { + LoginLibrus(data, LOGIN_METHOD_LIBRUS_MESSAGES) { d(TAG, "Login succeeded.") d(TAG, "Profile data: ${data.profile?.studentData?.toString()}") d(TAG, "LoginStore data: ${data.loginStore.data}") diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/DataLibrus.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/DataLibrus.kt index b6aa2a4e..8604077f 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/DataLibrus.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/DataLibrus.kt @@ -4,6 +4,8 @@ package pl.szczodrzynski.edziennik.api.v2.librus.data +import okhttp3.Cookie +import okhttp3.HttpUrl import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.api.v2.LOGIN_METHOD_LIBRUS_API import pl.szczodrzynski.edziennik.api.v2.LOGIN_METHOD_LIBRUS_MESSAGES @@ -27,10 +29,26 @@ class DataLibrus(app: App, profile: Profile?, loginStore: LoginStore) : Data(app loginMethods += LOGIN_METHOD_LIBRUS_PORTAL if (isApiLoginValid()) loginMethods += LOGIN_METHOD_LIBRUS_API - if (isSynergiaLoginValid()) + if (isSynergiaLoginValid()) { loginMethods += LOGIN_METHOD_LIBRUS_SYNERGIA - if (isMessagesLoginValid()) + app.cookieJar.saveFromResponse(null, listOf( + Cookie.Builder() + .name("DZIENNIKSID") + .value(synergiaSessionId!!) + .domain("synergia.librus.pl") + .secure().httpOnly().build() + )) + } + if (isMessagesLoginValid()) { loginMethods += LOGIN_METHOD_LIBRUS_MESSAGES + app.cookieJar.saveFromResponse(null, listOf( + Cookie.Builder() + .name("DZIENNIKSID") + .value(messagesSessionId!!) + .domain("wiadomosci.librus.pl") + .secure().httpOnly().build() + )) + } } /* _____ _ _ diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrus.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrus.kt index b79c09c1..b370305d 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrus.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrus.kt @@ -30,6 +30,7 @@ class LoginLibrus(val data: DataLibrus, vararg loginMethodIds: Int, val onSucces loginMethodList = loginMethodList.toHashSet().toMutableList() loginMethodList.sort() + data.satisfyLoginMethods() nextLoginMethod() } @@ -69,7 +70,7 @@ class LoginLibrus(val data: DataLibrus, vararg loginMethodIds: Int, val onSucces } } LOGIN_METHOD_LIBRUS_MESSAGES -> { - LoginLibrusApi(data) { + LoginLibrusMessages(data) { data.loginMethods.add(loginMethodId) onSuccess() } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusMessages.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusMessages.kt index ec3bf565..3af49a01 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusMessages.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusMessages.kt @@ -4,10 +4,15 @@ package pl.szczodrzynski.edziennik.api.v2.librus.login +import im.wangchao.mhttp.Request +import im.wangchao.mhttp.Response +import im.wangchao.mhttp.callback.TextCallbackHandler +import okhttp3.Cookie +import okhttp3.HttpUrl +import okhttp3.internal.http.HttpDate import pl.szczodrzynski.edziennik.api.v2.* import pl.szczodrzynski.edziennik.api.v2.librus.data.DataLibrus import pl.szczodrzynski.edziennik.currentTimeUnix -import pl.szczodrzynski.edziennik.isNotNullNorEmpty class LoginLibrusMessages(val data: DataLibrus, val onSuccess: () -> Unit) { companion object { @@ -21,13 +26,21 @@ class LoginLibrusMessages(val data: DataLibrus, val onSuccess: () -> Unit) { } if (data.isMessagesLoginValid()) { + data.app.cookieJar.saveFromResponse(null, listOf( + Cookie.Builder() + .name("DZIENNIKSID") + .value(data.messagesSessionId!!) + .domain("wiadomosci.librus.pl") + .secure().httpOnly().build() + )) onSuccess() } else { + data.app.cookieJar.clearForDomain("wiadomosci.librus.pl") if (data.loginMethods.contains(LOGIN_METHOD_LIBRUS_SYNERGIA)) { loginWithSynergia() } - else if (data.apiLogin != null && data.apiPassword != null) { + else if (data.apiLogin != null && data.apiPassword != null && false) { loginWithCredentials() } else { @@ -46,7 +59,41 @@ class LoginLibrusMessages(val data: DataLibrus, val onSuccess: () -> Unit) { /** * A login method using the Synergia website (/wiadomosci2 Auto Login). */ - private fun loginWithSynergia() { + private fun loginWithSynergia(url: String = "https://synergia.librus.pl/wiadomosci2") { + val callback = object : TextCallbackHandler() { + override fun onSuccess(text: String?, response: Response?) { + val location = response?.headers()?.get("Location") + when { + location?.contains("MultiDomainLogon") == true -> loginWithSynergia(location) + location?.contains("AutoLogon") == true -> { + var sessionId = data.app.cookieJar.getCookie("wiadomosci.librus.pl", "DZIENNIKSID") + sessionId = sessionId?.replace("-MAINT", "") + if (sessionId == null) { + data.error(TAG, ERROR_LOGIN_LIBRUS_MESSAGES_NO_SESSION_ID, response, text) + return + } + data.messagesSessionId = sessionId + data.messagesSessionIdExpiryTime = currentTimeUnix() + 3600 /* 1h */ + onSuccess() + } + text?.contains("eAccessDeny") == true -> data.error(TAG, ERROR_LIBRUS_MESSAGES_ACCESS_DENIED, response, text) + text?.contains("stop.png") == true -> data.error(TAG, ERROR_LIBRUS_SYNERGIA_ACCESS_DENIED, response, text) + } + } + + override fun onFailure(response: Response?, throwable: Throwable?) { + data.error(TAG, ERROR_REQUEST_FAILURE, response, throwable) + } + } + + Request.builder() + .url(url) + .userAgent(SYNERGIA_USER_AGENT) + .get() + .callback(callback) + .withClient(data.app.httpLazy) + .build() + .enqueue() } } \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusSynergia.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusSynergia.kt index ea43ff37..a78ee769 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusSynergia.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusSynergia.kt @@ -9,6 +9,7 @@ import im.wangchao.mhttp.Request import im.wangchao.mhttp.Response import im.wangchao.mhttp.callback.JsonCallbackHandler import im.wangchao.mhttp.callback.TextCallbackHandler +import okhttp3.Cookie import okhttp3.HttpUrl import pl.szczodrzynski.edziennik.api.v2.* import pl.szczodrzynski.edziennik.api.v2.librus.data.DataLibrus @@ -30,13 +31,21 @@ class LoginLibrusSynergia(val data: DataLibrus, val onSuccess: () -> Unit) { } if (data.isSynergiaLoginValid()) { + data.app.cookieJar.saveFromResponse(null, listOf( + Cookie.Builder() + .name("DZIENNIKSID") + .value(data.synergiaSessionId!!) + .domain("synergia.librus.pl") + .secure().httpOnly().build() + )) onSuccess() } else { + data.app.cookieJar.clearForDomain("synergia.librus.pl") if (data.loginMethods.contains(LOGIN_METHOD_LIBRUS_API)) { loginWithApi() } - else if (data.apiLogin != null && data.apiPassword != null) { + else if (data.apiLogin != null && data.apiPassword != null && false) { loginWithCredentials() } else { @@ -48,7 +57,6 @@ class LoginLibrusSynergia(val data: DataLibrus, val onSuccess: () -> Unit) { /** * HTML form-based login method. Uses a Synergia login and password. */ - // TODO if loginWithCredentials fails and it is possible to use API, use it private fun loginWithCredentials() { } @@ -131,13 +139,7 @@ class LoginLibrusSynergia(val data: DataLibrus, val onSuccess: () -> Unit) { override fun onSuccess(json: String?, response: Response?) { val location = response?.headers()?.get("Location") if (location?.endsWith("centrum_powiadomien") == true) { - val cookieList = data.app.cookieJar.loadForRequest(HttpUrl.get("https://synergia.librus.pl")) - var sessionId: String? = null - for (cookie in cookieList) { - if (cookie.name().equals("DZIENNIKSID", ignoreCase = true)) { - sessionId = cookie.value() - } - } + val sessionId = data.app.cookieJar.getCookie("synergia.librus.pl", "DZIENNIKSID") if (sessionId == null) { data.error(TAG, ERROR_LOGIN_LIBRUS_SYNERGIA_NO_SESSION_ID, response, json) return diff --git a/mhttp/src/main/java/im/wangchao/mhttp/internal/cookie/PersistentCookieJar.java b/mhttp/src/main/java/im/wangchao/mhttp/internal/cookie/PersistentCookieJar.java index 5be7c289..7f096d54 100644 --- a/mhttp/src/main/java/im/wangchao/mhttp/internal/cookie/PersistentCookieJar.java +++ b/mhttp/src/main/java/im/wangchao/mhttp/internal/cookie/PersistentCookieJar.java @@ -18,6 +18,7 @@ package im.wangchao.mhttp.internal.cookie; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import java.util.ArrayList; import java.util.Collections; @@ -42,21 +43,13 @@ public class PersistentCookieJar implements ClearableCookieJar { } @Override - synchronized public void saveFromResponse(HttpUrl url, List cookies) { - //Log.d("PersistentCookieJar", "FINISHING "+url.toString()); + synchronized public void saveFromResponse(@Nullable HttpUrl url, List cookies) { // cookies need to be reversed, in order to replace old cookies with these coming later // (if there are duplicate cookies in the same response) List reverseCookies = new ArrayList<>(cookies); Collections.reverse(reverseCookies); - /*for (Cookie cookie: reverseCookies) { - Log.d("PersistentCookieJar", "Saving cookie "+cookie.toString()+" from URL "+url.toString()); - }*/ cache.addAll(reverseCookies); persistor.saveAll(reverseCookies); - /*Log.d("PersistentCookieJar", "Cookies saved: "); - for (Cookie cookie : cache) { - Log.d("PersistentCookieJar", "Saving cookie " + cookie.toString() + " from URL " + url.toString()); - }*/ } @NonNull @@ -65,23 +58,15 @@ public class PersistentCookieJar implements ClearableCookieJar { List removedCookies = new ArrayList<>(); List validCookies = new ArrayList<>(); - //Log.d("PersistentCookieJar", "REQUESTING "+url.toString()); - for (Iterator it = cache.iterator(); it.hasNext(); ) { Cookie currentCookie = it.next(); - //Log.d("PersistentCookieJar", "Loading "+currentCookie.toString()+" to URL "+url.toString()); if (isCookieExpired(currentCookie)) { - //Log.d("PersistentCookieJar", "Cookie expired at "+new SimpleDateFormat("dd/MM/yyyy HH:mm:ss", Locale.getDefault()).format(new Date(currentCookie.expiresAt()))); removedCookies.add(currentCookie); it.remove(); } else if (currentCookie.matches(url)) { - //Log.d("PersistentCookieJar", "Cookie is still valid until "+new SimpleDateFormat("dd/MM/yyyy HH:mm:ss", Locale.getDefault()).format(new Date(currentCookie.expiresAt()))); validCookies.add(currentCookie); } - /*else { - Log.d("PersistentCookieJar", "URL doesn't match"); - }*/ } persistor.removeAll(removedCookies); @@ -89,6 +74,28 @@ public class PersistentCookieJar implements ClearableCookieJar { return validCookies; } + @Nullable + synchronized public String getCookie(String domain, String name) { + String cookieValue = null; + List removedCookies = new ArrayList<>(); + + for (Iterator it = cache.iterator(); it.hasNext(); ) { + Cookie currentCookie = it.next(); + if (isCookieExpired(currentCookie)) { + removedCookies.add(currentCookie); + it.remove(); + + } else if (domain.equals(currentCookie.domain()) && name.equals(currentCookie.name())) { + cookieValue = currentCookie.value(); + break; + } + } + + persistor.removeAll(removedCookies); + + return cookieValue; + } + private static boolean isCookieExpired(Cookie cookie) { return cookie.expiresAt() < System.currentTimeMillis(); }