mirror of
https://github.com/gristlabs/grist-core.git
synced 2026-03-02 04:09:24 +00:00
Add authorization header in webhooks stored in secrets table (#941)
Summary: Adding authorization header support for webhooks. Issue: https://github.com/gristlabs/grist-core/issues/827 --------- Co-authored-by: Florent <florent.git@zeteo.me>
This commit is contained in:
@@ -324,7 +324,7 @@ export class DocWorkerApi {
|
||||
);
|
||||
|
||||
const registerWebhook = async (activeDoc: ActiveDoc, req: RequestWithLogin, webhook: WebhookFields) => {
|
||||
const {fields, url} = await getWebhookSettings(activeDoc, req, null, webhook);
|
||||
const {fields, url, authorization} = await getWebhookSettings(activeDoc, req, null, webhook);
|
||||
if (!fields.eventTypes?.length) {
|
||||
throw new ApiError(`eventTypes must be a non-empty array`, 400);
|
||||
}
|
||||
@@ -336,7 +336,7 @@ export class DocWorkerApi {
|
||||
}
|
||||
|
||||
const unsubscribeKey = uuidv4();
|
||||
const webhookSecret: WebHookSecret = {unsubscribeKey, url};
|
||||
const webhookSecret: WebHookSecret = {unsubscribeKey, url, authorization};
|
||||
const secretValue = JSON.stringify(webhookSecret);
|
||||
const webhookId = (await this._dbManager.addSecret(secretValue, activeDoc.docName)).id;
|
||||
|
||||
@@ -392,7 +392,7 @@ export class DocWorkerApi {
|
||||
const tablesTable = activeDoc.docData!.getMetaTable("_grist_Tables");
|
||||
const trigger = webhookId ? activeDoc.triggers.getWebhookTriggerRecord(webhookId) : undefined;
|
||||
let currentTableId = trigger ? tablesTable.getValue(trigger.tableRef, 'tableId')! : undefined;
|
||||
const {url, eventTypes, watchedColIds, isReadyColumn, name} = webhook;
|
||||
const {url, authorization, eventTypes, watchedColIds, isReadyColumn, name} = webhook;
|
||||
const tableId = await getRealTableId(req.params.tableId || webhook.tableId, {metaTables});
|
||||
|
||||
const fields: Partial<SchemaTypes['_grist_Triggers']> = {};
|
||||
@@ -454,6 +454,7 @@ export class DocWorkerApi {
|
||||
return {
|
||||
fields,
|
||||
url,
|
||||
authorization,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -926,16 +927,16 @@ export class DocWorkerApi {
|
||||
|
||||
const docId = activeDoc.docName;
|
||||
const webhookId = req.params.webhookId;
|
||||
const {fields, url} = await getWebhookSettings(activeDoc, req, webhookId, req.body);
|
||||
const {fields, url, authorization} = await getWebhookSettings(activeDoc, req, webhookId, req.body);
|
||||
if (fields.enabled === false) {
|
||||
await activeDoc.triggers.clearSingleWebhookQueue(webhookId);
|
||||
}
|
||||
|
||||
const triggerRowId = activeDoc.triggers.getWebhookTriggerRecord(webhookId).id;
|
||||
|
||||
// update url in homedb
|
||||
if (url) {
|
||||
await this._dbManager.updateWebhookUrl(webhookId, docId, url);
|
||||
// update url and authorization header in homedb
|
||||
if (url || authorization) {
|
||||
await this._dbManager.updateWebhookUrlAndAuth({id: webhookId, docId, url, auth: authorization});
|
||||
activeDoc.triggers.webhookDeleted(webhookId); // clear cache
|
||||
}
|
||||
|
||||
|
||||
@@ -72,6 +72,7 @@ type Trigger = MetaRowRecord<"_grist_Triggers">;
|
||||
export interface WebHookSecret {
|
||||
url: string;
|
||||
unsubscribeKey: string;
|
||||
authorization?: string;
|
||||
}
|
||||
|
||||
// Work to do after fetching values from the document
|
||||
@@ -259,6 +260,7 @@ export class DocTriggers {
|
||||
const getTableId = docData.getMetaTable("_grist_Tables").getRowPropFunc("tableId");
|
||||
const getColId = docData.getMetaTable("_grist_Tables_column").getRowPropFunc("colId");
|
||||
const getUrl = async (id: string) => (await this._getWebHook(id))?.url ?? '';
|
||||
const getAuthorization = async (id: string) => (await this._getWebHook(id))?.authorization ?? '';
|
||||
const getUnsubscribeKey = async (id: string) => (await this._getWebHook(id))?.unsubscribeKey ?? '';
|
||||
const resultTable: WebhookSummary[] = [];
|
||||
|
||||
@@ -271,6 +273,7 @@ export class DocTriggers {
|
||||
for (const act of webhookActions) {
|
||||
// Url, probably should be hidden for non-owners (but currently this API is owners only).
|
||||
const url = await getUrl(act.id);
|
||||
const authorization = await getAuthorization(act.id);
|
||||
// Same story, should be hidden.
|
||||
const unsubscribeKey = await getUnsubscribeKey(act.id);
|
||||
if (!url || !unsubscribeKey) {
|
||||
@@ -285,6 +288,7 @@ export class DocTriggers {
|
||||
fields: {
|
||||
// Url, probably should be hidden for non-owners (but currently this API is owners only).
|
||||
url,
|
||||
authorization,
|
||||
unsubscribeKey,
|
||||
// Other fields used to register this webhook.
|
||||
eventTypes: decodeObject(t.eventTypes) as string[],
|
||||
@@ -683,6 +687,7 @@ export class DocTriggers {
|
||||
const batch = _.takeWhile(this._webHookEventQueue.slice(0, 100), {id});
|
||||
const body = JSON.stringify(batch.map(e => e.payload));
|
||||
const url = await this._getWebHookUrl(id);
|
||||
const authorization = (await this._getWebHook(id))?.authorization || "";
|
||||
if (this._loopAbort.signal.aborted) {
|
||||
continue;
|
||||
}
|
||||
@@ -698,7 +703,8 @@ export class DocTriggers {
|
||||
this._activeDoc.logTelemetryEvent(null, 'sendingWebhooks', {
|
||||
limited: {numEvents: meta.numEvents},
|
||||
});
|
||||
success = await this._sendWebhookWithRetries(id, url, body, batch.length, this._loopAbort.signal);
|
||||
success = await this._sendWebhookWithRetries(
|
||||
id, url, authorization, body, batch.length, this._loopAbort.signal);
|
||||
if (this._loopAbort.signal.aborted) {
|
||||
continue;
|
||||
}
|
||||
@@ -770,7 +776,8 @@ export class DocTriggers {
|
||||
return this._drainingQueue ? Math.min(5, TRIGGER_MAX_ATTEMPTS) : TRIGGER_MAX_ATTEMPTS;
|
||||
}
|
||||
|
||||
private async _sendWebhookWithRetries(id: string, url: string, body: string, size: number, signal: AbortSignal) {
|
||||
private async _sendWebhookWithRetries(
|
||||
id: string, url: string, authorization: string, body: string, size: number, signal: AbortSignal) {
|
||||
const maxWait = 64;
|
||||
let wait = 1;
|
||||
for (let attempt = 0; attempt < this._maxWebhookAttempts; attempt++) {
|
||||
@@ -786,6 +793,7 @@ export class DocTriggers {
|
||||
body,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...(authorization ? {'Authorization': authorization} : {}),
|
||||
},
|
||||
signal,
|
||||
agent: proxyAgent(new URL(url)),
|
||||
|
||||
Reference in New Issue
Block a user