UNPKG

@cocalc/database

Version:

CoCalc: code for working with our PostgreSQL database

204 lines 7.4 kB
"use strict"; /* * This file is part of CoCalc: Copyright © 2022 Sagemath, Inc. * License: AGPLv3 s.t. "Commons Clause" – see LICENSE.md for details */ Object.defineProperty(exports, "__esModule", { value: true }); exports.update_account_and_passport = exports.passport_exists = exports.delete_passport = exports.create_passport = exports._passport_key = exports.get_all_passport_settings_cached = exports.get_all_passport_settings = exports.get_passport_settings = exports.set_passport_settings = void 0; // DEVELOPMENT: use scripts/auth/gen-sso.py to generate some test data const server_settings_1 = require("@cocalc/server/settings/server-settings"); const misc_1 = require("@cocalc/util/misc"); const account_queries_1 = require("./account-queries"); const unlink_strategy_1 = require("@cocalc/server/auth/sso/unlink-strategy"); async function set_passport_settings(db, opts) { const { strategy, conf, info } = opts; let err = null; try { await db.async_query({ query: "INSERT INTO passport_settings", values: { "strategy::TEXT ": strategy, "conf ::JSONB": conf, "info ::JSONB": info, }, conflict: "strategy", }); } catch (err) { err = err; } if (typeof opts.cb === "function") { opts.cb(err); } } exports.set_passport_settings = set_passport_settings; async function get_passport_settings(db, opts) { const { rows } = await db.async_query({ query: "SELECT conf, info FROM passport_settings", where: { "strategy = $::TEXT": opts.strategy }, }); if (typeof opts.cb === "function") { opts.cb(rows[0]); } return rows[0]; } exports.get_passport_settings = get_passport_settings; async function get_all_passport_settings(db) { return (await db.async_query({ query: "SELECT strategy, conf, info FROM passport_settings", })).rows; } exports.get_all_passport_settings = get_all_passport_settings; async function get_all_passport_settings_cached(db) { const passports = (0, server_settings_1.getPassportsCached)(); if (passports != null) { return passports; } const res = await get_all_passport_settings(db); (0, server_settings_1.setPassportsCached)(res); return res; } exports.get_all_passport_settings_cached = get_all_passport_settings_cached; // Passports -- accounts linked to Google/Dropbox/Facebook/Github, etc. // The Schema is slightly redundant, but indexed properly: // {passports:['google-id', 'facebook-id'], passport_profiles:{'google-id':'...', 'facebook-id':'...'}} function _passport_key(opts) { const { strategy, id } = opts; // note: strategy is *our* name of the strategy in the DB, not it's type string! if (typeof strategy !== "string") { throw new Error("_passport_key: strategy must be defined"); } if (typeof id !== "string") { throw new Error("_passport_key: id must be defined"); } return `${strategy}-${id}`; } exports._passport_key = _passport_key; async function create_passport(db, opts) { const dbg = db._dbg("create_passport"); dbg({ id: opts.id, strategy: opts.strategy, profile: (0, misc_1.to_json)(opts.profile) }); try { dbg("setting the passport for the account"); await db.async_query({ query: "UPDATE accounts", jsonb_set: { passports: { [_passport_key(opts)]: opts.profile }, }, where: { "account_id = $::UUID": opts.account_id, }, }); dbg(`setting other account info ${opts.account_id}: ${opts.email_address}, ${opts.first_name}, ${opts.last_name}`); await (0, account_queries_1.set_account_info_if_not_set)({ db: db, account_id: opts.account_id, email_address: opts.email_address, first_name: opts.first_name, last_name: opts.last_name, }); // we still record that email address as being verified if (opts.email_address != null) { await (0, account_queries_1.set_email_address_verified)({ db, account_id: opts.account_id, email_address: opts.email_address, }); } opts.cb?.(undefined); // all good } catch (err) { if (opts.cb != null) { opts.cb(err); } else { throw err; } } } exports.create_passport = create_passport; async function delete_passport(db, opts) { db._dbg("delete_passport")((0, misc_1.to_json)({ strategy: opts.strategy, id: opts.id })); if (await (0, unlink_strategy_1.isBlockedUnlinkStrategy)({ strategyName: opts.strategy, account_id: opts.account_id, })) { const err_msg = `You are not allowed to unlink '${opts.strategy}'`; if (typeof opts.cb === "function") { opts.cb(err_msg); return; } else { throw new Error(err_msg); } } return db._query({ query: "UPDATE accounts", jsonb_set: { // delete it passports: { [_passport_key(opts)]: null }, }, where: { "account_id = $::UUID": opts.account_id, }, cb: opts.cb, }); } exports.delete_passport = delete_passport; async function passport_exists(db, opts) { try { const result = await db.async_query({ query: "SELECT account_id FROM accounts", where: [ // this uses the corresponding index to only scan a subset of all accounts! "passports IS NOT NULL", { "(passports->>$::TEXT) IS NOT NULL": _passport_key(opts) }, ], }); const account_id = result?.rows[0]?.account_id; if (opts.cb != null) { opts.cb(null, account_id); } else { return account_id; } } catch (err) { if (opts.cb != null) { opts.cb(err); } else { throw err; } } } exports.passport_exists = passport_exists; async function update_account_and_passport(db, opts) { // we deliberately do not update the email address, because if the SSO // strategy sends a different one, this would break the "link". // rather, if the email (and hence most likely the email address) changes on the // SSO side, this would equal to creating a new account. const dbg = db._dbg("update_account_and_passport"); dbg(`updating account info ${(0, misc_1.to_json)({ first_name: opts.first_name, last_name: opts.last_name, })}`); await (0, account_queries_1.set_account_info_if_different)({ db: db, account_id: opts.account_id, first_name: opts.first_name, last_name: opts.last_name, }); const key = _passport_key(opts); dbg(`updating passport ${(0, misc_1.to_json)({ key, profile: opts.profile })}`); await db.async_query({ query: "UPDATE accounts", jsonb_set: { passports: { [key]: opts.profile }, }, where: { "account_id = $::UUID": opts.account_id, }, }); } exports.update_account_and_passport = update_account_and_passport; //# sourceMappingURL=passport.js.map