mirror of
https://github.com/gristlabs/grist-core.git
synced 2026-03-02 04:09:24 +00:00
(core) add a tool for deleting a user
Summary: This adds a `user:delete` target to the `cli.sh` tool. The desired user will be deleted from our database, from sendgrid, and from cognito. There is code for scrubbing the user from team sites, but it isn't yet activated, I'm leaving finalizing and writing tests for it for follow-up. Test Plan: tested manually Reviewers: dsagal Reviewed By: dsagal Differential Revision: https://phab.getgrist.com/D3043
This commit is contained in:
@@ -37,7 +37,7 @@ import {IBilling} from 'app/server/lib/IBilling';
|
||||
import {IDocStorageManager} from 'app/server/lib/IDocStorageManager';
|
||||
import {INotifier} from 'app/server/lib/INotifier';
|
||||
import * as log from 'app/server/lib/log';
|
||||
import {getLoginMiddleware} from 'app/server/lib/logins';
|
||||
import {getLoginSystem} from 'app/server/lib/logins';
|
||||
import {IPermitStore} from 'app/server/lib/Permit';
|
||||
import {getAppPathTo, getAppRoot, getUnpackedAppRoot} from 'app/server/lib/places';
|
||||
import {addPluginEndpoints, limitToPlugins} from 'app/server/lib/PluginEndpoint';
|
||||
@@ -750,7 +750,8 @@ export class FlexServer implements GristServer {
|
||||
|
||||
// TODO: We could include a third mock provider of login/logout URLs for better tests. Or we
|
||||
// could create a mock SAML identity provider for testing this using the SAML flow.
|
||||
this._loginMiddleware = await getLoginMiddleware(this);
|
||||
const loginSystem = await getLoginSystem();
|
||||
this._loginMiddleware = await loginSystem.getMiddleware(this);
|
||||
this._getLoginRedirectUrl = tbind(this._loginMiddleware.getLoginRedirectUrl, this._loginMiddleware);
|
||||
this._getSignUpRedirectUrl = tbind(this._loginMiddleware.getSignUpRedirectUrl, this._loginMiddleware);
|
||||
this._getLogoutRedirectUrl = tbind(this._loginMiddleware.getLogoutRedirectUrl, this._loginMiddleware);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { GristLoadConfig } from 'app/common/gristUrls';
|
||||
import { FullUser } from 'app/common/UserAPI';
|
||||
import { Document } from 'app/gen-server/entity/Document';
|
||||
import { Organization } from 'app/gen-server/entity/Organization';
|
||||
import { Workspace } from 'app/gen-server/entity/Workspace';
|
||||
@@ -33,6 +34,11 @@ export interface GristServer {
|
||||
getStorageManager(): IDocStorageManager;
|
||||
}
|
||||
|
||||
export interface GristLoginSystem {
|
||||
getMiddleware(gristServer: GristServer): Promise<GristLoginMiddleware>;
|
||||
deleteUser(user: FullUser): Promise<void>;
|
||||
}
|
||||
|
||||
export interface GristLoginMiddleware {
|
||||
getLoginRedirectUrl(req: express.Request, target: URL): Promise<string>;
|
||||
getSignUpRedirectUrl(req: express.Request, target: URL): Promise<string>;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
export interface INotifier {
|
||||
deleteUser(userId: number): Promise<void>;
|
||||
// for test purposes, check if any notifications are in progress
|
||||
readonly testPending: boolean;
|
||||
}
|
||||
|
||||
@@ -1,37 +1,44 @@
|
||||
import { UserProfile } from 'app/common/UserAPI';
|
||||
import { GristLoginMiddleware, GristServer } from 'app/server/lib/GristServer';
|
||||
import { GristLoginSystem, GristServer } from 'app/server/lib/GristServer';
|
||||
import { Request } from 'express';
|
||||
|
||||
/**
|
||||
* Return a login system that supports a single hard-coded user.
|
||||
*/
|
||||
export async function getMinimalLoginMiddleware(gristServer: GristServer): Promise<GristLoginMiddleware> {
|
||||
export async function getMinimalLoginSystem(): Promise<GristLoginSystem> {
|
||||
// Login and logout, redirecting immediately back. Signup is treated as login,
|
||||
// no nuance here.
|
||||
return {
|
||||
async getLoginRedirectUrl(req: Request, url: URL) {
|
||||
await setSingleUser(req, gristServer);
|
||||
return url.href;
|
||||
async getMiddleware(gristServer: GristServer) {
|
||||
return {
|
||||
async getLoginRedirectUrl(req: Request, url: URL) {
|
||||
await setSingleUser(req, gristServer);
|
||||
return url.href;
|
||||
},
|
||||
async getLogoutRedirectUrl(req: Request, url: URL) {
|
||||
return url.href;
|
||||
},
|
||||
async getSignUpRedirectUrl(req: Request, url: URL) {
|
||||
await setSingleUser(req, gristServer);
|
||||
return url.href;
|
||||
},
|
||||
async addEndpoints() {
|
||||
// If working without a login system, make sure default user exists.
|
||||
const dbManager = gristServer.getHomeDBManager();
|
||||
const profile = getDefaultProfile();
|
||||
const user = await dbManager.getUserByLoginWithRetry(profile.email, profile);
|
||||
if (user) {
|
||||
// No need to survey this user!
|
||||
user.isFirstTimeUser = false;
|
||||
await user.save();
|
||||
}
|
||||
return "no-logins";
|
||||
},
|
||||
};
|
||||
},
|
||||
async getLogoutRedirectUrl(req: Request, url: URL) {
|
||||
return url.href;
|
||||
async deleteUser() {
|
||||
// nothing to do
|
||||
},
|
||||
async getSignUpRedirectUrl(req: Request, url: URL) {
|
||||
await setSingleUser(req, gristServer);
|
||||
return url.href;
|
||||
},
|
||||
async addEndpoints() {
|
||||
// If working without a login system, make sure default user exists.
|
||||
const dbManager = gristServer.getHomeDBManager();
|
||||
const profile = getDefaultProfile();
|
||||
const user = await dbManager.getUserByLoginWithRetry(profile.email, profile);
|
||||
if (user) {
|
||||
// No need to survey this user!
|
||||
user.isFirstTimeUser = false;
|
||||
await user.save();
|
||||
}
|
||||
return "no-logins";
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@ import * as fse from 'fs-extra';
|
||||
import * as saml2 from 'saml2-js';
|
||||
|
||||
import {expressWrap} from 'app/server/lib/expressWrap';
|
||||
import {GristLoginMiddleware, GristServer} from 'app/server/lib/GristServer';
|
||||
import {GristLoginSystem, GristServer} from 'app/server/lib/GristServer';
|
||||
import * as log from 'app/server/lib/log';
|
||||
import {Permit} from 'app/server/lib/Permit';
|
||||
import {fromCallback} from 'app/server/lib/serverUtils';
|
||||
@@ -238,23 +238,30 @@ export class SamlConfig {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return SAML middleware if environment looks configured for it, else return undefined.
|
||||
* Return SAML login system if environment looks configured for it, else return undefined.
|
||||
*/
|
||||
export async function getSamlLoginMiddleware(gristServer: GristServer): Promise<GristLoginMiddleware|undefined> {
|
||||
export async function getSamlLoginSystem(): Promise<GristLoginSystem|undefined> {
|
||||
if (!process.env.GRIST_SAML_SP_HOST) {
|
||||
return undefined;
|
||||
}
|
||||
const samlConfig = new SamlConfig(gristServer);
|
||||
await samlConfig.initSaml();
|
||||
return {
|
||||
getLoginRedirectUrl: samlConfig.getLoginRedirectUrl.bind(samlConfig),
|
||||
// For saml, always use regular login page, users are enrolled externally.
|
||||
// TODO: is there a better link to give here?
|
||||
getSignUpRedirectUrl: samlConfig.getLoginRedirectUrl.bind(samlConfig),
|
||||
getLogoutRedirectUrl: samlConfig.getLogoutRedirectUrl.bind(samlConfig),
|
||||
async addEndpoints(app: express.Express) {
|
||||
samlConfig.addSamlEndpoints(app, gristServer.getSessions());
|
||||
return 'saml';
|
||||
}
|
||||
async getMiddleware(gristServer: GristServer) {
|
||||
const samlConfig = new SamlConfig(gristServer);
|
||||
await samlConfig.initSaml();
|
||||
return {
|
||||
getLoginRedirectUrl: samlConfig.getLoginRedirectUrl.bind(samlConfig),
|
||||
// For saml, always use regular login page, users are enrolled externally.
|
||||
// TODO: is there a better link to give here?
|
||||
getSignUpRedirectUrl: samlConfig.getLoginRedirectUrl.bind(samlConfig),
|
||||
getLogoutRedirectUrl: samlConfig.getLogoutRedirectUrl.bind(samlConfig),
|
||||
async addEndpoints(app: express.Express) {
|
||||
samlConfig.addSamlEndpoints(app, gristServer.getSessions());
|
||||
return 'saml';
|
||||
},
|
||||
};
|
||||
},
|
||||
deleteUser() {
|
||||
throw new Error('users cannot be deleted with SAML yet');
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user