Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/src/packages/database/postgres/passport-store.ts
Views: 687
/*1* This file is part of CoCalc: Copyright © 2022 Sagemath, Inc.2* License: MS-RSL – see LICENSE.md for details3*/45import { Pool } from "pg";6import getPool from "../pool";78// this is a general key/value store with expiration.9// each key is prefixed with the passport strategy name.10// this is used by some passport strategies to share data regarding11// a request and a response, or other information. for example,12// if there are 3 hubs and one of them generates an ID that's expected to13// be returned by the SSO server, then the possibly different hub receiving the14// response can only know that ID, if it is somehow stored in this table.1516const SAVE_QUERY = `17INSERT INTO passport_store (key, value, expire)18VALUES ($1, $2, NOW() + make_interval(secs => $3))19ON CONFLICT (key)20DO UPDATE SET value = $2, expire = NOW() + make_interval(secs => $3);`;2122interface RowType {23value: string;24expire: Date;25}2627// ATTN: do not change the method names nilly-willy: https://github.com/node-saml/passport-saml#cache-provider28class PassportCache {29private name: string;30private cachedMS: number;31private pool: Pool;3233constructor(name: string, cachedMS: number) {34if (typeof name !== "string" || name.length === 0) {35throw new Error("name must be a non-empty string");36}37if (typeof cachedMS !== "number" || cachedMS < 0) {38throw new Error("cachedMS must be a positive number");39}40this.name = name;41this.cachedMS = cachedMS;42this.pool = getPool();43}4445private getKey(key): string {46return `${this.name}::${key}`;47}4849// saves the key with the optional value, returns the saved value50async saveAsync(key: string, value: string): Promise<void> {51const cacheSecs = Math.floor(this.cachedMS / 1000);52await this.pool.query(SAVE_QUERY, [this.getKey(key), value, cacheSecs]);53}5455// returns the value if found, null otherwise56async getAsync(key: string): Promise<string | null> {57const { rows } = await this.pool.query<RowType>(58`SELECT value, expire FROM passport_store WHERE key = $1`,59[this.getKey(key)]60);61if (rows.length === 0) {62return null;63}64const { value, expire } = rows[0];65if (expire < new Date()) {66return null;67} else {68return value;69}70}7172// removes the key from the cache, returns the73// key removed, null if no key is removed74async removeAsync(key: string) {75await this.pool.query(`DELETE FROM passport_store WHERE key = $1`, [76this.getKey(key),77]);78}79}8081const samlCaches: { [name: string]: PassportCache } = {};8283export function getPassportCache(84name: string,85cachedMS: number86): PassportCache {87if (!samlCaches[name]) {88samlCaches[name] = new PassportCache(name, cachedMS);89}90return samlCaches[name];91}9293export function getOauthCache(name: string) {94return getPassportCache(name, 1000 * 60 * 60);95}969798