diff options
Diffstat (limited to 'src/session.js')
-rw-r--r-- | src/session.js | 221 |
1 files changed, 72 insertions, 149 deletions
diff --git a/src/session.js b/src/session.js index a127ac2..aee49c2 100644 --- a/src/session.js +++ b/src/session.js @@ -1,112 +1,19 @@ +import { Builder, Browser, By, Key, until } from "selenium-webdriver"; +import { Cookie } from "tough-cookie"; + import { + AGGIETIME_AUTH_COOKIE_NAME, + AGGIETIME_DOMAIN, AGGIETIME_URI, + AGGIETIME_URL_CONTAINS_SIGNIFIES_AUTH_COMPLETE, LOGIN_PATH, - LOGOUT_PATH, - USER_PATH, - DUO_IFRAME_SELECTOR, - DUO_FACTOR, - DUO_INPUT_FIELD_SELECTORS, - EXECUTION_SELECTOR, + SAML_SIGN_IN_TITLE, + SAML_SUBMIT_SELECTOR, + SAML_EMAIL_SELECTOR, + SAML_PASSWORD_SELECTOR, } from "./constants.js"; +import { jar } from "./axios_client.js"; import * as aggietime from "./aggietime.js"; -import { client } from "./axios_client.js"; - -import { parse } from "node-html-parser"; - -const make_auth_params = (username, password, execution) => - new URLSearchParams({ - username, - password, - execution, - _eventId: "submit", - geolocation: "", - }); - -const make_duo_push_params = ( - sid, - out_of_date, - days_out_of_date, - days_to_block, - device -) => - new URLSearchParams({ - sid, - out_of_date, - days_out_of_date, - days_to_block, - device, - factor: DUO_FACTOR, - }); - -const push_duo_get_cookie = async ( - duo_iframe_obj, - response_url, - username, - password, - execution -) => { - const [duo_host, duo_sig, duo_src] = [ - "data-host", - "data-sig-request", - "src", - ].map((attr) => duo_iframe_obj.getAttribute(attr)); - - const duo = client.create({ - baseURL: `https://${duo_host}`, - }); - const transaction_id = duo_sig.split(":").at(0); - const app = duo_sig.split(":APP").at(-1); - - console.log("Retrieving DUO frame DOM for this transaction..."); - const duo_frame = await duo - .post( - `/frame/web/v1/auth?tx=${transaction_id}&parent=${response_url}&v=2.6` - ) - .then(({ data }) => parse(data)); - - const push_param_list = DUO_INPUT_FIELD_SELECTORS.map((selector) => - duo_frame.querySelector(selector).getAttribute("value") - ); - let [sid, _] = push_param_list; - - const { - response: { txid }, - } = await duo - .post("/frame/prompt", make_duo_push_params.apply(null, push_param_list)) - .then(({ data }) => data); - - console.log("Waiting for approval..."); - const { cookie, parent } = await wait_approve_duo_cookie_resp(duo, sid, txid); - return { duo_signed_resp: cookie + ":APP" + app, parent }; -}; - -const wait_approve_duo_cookie_resp = async (duo, sid, txid) => { - // First status to confirm device was pushed to, - // Second to create a long-poll connection-alive socket for approval status :3 - const status_params = new URLSearchParams({ - sid, - txid, - }); - const { - response: { result_url }, - } = await duo.post("/frame/status", status_params).then(async ({ data }) => { - if (data.stat === "OK" && data.response.status_code === "pushed") - return await duo - .post("/frame/status", status_params) - .then(({ data }) => data); - return data; - }); - - const { - response: { cookie, parent }, - } = await duo - .post(result_url, new URLSearchParams({ sid })) - .then(({ data }) => data); - - if (!cookie) throw "Unable to retrieve signed cookie from DUO"; - - return { cookie, parent }; -}; export const refresh_jwt = () => { console.log("Refreshing JWT..."); @@ -116,51 +23,67 @@ export const refresh_jwt = () => { export const logout = () => client.get(`${AGGIETIME_URI}/${LOGOUT_PATH}`); -export const login = async (username, password) => { - const login_page_promise = client.get(`${AGGIETIME_URI}/${LOGIN_PATH}`); - console.log("Retreiving login page..."); - - const { - request: { - res: { responseUrl: response_url }, - }, - } = await login_page_promise; - - let cas_root = await login_page_promise.then(({ data }) => parse(data)); - - console.log("Parsing DOM for spring execution token..."); - const login_execution = cas_root - .querySelector(EXECUTION_SELECTOR) - .getAttribute("value"); - - console.log("Sending CAS credentials..."); - cas_root = await client - .post(response_url, make_auth_params(username, password, login_execution)) - .then(({ data }) => parse(data)); - - console.log("Parsing DOM for authenticated spring execution token..."); - const authed_execution = cas_root - .querySelector(EXECUTION_SELECTOR) - .getAttribute("value"); - - const duo_iframe_obj = cas_root.querySelector(DUO_IFRAME_SELECTOR); - console.log("Starting DUO authentication..."); - const { duo_signed_resp, parent: signed_response_url } = - await push_duo_get_cookie( - duo_iframe_obj, - response_url, - username, - password, - login_execution +export const login = async (a_number, password) => { + const driver = await new Builder().forBrowser(Browser.CHROME).build(); + let cookie; + + try { + console.log("Navigating to login path..."); + await driver.get(`${AGGIETIME_URI}/${LOGIN_PATH}`); + + if (a_number && password) { + console.log("Waiting until we eventually redirect to SAML..."); + await driver.wait(until.titleIs(SAML_SIGN_IN_TITLE)); + + console.log("Waiting until email field is located..."); + await driver.wait(until.elementLocated(By.css(SAML_EMAIL_SELECTOR))); + + console.log("Filling email field..."); + await driver + .findElement(By.css(SAML_EMAIL_SELECTOR)) + .sendKeys(`${a_number}@usu.edu`); + await driver.findElement(By.css(SAML_SUBMIT_SELECTOR)).click(); + + console.log("Waiting until password field is located..."); + await Promise.all( + [SAML_PASSWORD_SELECTOR, SAML_SUBMIT_SELECTOR].map((selector) => + driver.wait(until.elementLocated(By.css(selector))) + ) + ); + + console.log("Filling password..."); + await driver + .findElement(By.css(SAML_PASSWORD_SELECTOR)) + .sendKeys(password); + + console.log("Debouncing a bit..."); + await new Promise((res) => setTimeout(res, 500)); + + console.log("Submit!"); + await driver.findElement(By.css(SAML_SUBMIT_SELECTOR)).click(); + } + + console.log( + "Waiting for aggietime response (potential DUO required here)..." + ); + await driver.wait( + until.urlContains(AGGIETIME_URL_CONTAINS_SIGNIFIES_AUTH_COMPLETE) + ); + + console.log("Retrieving cookie..."); + cookie = await driver.manage().getCookie(AGGIETIME_AUTH_COOKIE_NAME); + + await jar.setCookie( + new Cookie({ + ...cookie, + key: cookie.name, + }), + AGGIETIME_URI ); + console.log("Got it!"); + } finally { + await driver.quit(); + } - console.log("Sending DUO signed response back to CAS..."); - return await client.post( - signed_response_url, - new URLSearchParams({ - execution: authed_execution, - signedDuoResponse: duo_signed_resp, - _eventId: "submit", - }) - ); + return cookie; }; |