diff --git a/README.md b/README.md
index 80953b2..5a54a25 100644
--- a/README.md
+++ b/README.md
@@ -9,16 +9,26 @@ This is a browser script, that performs an automatic login for the following TU
- [Mailbox@TUD](https://msx.tu-dresden.de/owa)
- [LSKOnline](https://lskonline.tu-dresden.de)
- [ShareLaTeX](https://tex.zih.tu-dresden.de)
-- and any other service with the TUD Login Page
+- [Moodle](https://tud.uni-leipzig.de/moodle2/)
+- and any other service with the TUD Login Page (maybe something new comes soon).
-## Installation
+## Installation for use with an application or for manual input
1. Install [Tampermonkey](https://www.tampermonkey.net) for your browser
-2. Download the [TUD-AutoLogin UserScript](https://raw.githubusercontent.com/spyfly/TUD-AutoLogin/master/script.user.js)
+2. Download the [TUD-AutoLogin UserScript](https://raw.githubusercontent.com/FurTactics/TUD-AutoLogin/master/script.user.js)
3. Click the install button to complete the installation
+4. Visit the [configuration page](https://tud-autologin.spyfly.xyz/configuration) after installing the script, to add your login credentials
+5. (optional) You can use [2FAS Browser Extension](https://2fas.com/browser-extension/) to automate the entry, but confirmation on your phone will be required each time
+6. Enjoy :partying_face:
-## Configuration
-Visit the [configuration page](https://tud-autologin.spyfly.xyz/configuration) after installing the script, to add your login credentials.
+## Installation for those who want to forever forget about the existence of login pages (nothing manual)
+1. Install [Tampermonkey](https://www.tampermonkey.net) for your browser
+2. Download the [TUD-AutoLogin UserScript](https://raw.githubusercontent.com/FurTactics/TUD-AutoLogin/master/script_only_for_Geeks.user.js)
+3. Click the install button to complete the installation
+4. Visit the [configuration page](https://tud-autologin.spyfly.xyz/configuration) after installing the script, to add your login credentials
+> You need a 32-digit secret code (Geheimschlüssel), which can be found in the [2FAS](https://2fas.com/) app or by decoding the QR code.
+5. Enjoy more :partying_face:
+**else:** check [Known Issues](https://github.com/FurTactics/TUD-AutoLogin/tree/master?tab=readme-ov-file#known-issues-and-faq) or open a [new issue](https://github.com/FurTactics/TUD-AutoLogin/issues).
## Compatibility
This script is compatible with any browser capable of supporting [Tampermonkey](https://www.tampermonkey.net) or [Greasemonkey (Firefox only)](https://addons.mozilla.org/de/firefox/addon/greasemonkey/)
@@ -26,13 +36,40 @@ Compatible Platforms:
- Windows
- Linux
- MacOS
-- Android (tested with [Firefox Nightly](https://blog.mozilla.org/addons/2020/09/29/expanded-extension-support-in-firefox-for-android-nightly/))
+- Android (tested with [Firefox Nightly](https://blog.mozilla.org/addons/2020/09/29/expanded-extension-support-in-firefox-for-android-nightly/) and [Kiwi](https://kiwibrowser.com/))
- iOS/iPadOS via [Custom Shortcut](https://www.icloud.com/shortcuts/b3bef7f4836d461fb73bc3a39f9e3411) (Safari only)
## Usage
Visit your favorite TUD websites and enjoy automatic login.
-On iOS/iPadOS you will have to run the [Custom Shortcut](https://www.icloud.com/shortcuts/b3bef7f4836d461fb73bc3a39f9e3411) via the Share Menu in Safari.
+On iOS/iPadOS you will have to run the Custom Shortcut via the Share Menu in Safari.
+
+## Future Development and Issues
+If you have improvement ideas or encounter issues with the script don't hesitate to open a [new issue](https://github.com/FurTactics/TUD-AutoLogin/issues). This could be a request for adding more supported services or reporting a bug.
+
-## Future development and issues
-If you have improvement ideas or encounter issues with the script don't hesitate to open a [new issue](https://github.com/spyfly/TUD-AutoLogin/issues). This could be a request for adding more supported services or reporting a bug.
+## Known Issues and FAQ
+
+No notifications
+
+You don't see notifications from the configuration site after clicking the test button.
+
+Windows
+
+Try turning off do not disturb mode and allowing browser notifications.
+
+Mac OS
+
+(unclear) Try turning on notifications in your browser settings (System Preferences > Notifications > Chrome or another Browswer)
+check screenshot.
+
+
+
+Where is my 32-digit secret code?
+The best and most secure way to find a token is to look it up in the 2FAS app. Long hold the TU Dresden card, then edit. You need a 32-digit key that needs to be copied.
+
+
+
+If I don't use 2FAS?
+If you do not use 2FAS, then all similar applications should have a function for viewing the secret code. If not, you can install the 2FAS app and export your TU Dresden entry there.
+
\ No newline at end of file
diff --git a/script.user.js b/script.user.js
index 8b8684e..4588f27 100644
--- a/script.user.js
+++ b/script.user.js
@@ -1,27 +1,31 @@
// ==UserScript==
-// @name TUD AutoLogin
-// @namespace http://tampermonkey.net/
-// @version 0.3.5
-// @description Stop wasting your time entering login credentials or pressing useless buttons!
-// @author spyfly
-// @website https://tud-autologin.spyfly.xyz/
-// @match https://bildungsportal.sachsen.de/*
-// @match https://idp2.tu-dresden.de/*
-// @match https://jexam.inf.tu-dresden.de/*
-// @match https://selma.tu-dresden.de/*
-// @match https://exam.zih.tu-dresden.de/*
-// @match https://exam2.zih.tu-dresden.de/*
-// @match https://exam3.zih.tu-dresden.de/*
-// @match https://qis.dez.tu-dresden.de/qisserver/*
-// @match https://msx.tu-dresden.de/owa/auth/logon*
-// @match https://lskonline.tu-dresden.de/lskonline/de/102.0.1*
-// @match https://idp.tu-dresden.de/idp*
-// @match https://tud-autologin.spyfly.xyz/configuration/
-// @match https://tex.zih.tu-dresden.de/*
-// @supportURL https://github.com/spyfly/TUD-AutoLogin/issues
-// @updateURL https://raw.githubusercontent.com/spyfly/TUD-AutoLogin/master/script.user.js
-// @grant GM_getValue
-// @grant GM_setValue
+// @name TUD AutoLogin
+// @namespace https://tud-autologin.spyfly.xyz/
+// @version 0.4.2
+// @description Stop wasting your time entering login credentials or pressing useless buttons! (updated from spyfly)
+// @description:de Verschwende keine Zeit mehr mit dem Eingeben von Anmeldedaten oder dem Drücken sinnloser Tasten!
+// @description:ru Перестань тратить время на ввод данных или кликанье бесполезных кнопок!
+// @author FurTactics
+// @icon https://upload.wikimedia.org/wikipedia/commons/a/a3/Logo_TU_Dresden_small.svg
+// @match https://bildungsportal.sachsen.de/*
+// @match https://idp2.tu-dresden.de/*
+// @match https://jexam.inf.tu-dresden.de/*
+// @match https://selma.tu-dresden.de/*
+// @match https://exam.zih.tu-dresden.de/*
+// @match https://exam2.zih.tu-dresden.de/*
+// @match https://exam3.zih.tu-dresden.de/*
+// @match https://qis.dez.tu-dresden.de/qisserver/*
+// @match https://msx.tu-dresden.de/owa/auth/logon*
+// @match https://lskonline.tu-dresden.de/lskonline/de/102.0.1*
+// @match https://idp.tu-dresden.de/idp*
+// @match https://tud-autologin.spyfly.xyz/configuration/
+// @match https://tex.zih.tu-dresden.de/*
+// @match https://tud.uni-leipzig.de/moodle2/*
+// @supportURL https://github.com/FurTactics/TUD-AutoLogin/issues
+// @updateURL https://raw.githubusercontent.com/FurTactics/TUD-AutoLogin/master/script.user.js
+// @downloadURL https://raw.githubusercontent.com/FurTactics/TUD-AutoLogin/master/script.user.js
+// @grant GM_getValue
+// @grant GM_setValue
// ==/UserScript==
(async function () {
@@ -47,8 +51,10 @@
const isLskOnline = (window.location.host == "lskonline.tu-dresden.de");
const isTudIdp = (window.location.host == "idp.tu-dresden.de");
const isShareLatex = (window.location.host == "tex.zih.tu-dresden.de");
+ const isMoodle = (window.location.host == "tud.uni-leipzig.de")
const credentialsAvailable = (tud.username.length > 0 && tud.password.length > 0);
+ var stats;
if (isConfigPage) {
document.getElementById("notinstalled").remove();
@@ -60,6 +66,7 @@
username: document.getElementById("username").value,
password: document.getElementById("password").value
});
+ GM_setValue("stats", null);
alert("Configuration updated!")
});
} else if (isOpalLoginPage || isTudExamLoginPage) {
@@ -89,12 +96,13 @@
}
loginSelector.selectedIndex = loginIndex;
- //Press Login Button
+ // Press Login Button
document.querySelector("button[name$='shibLogin']").click();
}
} else if (isTudLoginPage || isTudIdp) {
// We are on the TUD I2DP Page
const hasLoginField = (document.getElementById("username") != undefined);
+ const hasSecretField = (document.getElementById("fudis_otp_input") != undefined);
if (hasLoginField) {
// Try to fill in credentials
@@ -102,10 +110,14 @@
document.getElementById("password").value = tud.password;
if (credentialsAvailable) {
document.getElementsByName("_eventId_proceed")[0].click();
+ GM_setValue("stats", GM_getValue("stats") + 1);
}
- } else {
- // Just press the continue button
- document.getElementsByName("_eventId_proceed")[0].click();
+ }
+ if (hasSecretField) {
+ while (document.getElementById("fudis_otp_input").value.length < 6) {
+ await sleep(1500);
+ }
+ document.getElementsByName("_eventId_proceed")[0].click();
}
} else if (isJExam) {
// AutoLogin for JExam 5
@@ -116,6 +128,7 @@
document.getElementById("username").value = tud.username;
document.getElementById("password").value = tud.password;
if (credentialsAvailable) {
+ GM_setValue("stats", GM_getValue('stats') + 1);
document.getElementsByClassName("submit")[0].click();
}
}
@@ -128,12 +141,25 @@
document.getElementById("logIn_btn").click();
}
}
+ } else if (isMoodle) {
+ // AutoLogin for Moodle
+ if (window.location.pathname == "/moodle2/login/index.php") {
+ // Check if we are on the login page
+ document.querySelector('[href="https://tud.uni-leipzig.de/moodle2/auth/shibboleth/index.php"]').click();
+ } else {
+ //Go to the login page if we need to login
+ const loginBtn = document.querySelector('[href="https://tud.uni-leipzig.de/moodle2/login/index.php"]');
+ if (loginBtn) {
+ loginBtn.click();
+ }
+ }
} else if (isQisServer) {
//AutoLogin for QISServer
if (document.getElementsByClassName("loginuser").length >= 1) {
document.getElementsByClassName("loginuser")[0].value = tud.username;
document.getElementsByClassName("loginpass")[0].value = tud.password;
if (credentialsAvailable) {
+ GM_setValue("stats", GM_getValue('stats') + 1);
document.getElementsByClassName("submit")[0].click();
}
}
@@ -142,6 +168,7 @@
document.getElementById('username').value = tud.username;
document.getElementById('password').value = tud.password;
if (credentialsAvailable) {
+ GM_setValue("stats", GM_getValue('stats') + 1);
document.getElementsByClassName("signinbutton")[0].click();
}
} else if (isLskOnline) {
diff --git a/script_only_for_Geeks.user.js b/script_only_for_Geeks.user.js
new file mode 100644
index 0000000..2c19292
--- /dev/null
+++ b/script_only_for_Geeks.user.js
@@ -0,0 +1,451 @@
+// ==UserScript==
+// @name TUD AutoLogin with generating the 2FA
+// @namespace https://tud-autologin.spyfly.xyz/
+// @version 0.4.5
+// @description Stop wasting your time entering login credentials or pressing useless buttons! The script allows you to fully automate the entry of all login details, including two-factor authentication. (updated from spyfly)
+// @description:de Verschwende keine Zeit mehr mit dem Eingeben von Anmeldedaten oder dem Drücken sinnloser Tasten! Mit dem Skript kann man die Eingabe aller Anmeldedaten, einschließlich der Zwei-Faktor-Authentifizierung, vollständig automatisieren.
+// @description:ru Перестань тратить время на ввод данных или кликанье бесполезных кнопок! Скрипт позволяет полностью автоматизировать ввод всех данных, включая двухфакторную аутентификацию.
+// @author FurTactics
+// @icon https://upload.wikimedia.org/wikipedia/commons/a/a3/Logo_TU_Dresden_small.svg
+// @match https://bildungsportal.sachsen.de/*
+// @match https://idp2.tu-dresden.de/*
+// @match https://jexam.inf.tu-dresden.de/*
+// @match https://selma.tu-dresden.de/*
+// @match https://exam.zih.tu-dresden.de/*
+// @match https://exam2.zih.tu-dresden.de/*
+// @match https://exam3.zih.tu-dresden.de/*
+// @match https://qis.dez.tu-dresden.de/qisserver/*
+// @match https://msx.tu-dresden.de/owa/auth/logon*
+// @match https://lskonline.tu-dresden.de/lskonline/de/102.0.1*
+// @match https://idp.tu-dresden.de/idp*
+// @match https://tud-autologin.spyfly.xyz/configuration/
+// @match https://tex.zih.tu-dresden.de/*
+// @match https://tud.uni-leipzig.de/moodle2/*
+// @match https://artemis-app.inf.tu-dresden.de/*
+// @supportURL https://github.com/FurTactics/TUD-AutoLogin/issues
+// @downloadURL https://raw.githubusercontent.com/FurTactics/TUD-AutoLogin/master/script_only_for_Geeks.user.js
+// @grant GM_getValue
+// @grant GM_setValue
+// @grant GM_notification
+// @grant GM_registerMenuCommand
+// @grant GM_listValues
+// @grant GM_deleteValue
+// ==/UserScript==
+
+'use strict'; (function (G) {
+ function r(d, b, c) {
+ var h = 0, a = [], f = 0, g, m, k, e, l, p, q, t, w = !1, n = [], u = [], v, r = !1; c = c || {}; g = c.encoding || "UTF8"; v = c.numRounds || 1; if (v !== parseInt(v, 10) || 1 > v) throw Error("numRounds must a integer >= 1"); if ("SHA-1" === d) l = 512, p = z, q = H, e = 160, t = function (a) { return a.slice() }; else throw Error("Chosen SHA variant is not supported"); k = A(b, g); m = x(d); this.setHMACKey = function (a, f, b) {
+ var c; if (!0 === w) throw Error("HMAC key already set"); if (!0 === r) throw Error("Cannot set HMAC key after calling update");
+ g = (b || {}).encoding || "UTF8"; f = A(f, g)(a); a = f.binLen; f = f.value; c = l >>> 3; b = c / 4 - 1; if (c < a / 8) { for (f = q(f, a, 0, x(d), e); f.length <= b;)f.push(0); f[b] &= 4294967040 } else if (c > a / 8) { for (; f.length <= b;)f.push(0); f[b] &= 4294967040 } for (a = 0; a <= b; a += 1)n[a] = f[a] ^ 909522486, u[a] = f[a] ^ 1549556828; m = p(n, m); h = l; w = !0
+ }; this.update = function (b) { var e, g, c, d = 0, q = l >>> 5; e = k(b, a, f); b = e.binLen; g = e.value; e = b >>> 5; for (c = 0; c < e; c += q)d + l <= b && (m = p(g.slice(c, c + q), m), d += l); h += d; a = g.slice(d >>> 5); f = b % l; r = !0 }; this.getHash = function (b, g) {
+ var c, k, l, p; if (!0 ===
+ w) throw Error("Cannot call getHash after setting HMAC key"); l = B(g); switch (b) { case "HEX": c = function (a) { return C(a, e, l) }; break; case "B64": c = function (a) { return D(a, e, l) }; break; case "BYTES": c = function (a) { return E(a, e) }; break; case "ARRAYBUFFER": try { k = new ArrayBuffer(0) } catch (I) { throw Error("ARRAYBUFFER not supported by this environment"); } c = function (a) { return F(a, e) }; break; default: throw Error("format must be HEX, B64, BYTES, or ARRAYBUFFER"); }p = q(a.slice(), f, h, t(m), e); for (k = 1; k < v; k += 1)p = q(p, e, 0, x(d), e);
+ return c(p)
+ }; this.getHMAC = function (b, g) {
+ var c, k, n, r; if (!1 === w) throw Error("Cannot call getHMAC without first setting HMAC key"); n = B(g); switch (b) {
+ case "HEX": c = function (a) { return C(a, e, n) }; break; case "B64": c = function (a) { return D(a, e, n) }; break; case "BYTES": c = function (a) { return E(a, e) }; break; case "ARRAYBUFFER": try { c = new ArrayBuffer(0) } catch (I) { throw Error("ARRAYBUFFER not supported by this environment"); } c = function (a) { return F(a, e) }; break; default: throw Error("outputFormat must be HEX, B64, BYTES, or ARRAYBUFFER");
+ }k = q(a.slice(), f, h, t(m), e); r = p(u, x(d)); r = q(k, e, l, r, e); return c(r)
+ }
+ } function C(d, b, c) { var h = ""; b /= 8; var a, f; for (a = 0; a < b; a += 1)f = d[a >>> 2] >>> 8 * (3 + a % 4 * -1), h += "0123456789abcdef".charAt(f >>> 4 & 15) + "0123456789abcdef".charAt(f & 15); return c.outputUpper ? h.toUpperCase() : h } function D(d, b, c) {
+ var h = "", a = b / 8, f, g, m; for (f = 0; f < a; f += 3)for (g = f + 1 < a ? d[f + 1 >>> 2] : 0, m = f + 2 < a ? d[f + 2 >>> 2] : 0, m = (d[f >>> 2] >>> 8 * (3 + f % 4 * -1) & 255) << 16 | (g >>> 8 * (3 + (f + 1) % 4 * -1) & 255) << 8 | m >>> 8 * (3 + (f + 2) % 4 * -1) & 255, g = 0; 4 > g; g += 1)8 * f + 6 * g <= b ? h += "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".charAt(m >>>
+ 6 * (3 - g) & 63) : h += c.b64Pad; return h
+ } function E(d, b) { var c = "", h = b / 8, a, f; for (a = 0; a < h; a += 1)f = d[a >>> 2] >>> 8 * (3 + a % 4 * -1) & 255, c += String.fromCharCode(f); return c } function F(d, b) { var c = b / 8, h, a = new ArrayBuffer(c), f; f = new Uint8Array(a); for (h = 0; h < c; h += 1)f[h] = d[h >>> 2] >>> 8 * (3 + h % 4 * -1) & 255; return a } function B(d) {
+ var b = { outputUpper: !1, b64Pad: "=", shakeLen: -1 }; d = d || {}; b.outputUpper = d.outputUpper || !1; !0 === d.hasOwnProperty("b64Pad") && (b.b64Pad = d.b64Pad); if ("boolean" !== typeof b.outputUpper) throw Error("Invalid outputUpper formatting option");
+ if ("string" !== typeof b.b64Pad) throw Error("Invalid b64Pad formatting option"); return b
+ } function A(d, b) {
+ var c; switch (b) { case "UTF8": case "UTF16BE": case "UTF16LE": break; default: throw Error("encoding must be UTF8, UTF16BE, or UTF16LE"); }switch (d) {
+ case "HEX": c = function (b, a, f) {
+ var g = b.length, c, d, e, l, p; if (0 !== g % 2) throw Error("String of HEX type must be in byte increments"); a = a || [0]; f = f || 0; p = f >>> 3; for (c = 0; c < g; c += 2) {
+ d = parseInt(b.substr(c, 2), 16); if (isNaN(d)) throw Error("String of HEX type contains invalid characters");
+ l = (c >>> 1) + p; for (e = l >>> 2; a.length <= e;)a.push(0); a[e] |= d << 8 * (3 + l % 4 * -1)
+ } return { value: a, binLen: 4 * g + f }
+ }; break; case "TEXT": c = function (c, a, f) {
+ var g, d, k = 0, e, l, p, q, t, n; a = a || [0]; f = f || 0; p = f >>> 3; if ("UTF8" === b) for (n = 3, e = 0; e < c.length; e += 1)for (g = c.charCodeAt(e), d = [], 128 > g ? d.push(g) : 2048 > g ? (d.push(192 | g >>> 6), d.push(128 | g & 63)) : 55296 > g || 57344 <= g ? d.push(224 | g >>> 12, 128 | g >>> 6 & 63, 128 | g & 63) : (e += 1, g = 65536 + ((g & 1023) << 10 | c.charCodeAt(e) & 1023), d.push(240 | g >>> 18, 128 | g >>> 12 & 63, 128 | g >>> 6 & 63, 128 | g & 63)), l = 0; l < d.length; l += 1) {
+ t = k +
+ p; for (q = t >>> 2; a.length <= q;)a.push(0); a[q] |= d[l] << 8 * (n + t % 4 * -1); k += 1
+ } else if ("UTF16BE" === b || "UTF16LE" === b) for (n = 2, d = "UTF16LE" === b && !0 || "UTF16LE" !== b && !1, e = 0; e < c.length; e += 1) { g = c.charCodeAt(e); !0 === d && (l = g & 255, g = l << 8 | g >>> 8); t = k + p; for (q = t >>> 2; a.length <= q;)a.push(0); a[q] |= g << 8 * (n + t % 4 * -1); k += 2 } return { value: a, binLen: 8 * k + f }
+ }; break; case "B64": c = function (b, a, f) {
+ var c = 0, d, k, e, l, p, q, n; if (-1 === b.search(/^[a-zA-Z0-9=+\/]+$/)) throw Error("Invalid character in base-64 string"); k = b.indexOf("="); b = b.replace(/\=/g,
+ ""); if (-1 !== k && k < b.length) throw Error("Invalid '=' found in base-64 string"); a = a || [0]; f = f || 0; q = f >>> 3; for (k = 0; k < b.length; k += 4) { p = b.substr(k, 4); for (e = l = 0; e < p.length; e += 1)d = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".indexOf(p[e]), l |= d << 18 - 6 * e; for (e = 0; e < p.length - 1; e += 1) { n = c + q; for (d = n >>> 2; a.length <= d;)a.push(0); a[d] |= (l >>> 16 - 8 * e & 255) << 8 * (3 + n % 4 * -1); c += 1 } } return { value: a, binLen: 8 * c + f }
+ }; break; case "BYTES": c = function (b, a, c) {
+ var d, m, k, e, l; a = a || [0]; c = c || 0; k = c >>> 3; for (m = 0; m < b.length; m +=
+ 1)d = b.charCodeAt(m), l = m + k, e = l >>> 2, a.length <= e && a.push(0), a[e] |= d << 8 * (3 + l % 4 * -1); return { value: a, binLen: 8 * b.length + c }
+ }; break; case "ARRAYBUFFER": try { c = new ArrayBuffer(0) } catch (h) { throw Error("ARRAYBUFFER not supported by this environment"); } c = function (b, a, c) { var d, m, k, e, l; a = a || [0]; c = c || 0; m = c >>> 3; l = new Uint8Array(b); for (d = 0; d < b.byteLength; d += 1)e = d + m, k = e >>> 2, a.length <= k && a.push(0), a[k] |= l[d] << 8 * (3 + e % 4 * -1); return { value: a, binLen: 8 * b.byteLength + c } }; break; default: throw Error("format must be HEX, TEXT, B64, BYTES, or ARRAYBUFFER");
+ }return c
+ } function n(d, b) { return d << b | d >>> 32 - b } function u(d, b) { var c = (d & 65535) + (b & 65535); return ((d >>> 16) + (b >>> 16) + (c >>> 16) & 65535) << 16 | c & 65535 } function y(d, b, c, h, a) { var f = (d & 65535) + (b & 65535) + (c & 65535) + (h & 65535) + (a & 65535); return ((d >>> 16) + (b >>> 16) + (c >>> 16) + (h >>> 16) + (a >>> 16) + (f >>> 16) & 65535) << 16 | f & 65535 } function x(d) { var b = []; if ("SHA-1" === d) b = [1732584193, 4023233417, 2562383102, 271733878, 3285377520]; else throw Error("No SHA variants supported"); return b } function z(d, b) {
+ var c = [], h, a, f, g, m, k, e; h = b[0]; a = b[1];
+ f = b[2]; g = b[3]; m = b[4]; for (e = 0; 80 > e; e += 1)c[e] = 16 > e ? d[e] : n(c[e - 3] ^ c[e - 8] ^ c[e - 14] ^ c[e - 16], 1), k = 20 > e ? y(n(h, 5), a & f ^ ~a & g, m, 1518500249, c[e]) : 40 > e ? y(n(h, 5), a ^ f ^ g, m, 1859775393, c[e]) : 60 > e ? y(n(h, 5), a & f ^ a & g ^ f & g, m, 2400959708, c[e]) : y(n(h, 5), a ^ f ^ g, m, 3395469782, c[e]), m = g, g = f, f = n(a, 30), a = h, h = k; b[0] = u(h, b[0]); b[1] = u(a, b[1]); b[2] = u(f, b[2]); b[3] = u(g, b[3]); b[4] = u(m, b[4]); return b
+ } function H(d, b, c, h) {
+ var a; for (a = (b + 65 >>> 9 << 4) + 15; d.length <= a;)d.push(0); d[b >>> 5] |= 128 << 24 - b % 32; b += c; d[a] = b & 4294967295; d[a - 1] = b / 4294967296 | 0;
+ b = d.length; for (a = 0; a < b; a += 16)h = z(d.slice(a, a + 16), h); return h
+ } "function" === typeof define && define.amd ? define(function () { return r }) : "undefined" !== typeof exports ? ("undefined" !== typeof module && module.exports && (module.exports = r), exports = r) : G.jsSHA = r
+})(this);
+
+(async function () {
+ 'use strict';
+ //Load Configuration values
+ var tud = {
+ username: "",
+ password: "",
+ secret: ""
+ }
+ if (GM_getValue("tud_creds") != undefined) {
+ tud = GM_getValue("tud_creds");
+ }
+
+ // Code starts here
+ const isConfigPage = (window.location.host == "tud-autologin.spyfly.xyz");
+ const isOpalLoginPage = (window.location.host == "bildungsportal.sachsen.de");
+ const isTudExamLoginPage = (window.location.host.startsWith("exam") && window.location.host.endsWith(".zih.tu-dresden.de"));
+ const isTudLoginPage = (window.location.host == "idp2.tu-dresden.de");
+ const isJExam = (window.location.host == "jexam.inf.tu-dresden.de");
+ const isSelma = (window.location.host == "selma.tu-dresden.de");
+ const isQisServer = (window.location.host == "qis.dez.tu-dresden.de");
+ const isOWA = (window.location.host == "msx.tu-dresden.de");
+ const isLskOnline = (window.location.host == "lskonline.tu-dresden.de");
+ const isTudIdp = (window.location.host == "idp.tu-dresden.de");
+ const isShareLatex = (window.location.host == "tex.zih.tu-dresden.de");
+ const isMoodle = (window.location.host == "tud.uni-leipzig.de");
+
+ const credentialsAvailable = (tud.username.length > 0 && tud.password.length > 0);
+ const secretIsAvailable = tud.secret.length == 32;
+ const stat = GM_getValue("stats") == undefined ? 0 : GM_getValue("stats");
+ const menu_command_id_1 = GM_registerMenuCommand("You saved " + stat + " clicks 👍", function (event) { console.clear; console.log("You are awesome. " + stat + " clicks is more than " + Math.round(stat / 60) + " minutes. If you want, you can support me on GitHub https://github.com/FurTactics/TUD-AutoLogin/") }, { autoClose: false });
+ const menu_command_id_2 = GM_registerMenuCommand("Delete all your saved data", function (event) {
+ const keys = GM_listValues();
+ for (let i = 0; i < keys.length; i++) {
+ const key = keys[i];
+ GM_deleteValue(key);
+ }
+ GM_notification({
+ text: "Your data has been successfully deleted\nClick to visit configuration website",
+ title: "TUD Autologin",
+ url: 'https://tud-autologin.spyfly.xyz/configuration/#configuration',
+ silent: true,
+ timeout: 10000,
+ onclick: (event) => { }
+ });
+ }, { autoClose: true });
+
+
+ if (isConfigPage) {
+ // Change all links to GitHub on a config page
+ var links = [document.querySelectorAll("a")[0], document.querySelectorAll("a")[2], document.querySelectorAll("a")[4]];
+ for (var i = 0; i < links.length; i++) {
+ links[i].getAttribute("href");
+ links[i].setAttribute("href", "https://github.com/FurTactics/TUD-AutoLogin/");
+ links[i].addEventListener("click", function (event) {
+ event.preventDefault(); // Prevent the default action
+ window.open("https://github.com/FurTactics/TUD-AutoLogin/", "_blank"); // Open the link in a new tab
+ });
+ }
+
+ // Add GitHub link in footer
+ const footer = document.querySelector('.site-footer');
+ if (footer) {
+ const spyflyLink = footer.querySelector('a[href="https://github.com/spyfly"]');
+ if (spyflyLink) {
+ // Create and insert "and" text node
+ const andText = document.createTextNode(' and modified by ');
+ spyflyLink.parentNode.insertBefore(andText, spyflyLink.nextSibling);
+ // create link
+ const newLink = document.createElement('a');
+ newLink.href = 'https://github.com/FurTactics/';
+ newLink.textContent = 'FurTactics';
+ // Insert the new link after the spyfly link
+ andText.parentNode.insertBefore(newLink, andText.nextSibling);
+ }
+ }
+
+ // Test notification
+ const p_6 = document.querySelectorAll("p")[6];
+ p_6.textContent = "Make sure, that you can receive notifications from browser.";
+
+ const p_before_button = document.createElement('p');
+ p_before_button.textContent = 'Here is a test.';
+ p_6.parentNode.insertBefore(p_before_button, p_6.nextSibling);
+
+ const button = document.createElement('button');
+ button.textContent = 'Test';
+ button.addEventListener('click', () => {
+ GM_notification({
+ title: 'Test Notification',
+ text: 'This is a test notification from the script.\nYou will receive such notifications if an incorrect 2FA code is entered.',
+ timeout: 10000
+ });
+ });
+ p_before_button.parentNode.insertBefore(button, p_before_button.nextSibling);
+
+ const p_after_button = document.createElement('p');
+ p_after_button.textContent = 'If you haven\'t received a notification, try turning off do not disturb mode.';
+ button.parentNode.insertBefore(p_after_button, button.nextSibling);
+
+ const p_congratulation = document.createElement('p');
+ p_congratulation.textContent = 'Otherwise, welcome to the world of saved time 😉';
+ p_after_button.parentNode.insertBefore(p_congratulation, p_after_button.nextSibling);
+
+ document.getElementById("notinstalled").remove();
+ document.getElementById("username").value = tud.username;
+ document.getElementById("password").value = tud.password;
+
+ document.getElementById("save").addEventListener("click", function () {
+ let secretInput = prompt("Enter your secret code with 32 letters and digits from QR-Code");
+ GM_setValue("tud_creds", {
+ username: document.getElementById("username").value,
+ password: document.getElementById("password").value,
+ secret: secretInput
+ });
+ GM_setValue("stats", 0);
+ alert("Is your code from app: " + getOTP(secretInput) + "?\nIf not, please check your token");
+ alert("Configuration updated!");
+ });
+ } else if (isOpalLoginPage || isTudExamLoginPage) {
+ const mainPageLoginBtn = document.querySelector("button[name=shibLogin]");
+ const contentPageLoginBtn = document.querySelector('.btn-sm[title="Login"]');
+ var loginSelector = document.querySelector("select[name$='wayfselection']");
+ var loginIndex;
+
+ //Do we have to perform login action in the first place?
+ if (mainPageLoginBtn || contentPageLoginBtn) {
+ if (contentPageLoginBtn) {
+
+ contentPageLoginBtn.click();
+ GM_setValue("stats", GM_getValue('stats') + 1);
+ // Wait for Login Prompt to appear
+ while (loginSelector == null) {
+ loginSelector = document.querySelector("select[name$='wayfselection']");
+ await sleep(100);
+ }
+ }
+
+ // Select TU-Dresden as Login Option
+ for (const option of loginSelector.options) {
+ if (option.text == "TU Dresden") {
+ loginIndex = option.index;
+ break;
+ }
+ }
+ loginSelector.selectedIndex = loginIndex;
+
+ // Press Login Button
+ document.querySelector("button[name$='shibLogin']").click();
+ GM_setValue("stats", GM_getValue('stats') + 1);
+ }
+ }
+ else if (isTudLoginPage || isTudIdp) {
+ // Add dark theme
+ darkThemeIDP();
+ // We are on the TUD I2DP Page
+ const hasLoginField = (document.getElementById("username") != undefined);
+ const hasSecretField = (document.getElementById("fudis_otp_input") != undefined);
+
+ if (hasLoginField) {
+ // Try to fill in credentials
+ document.getElementById("username").value = tud.username;
+ document.getElementById("password").value = tud.password;
+ if (credentialsAvailable) {
+ document.getElementsByName("_eventId_proceed")[0].click();
+ GM_setValue("isSecretEntered", false);
+ GM_setValue("stats", GM_getValue("stats") + 1);
+ }
+ }
+ if (hasSecretField && secretIsAvailable) {
+ enterSecret();
+ }
+ }
+ else if (isJExam) {
+ // AutoLogin for JExam 5
+ if (window.location.pathname == "/") {
+ window.location = "https://jexam.inf.tu-dresden.de/de.jexam.web.v5/"
+ } else if (window.location.pathname == "/de.jexam.web.v5/spring/welcome") {
+ // Try to fill in credentials
+ document.getElementById("username").value = tud.username;
+ document.getElementById("password").value = tud.password;
+ if (credentialsAvailable) {
+ document.getElementsByClassName("submit")[0].click();
+ GM_setValue("stats", GM_getValue('stats') + 1);
+ }
+ }
+ }
+ else if (isSelma) {
+ // AutoLogin for selma
+ if (document.getElementById("field_user") != undefined) {
+ document.getElementById("field_user").value = tud.username;
+ document.getElementById("field_pass").value = tud.password;
+ if (credentialsAvailable) {
+ document.getElementById("logIn_btn").click();
+ GM_setValue("stats", GM_getValue('stats') + 1);
+ }
+ }
+ }
+ else if (isMoodle) {
+ // AutoLogin for Moodle
+ if (window.location.pathname == "/moodle2/login/index.php") {
+ // Check if we are on the login page
+ document.querySelector('[href="https://tud.uni-leipzig.de/moodle2/auth/shibboleth/index.php"]').click();
+ GM_setValue("stats", GM_getValue('stats') + 1);
+ } else {
+ //Go to the login page if we need to login
+ const loginBtn = document.querySelector('[href="https://tud.uni-leipzig.de/moodle2/login/index.php"]');
+ if (loginBtn) {
+ loginBtn.click();
+ GM_setValue("stats", GM_getValue('stats') + 1);
+ }
+ }
+ }
+ else if (isQisServer) {
+ //AutoLogin for QISServer
+ if (document.getElementsByClassName("loginuser").length >= 1) {
+ document.getElementsByClassName("loginuser")[0].value = tud.username;
+ document.getElementsByClassName("loginpass")[0].value = tud.password;
+ if (credentialsAvailable) {
+ document.getElementsByClassName("submit")[0].click();
+ GM_setValue("stats", GM_getValue('stats') + 1);
+ }
+ }
+ }
+ else if (isOWA) {
+ //AutoLogin for OWA
+ document.getElementById('username').value = tud.username;
+ document.getElementById('password').value = tud.password;
+ if (credentialsAvailable) {
+ document.getElementsByClassName("signinbutton")[0].click();
+ GM_setValue("stats", GM_getValue('stats') + 1);
+ }
+ }
+ else if (isLskOnline) {
+ //AutoLogin for LSKOnline
+ document.getElementsByName('j_username')[0].value = tud.username;
+ document.getElementsByName('j_password')[0].value = tud.password;
+ if (credentialsAvailable) {
+ document.getElementsByName('submit')[0].click();
+ GM_setValue("stats", GM_getValue('stats') + 1);
+ }
+ }
+ else if (isShareLatex) {
+ //AutoLogin for ShareLaTeX
+ if (window.location.pathname == "/saml/login") {
+ // Check if we are on the login page
+ document.querySelector('[href="/saml/login/go"]').click();
+ GM_setValue("stats", GM_getValue('stats') + 1);
+ } else {
+ //Go to the login page if we need to login
+ const loginBtn = document.querySelector('[href="/login"]');
+ if (loginBtn) {
+ loginBtn.click();
+ GM_setValue("stats", GM_getValue('stats') + 1);
+ }
+ }
+ }
+
+ function enterSecret(pinInput) {
+ document.getElementById("fudis_otp_input").focus();
+ var otp_input = getOTP(tud.secret);
+ sleep(200);
+ if (!GM_getValue("isSecretEntered")) {
+ document.getElementById("fudis_otp_input").value = otp_input;
+ GM_setValue("isSecretEntered", true); // this is for insert the code only once
+ document.getElementsByName("_eventId_proceed")[0].click();
+ GM_setValue("stats", GM_getValue('stats') + 1);
+ } else {
+ const clicked = showNotification(otp_input);
+ }
+ }
+
+ function showNotification(myCode) {
+ GM_notification({
+ text: "Your 2FA code was incorrect \nClick to show a new code, check it against the application or close this notification",
+ title: "TUD Autologin",
+ url: 'https:/google.com/',
+ silent: true,
+ timeout: 10000,
+ onclick: (event) => {
+ // The userscript is still running, so don't open example.com
+ event.preventDefault();
+ GM_notification({
+ title: "TUD Autologin",
+ text: "New Code is: " + myCode + "\nClick to enter the new code",
+ silent: true,
+ onclick: (event) => {
+ document.getElementById("fudis_otp_input").value = myCode;
+ }
+ });
+ }
+ });
+ }
+
+})();
+
+function sleep(ms) {
+ return new Promise(resolve => setTimeout(resolve, ms));
+}
+
+function darkThemeIDP() {
+ const background = document.querySelector('body');
+ const mainBox = document.querySelector('main');
+ const inputs = document.querySelectorAll('input');
+ background.style.backgroundColor = '#232323';
+ mainBox.style.backgroundColor = '#696969';
+ inputs.forEach(input => {
+ input.style.backgroundColor = '#696969';
+ input.style.color = '#fff';
+ });
+}
+
+// TOTP Code modified from https://github.com/tkooda/totp.info and fixed base32tohex function without check the correctness the secret set
+
+function dec2hex(s) { return (s < 15.5 ? '0' : '') + Math.round(s).toString(16); }
+function hex2dec(s) { return parseInt(s, 16); }
+
+function base32tohex(base32) {
+ var base32chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
+ var bits = "";
+ var hex = "";
+
+ for (var i = 0; i < base32.length; i++) {
+ var val = base32chars.indexOf(base32.charAt(i).toUpperCase());
+ bits += leftpad(val.toString(2), 5, '0');
+ }
+
+ for (var n = 0; n + 4 <= bits.length; n += 4) {
+ var chunk = bits.substr(n, 4);
+ hex = hex + parseInt(chunk, 2).toString(16);
+ }
+ return hex.length % 2 ? hex + "0" : hex;
+};
+
+
+function leftpad(str, len, pad) {
+ if (len + 1 >= str.length) {
+ str = Array(len + 1 - str.length).join(pad) + str;
+ }
+ return str;
+};
+
+function updateOtp(secret) {
+ var epoch = Math.round((new Date().getTime()) / 1000.0);
+ var time = leftpad(dec2hex(Math.floor(epoch / 30)), 16, '0');
+ var shaObj = new jsSHA("SHA-1", "HEX");
+ shaObj.setHMACKey(base32tohex(secret), "HEX");
+ shaObj.update(time);
+ var hmac = shaObj.getHMAC("HEX");
+
+ if (hmac == 'KEY MUST BE IN BYTE INCREMENTS') {
+
+ } else {
+ var offset = hex2dec(hmac.substring(hmac.length - 1));
+ }
+
+ var otp = (hex2dec(hmac.substr(offset * 2, 8)) & hex2dec('7fffffff')) + '';
+ otp = (otp).substr(otp.length - 6, 6);
+
+ return otp;
+};
+
+function getOTP(secret) {
+ if (secret) {
+ return updateOtp(secret);
+ } else {
+ return undefined;
+ }
+};