We do a little logging, but cringe OpenAPI errors be making me want to shoot myself. We have some shit working though.
This commit is contained in:
parent
30cbc219e6
commit
32803c4416
22
package-lock.json
generated
22
package-lock.json
generated
@ -5,9 +5,11 @@
|
|||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"argparse": "^2.0.1",
|
||||||
"axios": "^1.3.3",
|
"axios": "^1.3.3",
|
||||||
"axios-cookiejar-support": "^4.0.6",
|
"axios-cookiejar-support": "^4.0.6",
|
||||||
"dotenv": "^16.0.3",
|
"dotenv": "^16.0.3",
|
||||||
|
"expire-cache": "^1.0.0",
|
||||||
"node-html-parser": "^6.1.4",
|
"node-html-parser": "^6.1.4",
|
||||||
"tough-cookie": "^4.1.2"
|
"tough-cookie": "^4.1.2"
|
||||||
}
|
}
|
||||||
@ -23,6 +25,11 @@
|
|||||||
"node": ">= 6.0.0"
|
"node": ">= 6.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/argparse": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
|
||||||
|
},
|
||||||
"node_modules/asynckit": {
|
"node_modules/asynckit": {
|
||||||
"version": "0.4.0",
|
"version": "0.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
||||||
@ -192,6 +199,11 @@
|
|||||||
"url": "https://github.com/fb55/entities?sponsor=1"
|
"url": "https://github.com/fb55/entities?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/expire-cache": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/expire-cache/-/expire-cache-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-5qSqF7yhqxeZ/G0JFppVq+rQsh1Lx49jI9uaO7oye93eOXmsGQ0B9dmgCybKeGS2GHNyeg6O0kbeNiadWfH1WQ=="
|
||||||
|
},
|
||||||
"node_modules/follow-redirects": {
|
"node_modules/follow-redirects": {
|
||||||
"version": "1.15.2",
|
"version": "1.15.2",
|
||||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
|
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
|
||||||
@ -372,6 +384,11 @@
|
|||||||
"debug": "4"
|
"debug": "4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"argparse": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
|
||||||
|
},
|
||||||
"asynckit": {
|
"asynckit": {
|
||||||
"version": "0.4.0",
|
"version": "0.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
||||||
@ -481,6 +498,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/entities/-/entities-4.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/entities/-/entities-4.4.0.tgz",
|
||||||
"integrity": "sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA=="
|
"integrity": "sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA=="
|
||||||
},
|
},
|
||||||
|
"expire-cache": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/expire-cache/-/expire-cache-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-5qSqF7yhqxeZ/G0JFppVq+rQsh1Lx49jI9uaO7oye93eOXmsGQ0B9dmgCybKeGS2GHNyeg6O0kbeNiadWfH1WQ=="
|
||||||
|
},
|
||||||
"follow-redirects": {
|
"follow-redirects": {
|
||||||
"version": "1.15.2",
|
"version": "1.15.2",
|
||||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
|
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
{
|
{
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"argparse": "^2.0.1",
|
||||||
"axios": "^1.3.3",
|
"axios": "^1.3.3",
|
||||||
"axios-cookiejar-support": "^4.0.6",
|
"axios-cookiejar-support": "^4.0.6",
|
||||||
"dotenv": "^16.0.3",
|
"dotenv": "^16.0.3",
|
||||||
|
"expire-cache": "^1.0.0",
|
||||||
"node-html-parser": "^6.1.4",
|
"node-html-parser": "^6.1.4",
|
||||||
"tough-cookie": "^4.1.2"
|
"tough-cookie": "^4.1.2"
|
||||||
},
|
},
|
||||||
|
11
src/actions.js
Normal file
11
src/actions.js
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import * as aggietime from "./aggietime.js";
|
||||||
|
|
||||||
|
const ACTIONS = {
|
||||||
|
"clock-in": aggietime.clock_in,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const do_action = async (body) => {
|
||||||
|
const { action, rest } = body;
|
||||||
|
|
||||||
|
return await ACTIONS[action](rest);
|
||||||
|
};
|
62
src/aggietime.js
Normal file
62
src/aggietime.js
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
import {
|
||||||
|
AGGIETIME_URI,
|
||||||
|
AGGIETIME_DOMAIN,
|
||||||
|
USER_PATH,
|
||||||
|
USER_CACHE_EXP_SEC,
|
||||||
|
CLOCKIN_PATH,
|
||||||
|
} from "./constants.js";
|
||||||
|
|
||||||
|
import { client } from "./axios_client.js";
|
||||||
|
|
||||||
|
import expireCache from "expire-cache";
|
||||||
|
|
||||||
|
const replace_path_args = (path, map) =>
|
||||||
|
path.replaceAll(/:([a-zA-Z0-9_]+)/g, (_, key) => map[key]);
|
||||||
|
|
||||||
|
const get_user_position_or_specified = async (position) => {
|
||||||
|
const { positions } = await get_user_info();
|
||||||
|
|
||||||
|
if (!position && positions.length != 1) {
|
||||||
|
throw "Must specify a position when there's not only one to choose from";
|
||||||
|
} else if (!position) {
|
||||||
|
position = positions[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
return position;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const get_user_info = async () => {
|
||||||
|
if (!expireCache.get("user")) {
|
||||||
|
const user = await client
|
||||||
|
.get(`${AGGIETIME_URI}/${USER_PATH}`)
|
||||||
|
.then(({ data, config }) => {
|
||||||
|
const csrf_token = config.jar
|
||||||
|
.toJSON()
|
||||||
|
.cookies.find(
|
||||||
|
({ domain, key }) =>
|
||||||
|
domain === AGGIETIME_DOMAIN && key === "XSRF-TOKEN"
|
||||||
|
).value;
|
||||||
|
expireCache.set("aggietime-csrf", csrf_token);
|
||||||
|
return data;
|
||||||
|
});
|
||||||
|
|
||||||
|
expireCache.set("user", user, USER_CACHE_EXP_SEC);
|
||||||
|
}
|
||||||
|
return expireCache.get("user");
|
||||||
|
};
|
||||||
|
|
||||||
|
export const clock_in = async ({ position } = {}) => {
|
||||||
|
position = await get_user_position_or_specified(position);
|
||||||
|
|
||||||
|
return await client.post(
|
||||||
|
`${AGGIETIME_URI}/${replace_path_args(CLOCKIN_PATH, { position })}`,
|
||||||
|
{
|
||||||
|
comment: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
"X-XSRF-TOKEN": expireCache.get("aggietime-csrf"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
@ -1,6 +1,14 @@
|
|||||||
export const AGGIETIME_URI = "https://aggietimeultra.usu.edu";
|
export const DEFAULT_SOCKET_PATH = "/tmp/aggietimed.sock";
|
||||||
|
export const KILL_SIGNALS = ["SIGINT", "SIGTERM", "SIGQUIT"];
|
||||||
|
|
||||||
|
export const AGGIETIME_DOMAIN = "aggietimeultra.usu.edu";
|
||||||
|
export const AGGIETIME_URI = `https://${AGGIETIME_DOMAIN}`;
|
||||||
export const LOGIN_PATH = "api/v1/auth/login";
|
export const LOGIN_PATH = "api/v1/auth/login";
|
||||||
|
export const LOGOUT_PATH = "api/v1/auth/logout";
|
||||||
|
export const CLOCKIN_PATH = "api/v1/positions/:position/clock_in";
|
||||||
export const USER_PATH = "api/v1/auth/get_user_info";
|
export const USER_PATH = "api/v1/auth/get_user_info";
|
||||||
|
export const REFRESH_JWT_MS = 5 * 1000 * 60;
|
||||||
|
|
||||||
export const EXECUTION_SELECTOR = "input[type=hidden][name=execution]";
|
export const EXECUTION_SELECTOR = "input[type=hidden][name=execution]";
|
||||||
export const DUO_IFRAME_SELECTOR = "#duo_iframe";
|
export const DUO_IFRAME_SELECTOR = "#duo_iframe";
|
||||||
export const DUO_FACTOR = "Duo Push";
|
export const DUO_FACTOR = "Duo Push";
|
||||||
@ -11,3 +19,10 @@ export const DUO_INPUT_FIELD_SELECTORS = [
|
|||||||
"input[type=hidden][name=days_to_block]",
|
"input[type=hidden][name=days_to_block]",
|
||||||
"input[type=hidden][name=preferred_device]",
|
"input[type=hidden][name=preferred_device]",
|
||||||
];
|
];
|
||||||
|
|
||||||
|
export const USER_CACHE_EXP_SEC = 30;
|
||||||
|
|
||||||
|
export const MAX_DEFAULT_RETRY_AMOUNT = 3;
|
||||||
|
export const WAIT_MS = 2000;
|
||||||
|
export const RETRY_EXPONENT = 1.2;
|
||||||
|
export const RETRY_EXPONENTIAL_FACTOR = 1.1;
|
||||||
|
35
src/exponential_retry.js
Normal file
35
src/exponential_retry.js
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import {
|
||||||
|
MAX_DEFAULT_RETRY_AMOUNT,
|
||||||
|
WAIT_MS,
|
||||||
|
RETRY_EXPONENT,
|
||||||
|
RETRY_EXPONENTIAL_FACTOR,
|
||||||
|
} from "./constants.js";
|
||||||
|
|
||||||
|
const wait_for = (ms) => new Promise((rs) => setTimeout(rs, ms));
|
||||||
|
|
||||||
|
export const with_exponential_retry = async (
|
||||||
|
promise_fn,
|
||||||
|
validation_fn = (x) => Promise.resolve(!!x),
|
||||||
|
max_retries = MAX_DEFAULT_RETRY_AMOUNT,
|
||||||
|
retries = 0
|
||||||
|
) => {
|
||||||
|
try {
|
||||||
|
if (retries)
|
||||||
|
await wait_for(
|
||||||
|
WAIT_MS * Math.pow(RETRY_EXPONENT, RETRY_EXPONENTIAL_FACTOR * retries)
|
||||||
|
);
|
||||||
|
|
||||||
|
const res = await promise_fn();
|
||||||
|
if (await validation_fn(res)) return res;
|
||||||
|
|
||||||
|
throw new Error("Validation predicate not satisfied");
|
||||||
|
} catch (e) {
|
||||||
|
if (retries >= max_retries) throw e;
|
||||||
|
return with_exponential_retry(
|
||||||
|
promise_fn,
|
||||||
|
validation_fn,
|
||||||
|
max_retries,
|
||||||
|
retries + 1
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
94
src/main.js
94
src/main.js
@ -1,8 +1,94 @@
|
|||||||
import { login } from "./session.js";
|
import {
|
||||||
|
DEFAULT_SOCKET_PATH,
|
||||||
|
KILL_SIGNALS,
|
||||||
|
REFRESH_JWT_MS,
|
||||||
|
} from "./constants.js";
|
||||||
|
import * as actions from "./actions.js";
|
||||||
|
import * as session from "./session.js";
|
||||||
|
import * as argparse from "argparse";
|
||||||
|
import * as net from "net";
|
||||||
import * as dotenv from "dotenv";
|
import * as dotenv from "dotenv";
|
||||||
|
import * as fs from "fs";
|
||||||
|
|
||||||
|
const main = async () => {
|
||||||
dotenv.config();
|
dotenv.config();
|
||||||
|
const args = build_args();
|
||||||
|
|
||||||
(async () => {
|
if (args.daemon) {
|
||||||
await login(process.env.A_NUMBER, process.env.PASSWORD);
|
try {
|
||||||
})();
|
start_server(args.socket_path, session.logout);
|
||||||
|
} catch {
|
||||||
|
fs.unlinkSync(args.socket_path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const build_args = () => {
|
||||||
|
const parser = new argparse.ArgumentParser({ description: "AggieTime CLI" });
|
||||||
|
|
||||||
|
parser.add_argument("-d", "--daemon", {
|
||||||
|
help: "Start server as a process blocking daemon",
|
||||||
|
action: argparse.BooleanOptionalAction,
|
||||||
|
default: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
parser.add_argument("-s", "--socket_path", {
|
||||||
|
default: DEFAULT_SOCKET_PATH,
|
||||||
|
help: `Set server socket path, defaults to ${DEFAULT_SOCKET_PATH}`,
|
||||||
|
});
|
||||||
|
|
||||||
|
return parser.parse_args();
|
||||||
|
};
|
||||||
|
|
||||||
|
const kill_server = (server, socket_path) => {
|
||||||
|
server.close();
|
||||||
|
|
||||||
|
try {
|
||||||
|
fs.unlinkSync(socket_path);
|
||||||
|
} finally {
|
||||||
|
process.exit();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const start_server = async (socket_path, on_exit = () => {}) => {
|
||||||
|
if (fs.existsSync(socket_path)) {
|
||||||
|
console.error(
|
||||||
|
`ERR: Socket '${socket_path}' already exists.
|
||||||
|
If no server process is running, remove it (this should've been done automatically, except in the event of a catastrophic failure)
|
||||||
|
OR
|
||||||
|
specify another socket path with --socket_path`
|
||||||
|
);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
await session.login(process.env.A_NUMBER, process.env.PASSWORD);
|
||||||
|
session.refresh_jwt();
|
||||||
|
setInterval(session.refresh_jwt, REFRESH_JWT_MS);
|
||||||
|
|
||||||
|
const unix_server = net.createServer((client) => {
|
||||||
|
client.on("data", (data) => {
|
||||||
|
// 4096 byte limitation since we don't buffer here :3
|
||||||
|
let body;
|
||||||
|
try {
|
||||||
|
body = JSON.parse(data);
|
||||||
|
} catch {
|
||||||
|
console.error("Client provided invalid JSON data");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
actions.do_action(body);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
unix_server.on("close", () => kill_server(unix_server, socket_path));
|
||||||
|
|
||||||
|
console.log(`Server listening on socket ${socket_path}...`);
|
||||||
|
unix_server.listen(socket_path);
|
||||||
|
|
||||||
|
// Attempt to clean up socket before process gets killed
|
||||||
|
KILL_SIGNALS.forEach((signal) =>
|
||||||
|
process.on(signal, () => kill_server(unix_server, socket_path))
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
main();
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
import {
|
import {
|
||||||
AGGIETIME_URI,
|
AGGIETIME_URI,
|
||||||
LOGIN_PATH,
|
LOGIN_PATH,
|
||||||
|
LOGOUT_PATH,
|
||||||
USER_PATH,
|
USER_PATH,
|
||||||
DUO_IFRAME_SELECTOR,
|
DUO_IFRAME_SELECTOR,
|
||||||
DUO_FACTOR,
|
DUO_FACTOR,
|
||||||
DUO_INPUT_FIELD_SELECTORS,
|
DUO_INPUT_FIELD_SELECTORS,
|
||||||
EXECUTION_SELECTOR,
|
EXECUTION_SELECTOR,
|
||||||
} from "./constants.js";
|
} from "./constants.js";
|
||||||
|
import * as aggietime from "./aggietime.js";
|
||||||
import { client } from "./axios_client.js";
|
import { client } from "./axios_client.js";
|
||||||
|
|
||||||
import { parse } from "node-html-parser";
|
import { parse } from "node-html-parser";
|
||||||
//import axios from "axios";
|
|
||||||
|
|
||||||
const make_auth_params = (username, password, execution) =>
|
const make_auth_params = (username, password, execution) =>
|
||||||
new URLSearchParams({
|
new URLSearchParams({
|
||||||
@ -22,6 +22,22 @@ const make_auth_params = (username, password, execution) =>
|
|||||||
geolocation: "",
|
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 (
|
const push_duo_get_cookie = async (
|
||||||
duo_iframe_obj,
|
duo_iframe_obj,
|
||||||
response_url,
|
response_url,
|
||||||
@ -34,49 +50,43 @@ const push_duo_get_cookie = async (
|
|||||||
"data-sig-request",
|
"data-sig-request",
|
||||||
"src",
|
"src",
|
||||||
].map((attr) => duo_iframe_obj.getAttribute(attr));
|
].map((attr) => duo_iframe_obj.getAttribute(attr));
|
||||||
const transaction_id = duo_sig.split(":").at(0);
|
|
||||||
const app = duo_sig.split(":APP").at(-1);
|
|
||||||
|
|
||||||
const duo = client.create({
|
const duo = client.create({
|
||||||
baseURL: `https://${duo_host}`,
|
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
|
const duo_frame = await duo
|
||||||
.post(
|
.post(
|
||||||
`/frame/web/v1/auth?tx=${transaction_id}&parent=${response_url}&v=2.6`
|
`/frame/web/v1/auth?tx=${transaction_id}&parent=${response_url}&v=2.6`
|
||||||
)
|
)
|
||||||
.then(({ data }) => parse(data));
|
.then(({ data }) => parse(data));
|
||||||
|
|
||||||
const [sid, out_of_date, days_out_of_date, days_to_block, device] =
|
const push_param_list = DUO_INPUT_FIELD_SELECTORS.map((selector) =>
|
||||||
DUO_INPUT_FIELD_SELECTORS.map((selector) =>
|
|
||||||
duo_frame.querySelector(selector).getAttribute("value")
|
duo_frame.querySelector(selector).getAttribute("value")
|
||||||
);
|
);
|
||||||
|
let [sid, _] = push_param_list;
|
||||||
const push_params = new URLSearchParams({
|
|
||||||
sid,
|
|
||||||
out_of_date,
|
|
||||||
days_out_of_date,
|
|
||||||
days_to_block,
|
|
||||||
device,
|
|
||||||
factor: DUO_FACTOR,
|
|
||||||
});
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
response: { txid },
|
response: { txid },
|
||||||
} = await duo.post("/frame/prompt", push_params).then(({ data }) => data);
|
} = 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);
|
const { cookie, parent } = await wait_approve_duo_cookie_resp(duo, sid, txid);
|
||||||
return { duo_signed_resp: cookie + ":APP" + app, parent };
|
return { duo_signed_resp: cookie + ":APP" + app, parent };
|
||||||
};
|
};
|
||||||
|
|
||||||
const wait_approve_duo_cookie_resp = async (duo, sid, txid) => {
|
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({
|
const status_params = new URLSearchParams({
|
||||||
sid,
|
sid,
|
||||||
txid,
|
txid,
|
||||||
});
|
});
|
||||||
|
|
||||||
// First status to confirm device was pushed to
|
|
||||||
// Second to long-poll for approval :3
|
|
||||||
const {
|
const {
|
||||||
response: { result_url },
|
response: { result_url },
|
||||||
} = await duo.post("/frame/status", status_params).then(async ({ data }) => {
|
} = await duo.post("/frame/status", status_params).then(async ({ data }) => {
|
||||||
@ -93,33 +103,48 @@ const wait_approve_duo_cookie_resp = async (duo, sid, txid) => {
|
|||||||
.post(result_url, new URLSearchParams({ sid }))
|
.post(result_url, new URLSearchParams({ sid }))
|
||||||
.then(({ data }) => data);
|
.then(({ data }) => data);
|
||||||
|
|
||||||
|
if (!cookie) throw "Unable to retrieve signed cookie from DUO";
|
||||||
|
|
||||||
return { cookie, parent };
|
return { cookie, parent };
|
||||||
};
|
};
|
||||||
|
|
||||||
const get_execution = (cas_root) => {};
|
export const refresh_jwt = () => {
|
||||||
|
console.log("Refreshing JWT...");
|
||||||
|
|
||||||
|
return aggietime.get_user_info();
|
||||||
|
};
|
||||||
|
|
||||||
|
export const logout = () => client.get(`${AGGIETIME_URI}/${LOGOUT_PATH}`);
|
||||||
|
|
||||||
export const login = async (username, password) => {
|
export const login = async (username, password) => {
|
||||||
const login_page_promise = client.get(`${AGGIETIME_URI}/${LOGIN_PATH}`);
|
const login_page_promise = client.get(`${AGGIETIME_URI}/${LOGIN_PATH}`);
|
||||||
|
console.log("Retreiving login page...");
|
||||||
|
|
||||||
const {
|
const {
|
||||||
request: {
|
request: {
|
||||||
res: { responseUrl: response_url },
|
res: { responseUrl: response_url },
|
||||||
},
|
},
|
||||||
} = await login_page_promise;
|
} = await login_page_promise;
|
||||||
|
|
||||||
let cas_root = await login_page_promise.then(({ data }) => parse(data));
|
let cas_root = await login_page_promise.then(({ data }) => parse(data));
|
||||||
|
|
||||||
|
console.log("Parsing DOM for spring execution token...");
|
||||||
const login_execution = cas_root
|
const login_execution = cas_root
|
||||||
.querySelector(EXECUTION_SELECTOR)
|
.querySelector(EXECUTION_SELECTOR)
|
||||||
.getAttribute("value");
|
.getAttribute("value");
|
||||||
|
|
||||||
|
console.log("Sending CAS credentials...");
|
||||||
cas_root = await client
|
cas_root = await client
|
||||||
.post(response_url, make_auth_params(username, password, login_execution))
|
.post(response_url, make_auth_params(username, password, login_execution))
|
||||||
.then(({ data }) => parse(data));
|
.then(({ data }) => parse(data));
|
||||||
|
|
||||||
|
console.log("Parsing DOM for authenticated spring execution token...");
|
||||||
const authed_execution = cas_root
|
const authed_execution = cas_root
|
||||||
.querySelector(EXECUTION_SELECTOR)
|
.querySelector(EXECUTION_SELECTOR)
|
||||||
.getAttribute("value");
|
.getAttribute("value");
|
||||||
|
|
||||||
const duo_iframe_obj = cas_root.querySelector(DUO_IFRAME_SELECTOR);
|
const duo_iframe_obj = cas_root.querySelector(DUO_IFRAME_SELECTOR);
|
||||||
|
console.log("Starting DUO authentication...");
|
||||||
const { duo_signed_resp, parent: signed_response_url } =
|
const { duo_signed_resp, parent: signed_response_url } =
|
||||||
await push_duo_get_cookie(
|
await push_duo_get_cookie(
|
||||||
duo_iframe_obj,
|
duo_iframe_obj,
|
||||||
@ -129,7 +154,8 @@ export const login = async (username, password) => {
|
|||||||
login_execution
|
login_execution
|
||||||
);
|
);
|
||||||
|
|
||||||
const jwt_cookie_set = await client.post(
|
console.log("Sending DUO signed response back to CAS...");
|
||||||
|
return await client.post(
|
||||||
signed_response_url,
|
signed_response_url,
|
||||||
new URLSearchParams({
|
new URLSearchParams({
|
||||||
execution: authed_execution,
|
execution: authed_execution,
|
||||||
@ -137,6 +163,4 @@ export const login = async (username, password) => {
|
|||||||
_eventId: "submit",
|
_eventId: "submit",
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
return jwt_cookie_set;
|
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user