@cocalc/database
Version:
CoCalc: code for working with our PostgreSQL database
204 lines • 7.4 kB
JavaScript
;
/*
* 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