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/load-sso-conf.ts
Views: 687
/*1* This file is part of CoCalc: Copyright © 2022 Sagemath, Inc.2* License: MS-RSL – see LICENSE.md for details3*/45import { Client } from "pg";6import getLogger from "@cocalc/backend/logger";7import { PostgreSQL } from "@cocalc/database/postgres/types";8import { lstat, readFile , realpath } from "fs/promises";910const L = getLogger("auth:sso:import-sso-configuration").debug;1112// The path to the file. In actual use, this is a K8S secret exported as a file to /secrets/sso/sso.json13// content of that file: "{ [strategy name]: {conf: {…}, info: {…}}, […] : { … } | null, … }"14// further details are describe in src/packages/server/auth/sso/types.ts15const SSO_JSON = process.env.COCALC_SSO_CONFIG;1617// This function imports the SSO configuration from a file into the database.18// If a key points to "null", the entry is deleted.19// This runs only once during startup, called by the hub's auth.ts.20export async function loadSSOConf(db: PostgreSQL): Promise<void> {21if (SSO_JSON == null) {22L("No SSO configuration file specified via $COCALC_SSO_CONFIG.");23return;24}2526// test if the path at SSO_JSON is a regular file and is readable27try {28// the file could be a symlink, we have to resolve it29const ssofn = await realpath(SSO_JSON)30const stats = await lstat(ssofn);31if (!stats.isFile()) {32L(`SSO configuration file ${SSO_JSON} is not a regular file`);33return;34}35} catch (err) {36L(`SSO configuration file ${SSO_JSON} does not exist or is not readable`);37return;38}39await load(db);40}4142async function load(db: PostgreSQL) {43if (SSO_JSON == null) {44throw new Error("SSO_JSON is not defined, should never happen");45}46// load the json data stored in the file SSO_JSON47L(`Loading SSO configuration from '${SSO_JSON}'`);4849const client = db._client();50if (client == null) {51L(`no database client available -- skipping SSO configuration`);52return;53}5455// throws upon JSON parsing errors56const data = JSON.parse(await readFile(SSO_JSON, "utf8"));5758try {59await client.query("BEGIN");60for (const strategy in data) {61const val = data[strategy];62if (val == null) {63await deleteSSO(client, strategy);64} else {65await upsertSSO(client, strategy, val);66}67}68await client.query("COMMIT");69} catch (err) {70L(`ROLLBACK -- err=${err}`);71await client.query("ROLLBACK");72}73}7475const deleteQuery = `76DELETE FROM passport_settings77WHERE strategy = $1`;7879async function deleteSSO(client: Client, strategy: string) {80L(`Deleting SSO configuration for ${strategy}`);81await client.query(deleteQuery, [strategy]);82}8384const upsertQuery = `85INSERT INTO passport_settings (strategy, conf, info)86VALUES ($1, $2, $3)87ON CONFLICT (strategy) DO UPDATE SET conf = $2, info = $3`;8889async function upsertSSO(90client: Client,91strategy: string,92val: { conf: object; info: object }93) {94const { conf, info } = val;95L(`Updating SSO configuration for ${strategy}:`, { conf, info });96await client.query(upsertQuery, [strategy, conf, info]);97}9899100