mirror of
https://github.com/gristlabs/grist-core.git
synced 2026-03-02 04:09:24 +00:00
(core) Refactoring google drive plugin
Summary: Finishing implementation for google drive plugin. - Refactoring plugin code to make it more robust and to follow grist ux - Changing the way server hosts untrusted user content, from different domain to different port Test Plan: Browser tests Reviewers: dsagal, paulfitz Reviewed By: paulfitz Differential Revision: https://phab.getgrist.com/D2881
This commit is contained in:
@@ -747,6 +747,27 @@ export class FlexServer implements GristServer {
|
||||
httpsServer: this.httpsServer,
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Add endpoint that servers a javascript file with various api keys that
|
||||
* are used by the client libraries.
|
||||
*/
|
||||
public addClientSecrets() {
|
||||
if (this._check('clientSecret')) { return; }
|
||||
this.app.get('/client-secret.js', expressWrap(async (req, res) => {
|
||||
const config = this.getGristConfig();
|
||||
// Currently we are exposing only Google keys.
|
||||
// Those keys are eventually visible by the client, but should be usable
|
||||
// only from Grist's domains.
|
||||
const secrets = {
|
||||
googleClientId : config.googleClientId,
|
||||
};
|
||||
res.set('Content-Type', 'application/javascript');
|
||||
res.status(200);
|
||||
res.send(`
|
||||
window.gristClientSecret = ${JSON.stringify(secrets)}
|
||||
`);
|
||||
}));
|
||||
}
|
||||
|
||||
public addLoginRoutes() {
|
||||
if (this._check('login', 'org', 'sessions')) { return; }
|
||||
@@ -1416,7 +1437,7 @@ export class FlexServer implements GristServer {
|
||||
* path.
|
||||
*/
|
||||
private _getOrgRedirectUrl(req: RequestWithLogin, subdomain: string, pathname: string = req.originalUrl): string {
|
||||
const {hostname, orgInPath} = getOrgUrlInfo(subdomain, req.hostname, {
|
||||
const {hostname, orgInPath} = getOrgUrlInfo(subdomain, req.get('host')!, {
|
||||
org: req.org,
|
||||
baseDomain: this._defaultBaseDomain,
|
||||
pluginUrl: this._pluginUrl,
|
||||
|
||||
@@ -64,9 +64,8 @@ export class Hosts {
|
||||
// Extract org info in a request. This applies to the low-level IncomingMessage type (rather
|
||||
// than express.Request that derives from it) to be usable with websocket requests too.
|
||||
public async getOrgInfo(req: IncomingMessage): Promise<RequestOrgInfo> {
|
||||
const host = req.headers.host || '';
|
||||
const hostname = host.split(':')[0]; // Strip out port (ignores IPv6 but is OK for us).
|
||||
const info = await this.getOrgInfoFromParts(hostname, req.url!);
|
||||
const host = req.headers.host!;
|
||||
const info = await this.getOrgInfoFromParts(host, req.url!);
|
||||
// "Organization" header is used in proxying to doc worker, so respect it if
|
||||
// no org info found in url.
|
||||
if (!info.org && req.headers.organization) {
|
||||
@@ -77,7 +76,9 @@ export class Hosts {
|
||||
|
||||
// Extract org, isCustomHost, and the URL with /o/ORG stripped away. Throws ApiError for
|
||||
// mismatching org or invalid custom domain. Hostname should not include port.
|
||||
public async getOrgInfoFromParts(hostname: string, urlPath: string): Promise<RequestOrgInfo> {
|
||||
public async getOrgInfoFromParts(host: string, urlPath: string): Promise<RequestOrgInfo> {
|
||||
const hostname = host.split(':')[0]; // Strip out port (ignores IPv6 but is OK for us).
|
||||
|
||||
// Extract the org from the host and URL path.
|
||||
const parts = extractOrgParts(hostname, urlPath);
|
||||
|
||||
@@ -87,7 +88,7 @@ export class Hosts {
|
||||
return {org: singleOrg, url: parts.pathRemainder, isCustomHost: false};
|
||||
}
|
||||
|
||||
const hostType = this._getHostType(hostname);
|
||||
const hostType = this._getHostType(host);
|
||||
if (hostType === 'native') {
|
||||
if (parts.mismatch) {
|
||||
throw new ApiError(`Wrong org for this domain: ` +
|
||||
@@ -147,14 +148,14 @@ export class Hosts {
|
||||
private async _redirectHost(req: Request, resp: Response, next: NextFunction) {
|
||||
const {org} = req as RequestWithOrg;
|
||||
|
||||
if (org && this._getHostType(req.hostname) === 'native' && !this._dbManager.isMergedOrg(org)) {
|
||||
if (org && this._getHostType(req.headers.host!) === 'native' && !this._dbManager.isMergedOrg(org)) {
|
||||
// Check if the org has a preferred host.
|
||||
const orgHost = await mapGetOrSet(this._org2host, org, async () => {
|
||||
const o = await this._dbManager.connection.manager.findOne(Organization, {domain: org});
|
||||
return o && o.host || undefined;
|
||||
});
|
||||
if (orgHost && orgHost !== req.hostname) {
|
||||
const url = new URL(`${req.protocol}://${req.get('host')}${req.path}`);
|
||||
const url = new URL(`${req.protocol}://${req.headers.host}${req.path}`);
|
||||
url.hostname = orgHost; // assigning hostname rather than host preserves port.
|
||||
return resp.redirect(url.href);
|
||||
}
|
||||
@@ -162,7 +163,7 @@ export class Hosts {
|
||||
return next();
|
||||
}
|
||||
|
||||
private _getHostType(hostname: string) {
|
||||
return getHostType(hostname, {baseDomain: this._baseDomain, pluginUrl: this._pluginUrl});
|
||||
private _getHostType(host: string) {
|
||||
return getHostType(host, {baseDomain: this._baseDomain, pluginUrl: this._pluginUrl});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ export function makeGristConfig(homeUrl: string|null, extra: Partial<GristLoadCo
|
||||
baseDomain?: string, req?: express.Request
|
||||
): GristLoadConfig {
|
||||
// .invalid is a TLD the IETF promises will never exist.
|
||||
const pluginUrl = process.env.APP_UNTRUSTED_URL || 'plugins.invalid';
|
||||
const pluginUrl = process.env.APP_UNTRUSTED_URL || 'http://plugins.invalid';
|
||||
const pathOnly = (process.env.GRIST_ORG_IN_PATH === "true") ||
|
||||
(homeUrl && new URL(homeUrl).hostname === 'localhost') || false;
|
||||
const mreq = req as RequestWithOrg|undefined;
|
||||
@@ -37,6 +37,7 @@ export function makeGristConfig(homeUrl: string|null, extra: Partial<GristLoadCo
|
||||
supportAnon: shouldSupportAnon(),
|
||||
pluginUrl,
|
||||
stripeAPIKey: process.env.STRIPE_PUBLIC_API_KEY,
|
||||
googleClientId: process.env.GOOGLE_CLIENT_ID,
|
||||
helpScoutBeaconId: process.env.HELP_SCOUT_BEACON_ID,
|
||||
maxUploadSizeImport: (Number(process.env.GRIST_MAX_UPLOAD_IMPORT_MB) * 1024 * 1024) || undefined,
|
||||
maxUploadSizeAttachment: (Number(process.env.GRIST_MAX_UPLOAD_ATTACHMENT_MB) * 1024 * 1024) || undefined,
|
||||
|
||||
Reference in New Issue
Block a user