gristlabs_grist-core/app/server/lib/Sessions.ts

105 lines
3.8 KiB
TypeScript
Raw Normal View History

import {ScopedSession} from 'app/server/lib/BrowserSession';
import {cookieName, SessionStore} from 'app/server/lib/gristSessions';
import * as cookie from 'cookie';
import * as cookieParser from 'cookie-parser';
import {Request} from 'express';
/**
*
* A collection of all the sessions relevant to this instance of Grist.
*
* This collection was previously maintained by the Comm object. This
* class is added as a stepping stone to disentangling session management
* from code related to websockets.
*
* The collection caches all existing interfaces to sessions.
* ScopedSessions play an important role in
* hosted Grist and address per-organization scoping of identity.
*
* TODO: now this is separated out, we could refactor to share sessions
* across organizations. Currently, when a user moves between organizations,
* the session interfaces are not shared. This was for simplicity in working
* with existing code.
*
*/
export class Sessions {
private _sessions = new Map<string, ScopedSession>();
constructor(private _sessionSecret: string, private _sessionStore: SessionStore) {
}
/**
* Get the session id and organization from the request (or just pass it in if known), and
* return the identified session.
*/
(core) move more tests to grist-core Summary: * Tie build and run-time docker base images to a consistent version (buster) * Extend the test login system activated by GRIST_TEST_LOGIN to ease porting tests that currently rely on cognito (many) * Make org resets work in absence of billing endpoints * When in-memory session caches are used, add missing invalidation steps * Pass org information through sign-ups/sign-ins more carefully * For CORS, explicitly trust GRIST_HOST origin when set * Move some fixtures and tests to core, focussing on tests that cover existing failures or are in the set of tests run on deployments * Retain regular `test` target to run the test suite directly, without docker * Add a `test:smoke` target to run a single simple test without `GRIST_TEST_LOGIN` activated * Add a `test:docker` target to run the tests against a grist-core docker image - since tests rely on certain fixture teams/docs, added `TEST_SUPPORT_API_KEY` and `TEST_ADD_SAMPLES` flags to ease porting The tests ported were `nbrowser` tests: `ActionLog.ts` (the first test I tend to port to anything, out of habit), `Fork.ts` (exercises a lot of doc creation paths), `HomeIntro.ts` (a lot of DocMenu exercise), and `DuplicateDocument.ts` (covers a feature known to be failing prior to this diff, the CORS tweak resolves it). Test Plan: Manually tested via `buildtools/build_core.sh`. In follow up, I want to add running the `test:docker` target in grist-core's workflows. In jenkins, only the smoke test is run. There'd be an argument for running all tests, but they include particularly slow tests, and are duplicates of tests already run (in different configuration admittedly), so I'd like to try first just using them in grist-core to gate updates to any packaged version of Grist (the docker image currently). Reviewers: alexmojaki Reviewed By: alexmojaki Subscribers: alexmojaki Differential Revision: https://phab.getgrist.com/D3176
2021-12-10 22:42:54 +00:00
public getOrCreateSessionFromRequest(req: Request, options?: {
sessionId?: string,
org?: string
}): ScopedSession {
const sid = options?.sessionId ?? this.getSessionIdFromRequest(req);
const org = options?.org ?? (req as any).org;
if (!sid) { throw new Error("session not found"); }
return this.getOrCreateSession(sid, org, ''); // TODO: allow for tying to a preferred user.
}
/**
* Get or create a session given the session id and organization name.
*/
public getOrCreateSession(sid: string, domain: string, userSelector: string): ScopedSession {
const key = this._getSessionOrgKey(sid, domain, userSelector);
if (!this._sessions.has(key)) {
const scopedSession = new ScopedSession(sid, this._sessionStore, domain, userSelector);
this._sessions.set(key, scopedSession);
}
return this._sessions.get(key)!;
}
(core) move more tests to grist-core Summary: * Tie build and run-time docker base images to a consistent version (buster) * Extend the test login system activated by GRIST_TEST_LOGIN to ease porting tests that currently rely on cognito (many) * Make org resets work in absence of billing endpoints * When in-memory session caches are used, add missing invalidation steps * Pass org information through sign-ups/sign-ins more carefully * For CORS, explicitly trust GRIST_HOST origin when set * Move some fixtures and tests to core, focussing on tests that cover existing failures or are in the set of tests run on deployments * Retain regular `test` target to run the test suite directly, without docker * Add a `test:smoke` target to run a single simple test without `GRIST_TEST_LOGIN` activated * Add a `test:docker` target to run the tests against a grist-core docker image - since tests rely on certain fixture teams/docs, added `TEST_SUPPORT_API_KEY` and `TEST_ADD_SAMPLES` flags to ease porting The tests ported were `nbrowser` tests: `ActionLog.ts` (the first test I tend to port to anything, out of habit), `Fork.ts` (exercises a lot of doc creation paths), `HomeIntro.ts` (a lot of DocMenu exercise), and `DuplicateDocument.ts` (covers a feature known to be failing prior to this diff, the CORS tweak resolves it). Test Plan: Manually tested via `buildtools/build_core.sh`. In follow up, I want to add running the `test:docker` target in grist-core's workflows. In jenkins, only the smoke test is run. There'd be an argument for running all tests, but they include particularly slow tests, and are duplicates of tests already run (in different configuration admittedly), so I'd like to try first just using them in grist-core to gate updates to any packaged version of Grist (the docker image currently). Reviewers: alexmojaki Reviewed By: alexmojaki Subscribers: alexmojaki Differential Revision: https://phab.getgrist.com/D3176
2021-12-10 22:42:54 +00:00
/**
* Called when a session is modified, and any caching should be invalidated.
* Currently just removes all caching, if there is any. This caching is a bit
* of a weird corner of Grist, it is used in development for historic reasons
* but not in production.
* TODO: make more fine grained, or rethink.
*/
public clearCacheIfNeeded(options?: {
email?: string,
org?: string|null,
sessionID?: string,
}) {
if (!(process.env.GRIST_HOST || process.env.GRIST_HOSTED)) {
this._sessions.clear();
}
}
/**
* Returns the sessionId from the signed grist cookie.
*/
public getSessionIdFromCookie(gristCookie: string) {
return cookieParser.signedCookie(gristCookie, this._sessionSecret);
}
/**
* Get the session id from the grist cookie. Returns null if no cookie found.
*/
public getSessionIdFromRequest(req: Request): string|null {
if (req.headers.cookie) {
const cookies = cookie.parse(req.headers.cookie);
const sessionId = this.getSessionIdFromCookie(cookies[cookieName]);
if (sessionId) { return sessionId; }
}
return (req as any).sessionID || null; // sessionID set by express-session
}
/**
* Get a per-organization, per-session key.
* Grist has historically cached sessions in memory by their session id.
* With the introduction of per-organization identity, that cache is now
* needs to be keyed by the session id and organization name.
* Also, clients may now want to be tied to a particular user available within
* a session, so we add that into key too.
*/
private _getSessionOrgKey(sid: string, domain: string, userSelector: string): string {
return `${sid}__${domain}__${userSelector}`;
}
}