Skip to content

Commit

Permalink
f**k google
Browse files Browse the repository at this point in the history
allow "login" for google, needed because of their policy changes
  • Loading branch information
marunjar committed Dec 26, 2023
1 parent 6a019c6 commit 03ca66e
Show file tree
Hide file tree
Showing 6 changed files with 233 additions and 68 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ public void init(Application app) {
@Override
public void sendException(Context c, Exception e, boolean fatal, List<String> additionalData) {
if (e != null) {
logger.error("{} (fatal={}, {})", e.getMessage(), fatal, additionalData);
logger.error("", e);
logger.error("{} (fatal={}, {})", e.getClass().getSimpleName(), fatal, additionalData);
}
}

Expand Down
50 changes: 50 additions & 0 deletions app/src/fdroid/java/org/voidsink/anewjkuapp/mock/DummyLogin.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* ____.____ __.____ ___ _____
* | | |/ _| | \ / _ \ ______ ______
* | | < | | / / /_\ \\____ \\____ \
* /\__| | | \| | / / | \ |_> > |_> >
* \________|____|__ \______/ \____|__ / __/| __/
* \/ \/|__| |__|
*
* Copyright (c) 2023 Paul "Marunjar" Pretsch
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/

package org.voidsink.anewjkuapp.mock;

import java.net.CookieStore;

public class DummyLogin implements IDummyLogin {
@Override
public boolean isGoogleLogin(String user, String password) {
return false;
}

@Override
public String getSessionId() {
return null;
}

@Override
public boolean isGoogleSession(String sessionId) {
return false;
}

@Override
public void prepareCookies(CookieStore cookieStore, String url) {
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,8 @@ public void sendException(Context c, Exception e, boolean fatal, List<String> ad
logger.error("sendException", e2);
}
if (e != null) {
logger.error("{} (fatal={}, {})", e.getMessage(), fatal, additionalData);
logger.error("", e);
logger.error("{} (fatal={}, {})", e.getClass().getSimpleName(), fatal, additionalData);
}
}

Expand Down
63 changes: 63 additions & 0 deletions app/src/google/java/org/voidsink/anewjkuapp/mock/DummyLogin.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* ____.____ __.____ ___ _____
* | | |/ _| | \ / _ \ ______ ______
* | | < | | / / /_\ \\____ \\____ \
* /\__| | | \| | / / | \ |_> > |_> >
* \________|____|__ \______/ \____|__ / __/| __/
* \/ \/|__| |__|
*
* Copyright (c) 2023 Paul "Marunjar" Pretsch
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/

package org.voidsink.anewjkuapp.mock;

import java.net.CookieStore;
import java.net.HttpCookie;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.List;

public class DummyLogin implements IDummyLogin {

private static final String USER = "google";
private static final String PASSWORD = "ThisIsAMockBecauseOfGooglePolicyWhichSuxx";
private static final String SESSION_ID = " e1f8f2c4-70ed-4c39-908e-89c3afc850e1";

@Override
public boolean isGoogleLogin(String user, String password) {
return USER.equals(user) && PASSWORD.equals(password);
}

@Override
public String getSessionId() {
return SESSION_ID;
}

@Override
public boolean isGoogleSession(String sessionId) {
return SESSION_ID.equals(sessionId);
}

@Override
public void prepareCookies(CookieStore cookieStore, String url) {
try {
List<HttpCookie> cookies = cookieStore.get(new URI(url));
cookies.add(new HttpCookie("JSESSIONID", getSessionId()));
} catch (URISyntaxException ignored) {
}
}
}
144 changes: 78 additions & 66 deletions app/src/main/java/org/voidsink/anewjkuapp/kusss/KusssHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@
import org.slf4j.LoggerFactory;
import org.voidsink.anewjkuapp.analytics.AnalyticsHelper;
import org.voidsink.anewjkuapp.calendar.CalendarUtils;
import org.voidsink.anewjkuapp.mock.DummyLogin;
import org.voidsink.anewjkuapp.mock.IDummyLogin;
import org.voidsink.anewjkuapp.utils.AppUtils;

import java.io.ByteArrayInputStream;
Expand Down Expand Up @@ -77,6 +79,7 @@ public class KusssHandler {
+ PATTERN_LVA_NR + "," + PATTERN_TERM + "\\)";
public static final String PATTERN_LVA_NR_SLASH_TERM = "\\("
+ PATTERN_LVA_NR + "\\/" + PATTERN_TERM + "\\)";
private static final String URL_KUSSS = "https://www.kusss.jku.at/";
private static final String URL_KUSSS_INDEX = "https://www.kusss.jku.at/kusss/index.action";
private static final String URL_MY_LVAS = "https://www.kusss.jku.at/kusss/assignment-results.action";
private static final String URL_GET_TERMS = "https://www.kusss.jku.at/kusss/listmystudentlvas.action";
Expand Down Expand Up @@ -104,6 +107,7 @@ public class KusssHandler {
private volatile static KusssHandler handler = null;
private final CookieManager mCookies;
private String mUserAgent;
private final IDummyLogin dummyLogin = new DummyLogin();

private static final Logger logger = LoggerFactory.getLogger(KusssHandler.class);

Expand Down Expand Up @@ -139,7 +143,7 @@ public static synchronized KusssHandler getInstance() {
private String getSessionIDFromCookie() {
try {
List<HttpCookie> cookies = mCookies.getCookieStore().get(
new URI("https://www.kusss.jku.at/"));
new URI(URL_KUSSS));

for (HttpCookie cookie : cookies) {
if (cookie.getName().equals("JSESSIONID")) {
Expand Down Expand Up @@ -201,74 +205,78 @@ public synchronized String login(Context c, String user, String password) {
if (!isConnected(c)) {
return null;
}
if (dummyLogin.isGoogleLogin(user, password)) {
dummyLogin.prepareCookies(mCookies.getCookieStore(), URL_KUSSS);
return dummyLogin.getSessionId();
} else {
try {

try {
user = user.toUpperCase(Locale.ROOT);
if ((user.length() > 0) && StringUtils.isNumeric(user)) {
user = "k" + user;
}

user = user.toUpperCase(Locale.ROOT);
if ((user.length() > 0) && StringUtils.isNumeric(user)) {
user = "k" + user;
mCookies.getCookieStore().removeAll();

// must request kusss once so you get a proper cookie
Jsoup.connect(URL_KUSSS_INDEX).get();

// follow SAML redirect, (followRedirect is true by default)
Document r = Jsoup.connect(URL_LOGIN).userAgent(this.mUserAgent).cookies(getCookieMap()).get();
for (int i = 0; r.selectFirst("input[name=csrf_token]") == null || i >= 5; i++) {
r = Jsoup.connect(URL_LOGIN).userAgent(this.mUserAgent).cookies(getCookieMap()).get();
} // Don't know why, but it mostly works on the third request. I guess some cookie issue

String shibUrl = r.location(); // https://shibboleth.im.jku.at/idp/profile/SAML2/Redirect/SSO?execution=e1s1
String csrf = r.selectFirst("input[name=csrf_token]").attr("value");

Document doc = Jsoup.connect(shibUrl).userAgent(this.mUserAgent).cookies(getCookieMap())
.data("csrf_token", csrf)
.data("shib_idp_ls_exception.shib_idp_session_ss", "")
.data("shib_idp_ls_success.shib_idp_session_ss", "true")
.data("shib_idp_ls_value.shib_idp_session_ss", "")
.data("shib_idp_ls_exception.shib_idp_persistent_ss", "")
.data("shib_idp_ls_success.shib_idp_persistent_ss", "true")
.data("shib_idp_ls_value.shib_idp_persistent_ss", "")
.data("shib_idp_ls_supported", "true")
.data("_eventId_proceed", "")
.post();

csrf = doc.selectFirst("input[name=csrf_token]").attr("value");
doc = Jsoup.connect(doc.location()).userAgent(this.mUserAgent).cookies(getCookieMap())
.data("j_username", user).data("j_password", password)
.data("_eventId_proceed", "Login")
.data("csrf_token", csrf)
.post();

csrf = doc.selectFirst("input[name=csrf_token]").attr("value");
doc = Jsoup.connect(doc.location()).userAgent(this.mUserAgent).cookies(getCookieMap())
.data("shib_idp_ls_exception.shib_idp_session_ss", "")
.data("shib_idp_ls_success.shib_idp_session_ss", "false")
.data("_eventId_proceed", "")
.data("csrf_token", csrf)
.post();

// parse form, if one of the expected fields is not found, login failed
String action = doc.selectFirst("form").attr("action");
String relayState = doc.selectFirst("input[name=RelayState]").attr("value");
String samlResponse = doc.selectFirst("input[name=SAMLResponse]").attr("value");

Jsoup.connect(action).userAgent(this.mUserAgent).cookies(getCookieMap())
.data("RelayState", relayState).data("SAMLResponse", samlResponse)
.post();

return getSessionIDFromCookie();
} catch (NullPointerException ne) {
logger.warn("Login failed: Invalid credentials? RelayState/SAMLResponse in response form not found.");
} catch (SocketTimeoutException e) {
// bad connection, timeout
logger.warn("login failed: connection timeout", e);
} catch (Exception e) {
AnalyticsHelper.sendException(c, e, true);
}

mCookies.getCookieStore().removeAll();

// must request kusss once so you get a proper cookie
Jsoup.connect(URL_KUSSS_INDEX).get();

// follow SAML redirect, (followRedirect is true by default)
Document r = Jsoup.connect(URL_LOGIN).userAgent(this.mUserAgent).cookies(getCookieMap()).get();
for (int i = 0; r.selectFirst("input[name=csrf_token]") == null || i >= 5; i++) {
r = Jsoup.connect(URL_LOGIN).userAgent(this.mUserAgent).cookies(getCookieMap()).get();
} // Don't know why, but it mostly works on the third request. I guess some cookie issue

String shibUrl = r.location(); // https://shibboleth.im.jku.at/idp/profile/SAML2/Redirect/SSO?execution=e1s1
String csrf = r.selectFirst("input[name=csrf_token]").attr("value");

Document doc = Jsoup.connect(shibUrl).userAgent(this.mUserAgent).cookies(getCookieMap())
.data("csrf_token", csrf)
.data("shib_idp_ls_exception.shib_idp_session_ss", "")
.data("shib_idp_ls_success.shib_idp_session_ss", "true")
.data("shib_idp_ls_value.shib_idp_session_ss", "")
.data("shib_idp_ls_exception.shib_idp_persistent_ss", "")
.data("shib_idp_ls_success.shib_idp_persistent_ss", "true")
.data("shib_idp_ls_value.shib_idp_persistent_ss", "")
.data("shib_idp_ls_supported", "true")
.data("_eventId_proceed", "")
.post();

csrf = doc.selectFirst("input[name=csrf_token]").attr("value");
doc = Jsoup.connect(doc.location()).userAgent(this.mUserAgent).cookies(getCookieMap())
.data("j_username", user).data("j_password", password)
.data("_eventId_proceed", "Login")
.data("csrf_token", csrf)
.post();

csrf = doc.selectFirst("input[name=csrf_token]").attr("value");
doc = Jsoup.connect(doc.location()).userAgent(this.mUserAgent).cookies(getCookieMap())
.data("shib_idp_ls_exception.shib_idp_session_ss", "")
.data("shib_idp_ls_success.shib_idp_session_ss", "false")
.data("_eventId_proceed", "")
.data("csrf_token", csrf)
.post();

// parse form, if one of the expected fields is not found, login failed
String action = doc.selectFirst("form").attr("action");
String relayState = doc.selectFirst("input[name=RelayState]").attr("value");
String samlResponse = doc.selectFirst("input[name=SAMLResponse]").attr("value");

Jsoup.connect(action).userAgent(this.mUserAgent).cookies(getCookieMap())
.data("RelayState", relayState).data("SAMLResponse", samlResponse)
.post();

return getSessionIDFromCookie();
} catch (NullPointerException ne) {
logger.warn("Login failed: Invalid credentials? RelayState/SAMLResponse in response form not found.");
} catch (SocketTimeoutException e) {
// bad connection, timeout
logger.warn("login failed: connection timeout", e);
} catch (Exception e) {
AnalyticsHelper.sendException(c, e, true);
return null;
}
return null;
}

private Map<String, String> getCookieMap() {
Expand All @@ -291,6 +299,10 @@ public synchronized boolean isLoggedIn(Context c, String sessionId) {
if (!isConnected(c)) {
return false;
}
if (dummyLogin.isGoogleSession(sessionId)) {
dummyLogin.prepareCookies(mCookies.getCookieStore(), URL_KUSSS);
return true;
}
try {
Document doc = getDocument(Jsoup.connect(URL_KUSSS_INDEX).userAgent(this.mUserAgent).cookies(getCookieMap()).timeout(TIMEOUT_LOGIN).followRedirects(true));

Expand All @@ -309,7 +321,7 @@ private boolean isLoggedIn(Context c, Document doc) {
return false;
}
Element logoutElement = doc.selectFirst(SELECT_LOGOUT);
return logoutElement.text().contains("Logout Info");
return logoutElement != null && logoutElement.text().contains("Logout Info");
}

public synchronized boolean isAvailable(Context c, String sessionId,
Expand Down
38 changes: 38 additions & 0 deletions app/src/main/java/org/voidsink/anewjkuapp/mock/IDummyLogin.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* ____.____ __.____ ___ _____
* | | |/ _| | \ / _ \ ______ ______
* | | < | | / / /_\ \\____ \\____ \
* /\__| | | \| | / / | \ |_> > |_> >
* \________|____|__ \______/ \____|__ / __/| __/
* \/ \/|__| |__|
*
* Copyright (c) 2023 Paul "Marunjar" Pretsch
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/

package org.voidsink.anewjkuapp.mock;

import java.net.CookieStore;

public interface IDummyLogin {
boolean isGoogleLogin(String user, String password);

String getSessionId();

boolean isGoogleSession(String sessionId);

void prepareCookies(CookieStore cookieStore, String url);
}

0 comments on commit 03ca66e

Please sign in to comment.