mirror of
https://github.com/matrix-org/matrix-hookshot.git
synced 2025-03-10 21:19:13 +00:00
Support updating some configs
This commit is contained in:
parent
431a96f693
commit
a3c7a29267
@ -83,7 +83,7 @@ export class ConnectionManager {
|
||||
throw Error('GitHub is not configured');
|
||||
}
|
||||
const res = await GitHubRepoConnection.provisionConnection(roomId, userId, data, this.as, this.tokenStore, this.github, this.config.github);
|
||||
await this.as.botIntent.underlyingClient.sendStateEvent(roomId, JiraProjectConnection.CanonicalEventType, res.connection.stateKey, res.stateEventContent);
|
||||
await this.as.botIntent.underlyingClient.sendStateEvent(roomId, GitHubRepoConnection.CanonicalEventType, res.connection.stateKey, res.stateEventContent);
|
||||
this.push(res.connection);
|
||||
return res.connection;
|
||||
}
|
||||
@ -92,7 +92,7 @@ export class ConnectionManager {
|
||||
throw Error('Generic hook support not supported');
|
||||
}
|
||||
const res = await GenericHookConnection.provisionConnection(roomId, this.as, data, this.config.generic, this.messageClient);
|
||||
await this.as.botIntent.underlyingClient.sendStateEvent(roomId, JiraProjectConnection.CanonicalEventType, res.connection.stateKey, res.stateEventContent);
|
||||
await this.as.botIntent.underlyingClient.sendStateEvent(roomId, GenericHookConnection.CanonicalEventType, res.connection.stateKey, res.stateEventContent);
|
||||
this.push(res.connection);
|
||||
return res.connection;
|
||||
}
|
||||
|
@ -13,11 +13,11 @@ export interface GenericHookConnectionState {
|
||||
/**
|
||||
* This is ONLY used for display purposes, but the account data value is used to prevent misuse.
|
||||
*/
|
||||
hookId: string;
|
||||
hookId?: string;
|
||||
/**
|
||||
* The name given in the provisioning UI and displaynames.
|
||||
*/
|
||||
name?: string;
|
||||
name: string;
|
||||
transformationFunction?: string;
|
||||
}
|
||||
|
||||
@ -39,29 +39,38 @@ const TRANSFORMATION_TIMEOUT_MS = 2000;
|
||||
*/
|
||||
export class GenericHookConnection extends BaseConnection implements IConnection {
|
||||
|
||||
static validateState(state: Record<string, unknown>, allowJsTransformationFunctions: boolean): GenericHookConnectionState {
|
||||
const {name, transformationFunction} = state;
|
||||
let transformationFunctionResult: string|undefined;
|
||||
if (transformationFunction) {
|
||||
if (!allowJsTransformationFunctions) {
|
||||
throw new ApiError('Transformation functions are not allowed', ErrCode.DisabledFeature);
|
||||
}
|
||||
if (typeof transformationFunction !== "string") {
|
||||
throw new ApiError('Transformation functions must be a string', ErrCode.BadValue);
|
||||
}
|
||||
transformationFunctionResult = transformationFunction;
|
||||
}
|
||||
if (!name) {
|
||||
throw new ApiError('Missing name', ErrCode.BadValue);
|
||||
}
|
||||
if (typeof name !== "string" || name.length < 3 || name.length > 64) {
|
||||
throw new ApiError("'name' must be a string between 3-64 characters long", ErrCode.BadValue);
|
||||
}
|
||||
return {
|
||||
name,
|
||||
...(transformationFunctionResult && {transformationFunction: transformationFunctionResult}),
|
||||
};
|
||||
}
|
||||
|
||||
static async provisionConnection(roomId: string, as: Appservice, data: Record<string, unknown> = {}, config: BridgeGenericWebhooksConfig, messageClient: MessageSenderClient) {
|
||||
const hookId = uuid();
|
||||
const validState: GenericHookConnectionState = {
|
||||
...GenericHookConnection.validateState(data, config.allowJsTransformationFunctions || false),
|
||||
hookId,
|
||||
};
|
||||
if (data.transformationFunction) {
|
||||
if (!config.allowJsTransformationFunctions) {
|
||||
throw new ApiError('Transformation functions are not allowed', ErrCode.DisabledFeature);
|
||||
}
|
||||
if (typeof data.transformationFunction !== "string") {
|
||||
throw new ApiError('Transformation functions must be a string', ErrCode.BadValue);
|
||||
}
|
||||
validState.transformationFunction = data.transformationFunction;
|
||||
}
|
||||
if (!data.name) {
|
||||
throw new ApiError('Missing name', ErrCode.BadValue);
|
||||
}
|
||||
if (typeof data.name !== "string" || data.name.length < 3 || data.name.length > 64) {
|
||||
throw new ApiError("'name' must be a string between 3-64 characters long", ErrCode.BadValue);
|
||||
}
|
||||
validState.name = data.name;
|
||||
const connection = new GenericHookConnection(roomId, validState, hookId, data.name, messageClient, config, as);
|
||||
await GenericHookConnection.ensureRoomAccountData(roomId, as, hookId, data.name);
|
||||
const connection = new GenericHookConnection(roomId, validState, hookId, validState.name, messageClient, config, as);
|
||||
await GenericHookConnection.ensureRoomAccountData(roomId, as, hookId, validState.name);
|
||||
return {
|
||||
connection,
|
||||
stateEventContent: validState,
|
||||
@ -98,7 +107,7 @@ export class GenericHookConnection extends BaseConnection implements IConnection
|
||||
private cachedDisplayname?: string;
|
||||
|
||||
constructor(roomId: string,
|
||||
private readonly state: GenericHookConnectionState,
|
||||
private state: GenericHookConnectionState,
|
||||
public readonly hookId: string,
|
||||
stateKey: string,
|
||||
private readonly messageClient: MessageSenderClient,
|
||||
@ -149,15 +158,15 @@ export class GenericHookConnection extends BaseConnection implements IConnection
|
||||
}
|
||||
|
||||
public async onStateUpdate(stateEv: MatrixEvent<unknown>) {
|
||||
const state = stateEv.content as GenericHookConnectionState;
|
||||
if (state.transformationFunction && this.config.allowJsTransformationFunctions) {
|
||||
const validatedConfig = GenericHookConnection.validateState(stateEv.content as Record<string, unknown>, this.config.allowJsTransformationFunctions || false);
|
||||
if (validatedConfig.transformationFunction) {
|
||||
try {
|
||||
this.transformationFunction = new Script(state.transformationFunction);
|
||||
this.transformationFunction = new Script(validatedConfig.transformationFunction);
|
||||
} catch (ex) {
|
||||
await this.messageClient.sendMatrixText(this.roomId, 'Could not compile transformation function:' + ex);
|
||||
}
|
||||
}
|
||||
this.state.name = state.name;
|
||||
this.state = validatedConfig;
|
||||
}
|
||||
|
||||
public transformHookData(data: Record<string, unknown>): string {
|
||||
@ -250,6 +259,16 @@ export class GenericHookConnection extends BaseConnection implements IConnection
|
||||
await GenericHookConnection.ensureRoomAccountData(this.roomId, this.as, this.hookId, this.stateKey, true);
|
||||
}
|
||||
|
||||
public async provisionerUpdateConfig(userId: string, config: Record<string, unknown>) {
|
||||
const validatedConfig = GenericHookConnection.validateState(config, this.config.allowJsTransformationFunctions || false);
|
||||
await this.as.botClient.sendStateEvent(this.roomId, GenericHookConnection.CanonicalEventType, this.stateKey,
|
||||
{
|
||||
...validatedConfig,
|
||||
hookId: this.hookId
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public toString() {
|
||||
return `GenericHookConnection ${this.hookId}`;
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ import markdownit from "markdown-it";
|
||||
import { generateJiraWebLinkFromIssue } from "../Jira";
|
||||
import { JiraProject } from "../Jira/Types";
|
||||
import { botCommand, BotCommands, compileBotCommands } from "../BotCommands";
|
||||
import { MatrixMessageContent } from "../MatrixEvent";
|
||||
import { MatrixEvent, MatrixMessageContent } from "../MatrixEvent";
|
||||
import { CommandConnection } from "./CommandConnection";
|
||||
import { UserTokenStore } from "../UserTokenStore";
|
||||
import { CommandError, NotLoggedInError } from "../errors";
|
||||
@ -102,6 +102,10 @@ export class JiraProjectConnection extends CommandConnection implements IConnect
|
||||
return parts ? parts[parts.length - 1] : undefined;
|
||||
}
|
||||
|
||||
public toString() {
|
||||
return `JiraProjectConnection ${this.projectId || this.projectUrl}`;
|
||||
}
|
||||
|
||||
public isInterestedInHookEvent(eventName: string) {
|
||||
return !this.state.events || this.state.events?.includes(eventName as JiraAllowedEventsNames);
|
||||
}
|
||||
@ -153,6 +157,11 @@ export class JiraProjectConnection extends CommandConnection implements IConnect
|
||||
return JiraProjectConnection.EventTypes.includes(eventType) && this.stateKey === stateKey;
|
||||
}
|
||||
|
||||
public async onStateUpdate(event: MatrixEvent<unknown>) {
|
||||
const validatedConfig = validateJiraConnectionState(event.content as JiraProjectConnectionState);
|
||||
this.state = validatedConfig;
|
||||
}
|
||||
|
||||
public async onJiraIssueCreated(data: JiraIssueEvent) {
|
||||
log.info(`onIssueCreated ${this.roomId} ${this.projectId} ${data.issue.id}`);
|
||||
|
||||
@ -337,10 +346,6 @@ export class JiraProjectConnection extends CommandConnection implements IConnect
|
||||
await api.updateAssigneeWithId(issueKey, searchForUser[0].accountId);
|
||||
}
|
||||
|
||||
public toString() {
|
||||
return `JiraProjectConnection ${this.projectId || this.projectUrl}`;
|
||||
}
|
||||
|
||||
public async onRemove() {
|
||||
log.info(`Removing ${this.toString()} for ${this.roomId}`);
|
||||
// Do a sanity check that the event exists.
|
||||
@ -352,6 +357,11 @@ export class JiraProjectConnection extends CommandConnection implements IConnect
|
||||
await this.as.botClient.sendStateEvent(this.roomId, JiraProjectConnection.LegacyCanonicalEventType, this.stateKey, { disabled: true });
|
||||
}
|
||||
}
|
||||
|
||||
public async provisionerUpdateConfig(userId: string, config: Record<string, unknown>) {
|
||||
const validatedConfig = validateJiraConnectionState(config);
|
||||
await this.as.botClient.sendStateEvent(this.roomId, JiraProjectConnection.CanonicalEventType, this.stateKey, validatedConfig);
|
||||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
|
@ -113,6 +113,20 @@ export class Provisioner {
|
||||
private async checkUserPermission(requiredPermission: "read"|"write", req: Request<{roomId: string}, unknown, unknown, {userId: string}>, res: Response, next: NextFunction) {
|
||||
const userId = req.query.userId;
|
||||
const roomId = req.params.roomId;
|
||||
try {
|
||||
const membership = await this.intent.underlyingClient.getRoomStateEvent(roomId, "m.room.member", this.intent.userId) as MembershipEventContent;
|
||||
if (membership.membership === "invite") {
|
||||
await this.intent.underlyingClient.joinRoom(roomId);
|
||||
} else if (membership.membership !== "join") {
|
||||
return next(new ApiError("Bot is not joined to the room.", ErrCode.NotInRoom));
|
||||
}
|
||||
} catch (ex) {
|
||||
if (ex.body.errcode === "M_NOT_FOUND") {
|
||||
return next(new ApiError("User is not joined to the room.", ErrCode.NotInRoom));
|
||||
}
|
||||
log.warn(`Failed to find member event for ${req.query.userId} in room ${roomId}`, ex);
|
||||
return next(new ApiError(`Could not determine if the user is in the room.`, ErrCode.NotInRoom));
|
||||
}
|
||||
// If the user just wants to read, just ensure they are in the room.
|
||||
try {
|
||||
const membership = await this.intent.underlyingClient.getRoomStateEvent(roomId, "m.room.member", userId) as MembershipEventContent;
|
||||
|
Loading…
x
Reference in New Issue
Block a user