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; + } +};