(core) add an access token mechanism to help with attachments in custom widgets

Summary:
With this, a custom widget can render an attachment by doing:
```
const tokenInfo = await grist.docApi.getAccessToken({readOnly: true});
const img = document.getElementById('the_image');
const id = record.C[0];  // get an id of an attachment
const src = `${tokenInfo.baseUrl}/attachments/${id}/download?auth=${tokenInfo.token}`;
img.setAttribute('src', src)
```

The access token expires after a few mins, so if a user right-clicks on an image
to save it, they may get access denied unless they refresh the page. A little awkward,
but s3 pre-authorized links behave similarly and it generally isn't a deal-breaker.

Test Plan: added tests

Reviewers: dsagal

Reviewed By: dsagal

Subscribers: dsagal

Differential Revision: https://phab.getgrist.com/D3488
This commit is contained in:
Paul Fitzpatrick
2022-07-19 11:39:49 -04:00
parent 5c0a250309
commit dd8d2e18f5
22 changed files with 551 additions and 34 deletions

View File

@@ -4,6 +4,7 @@ import { Document } from 'app/gen-server/entity/Document';
import { Organization } from 'app/gen-server/entity/Organization';
import { Workspace } from 'app/gen-server/entity/Workspace';
import { HomeDBManager } from 'app/gen-server/lib/HomeDBManager';
import { IAccessTokens } from 'app/server/lib/AccessTokens';
import { RequestWithLogin } from 'app/server/lib/Authorizer';
import { Comm } from 'app/server/lib/Comm';
import { create } from 'app/server/lib/create';
@@ -31,7 +32,8 @@ export interface GristServer {
getOwnUrl(): string;
getOrgUrl(orgKey: string|number): Promise<string>;
getMergedOrgUrl(req: RequestWithLogin, pathname?: string): string;
getResourceUrl(resource: Organization|Workspace|Document): Promise<string>;
getResourceUrl(resource: Organization|Workspace|Document,
purpose?: 'api'|'html'): Promise<string>;
getGristConfig(): GristLoadConfig;
getPermitStore(): IPermitStore;
getExternalPermitStore(): IPermitStore;
@@ -44,6 +46,7 @@ export interface GristServer {
getDocTemplate(): Promise<DocTemplate>;
getTag(): string;
sendAppPage(req: express.Request, resp: express.Response, options: ISendAppPageOptions): Promise<void>;
getAccessTokens(): IAccessTokens;
}
export interface GristLoginSystem {
@@ -117,5 +120,6 @@ export function createDummyGristServer(): GristServer {
getDocTemplate() { throw new Error('no doc template'); },
getTag() { return 'tag'; },
sendAppPage() { return Promise.resolve(); },
getAccessTokens() { throw new Error('no access tokens'); },
};
}