Add a bit of safety around the IRC bridge and other integrations

This commit is contained in:
Travis Ralston 2019-04-13 16:31:02 -06:00
parent 0c66f0f5d2
commit 52f3c72db6
12 changed files with 84 additions and 9 deletions

View File

@ -22,6 +22,7 @@ interface BridgeResponse {
provisionUrl?: string;
isEnabled: boolean;
availableNetworks: AvailableNetworks;
isOnline: boolean;
}
interface SetEnabledRequest {
@ -42,12 +43,21 @@ export class AdminIrcService {
const bridges = await IrcBridgeRecord.findAll();
const client = new IrcBridge(userId);
return Promise.all(bridges.map(async b => {
let networks = null;
let isOnline = true;
try {
networks = await client.getNetworks(b);
} catch (e) {
LogService.error("AdminIrcService", e);
isOnline = false;
}
return {
id: b.id,
upstreamId: b.upstreamId,
provisionUrl: b.provisionUrl,
isEnabled: b.isEnabled,
availableNetworks: await client.getNetworks(b),
availableNetworks: networks,
isOnline: isOnline,
};
}));
}
@ -61,12 +71,21 @@ export class AdminIrcService {
if (!ircBridge) throw new ApiError(404, "IRC Bridge not found");
const client = new IrcBridge(userId);
let networks = null;
let isOnline = true;
try {
networks = await client.getNetworks(ircBridge);
} catch (e) {
LogService.error("AdminIrcService", e);
isOnline = false;
}
return {
id: ircBridge.id,
upstreamId: ircBridge.upstreamId,
provisionUrl: ircBridge.provisionUrl,
isEnabled: ircBridge.isEnabled,
availableNetworks: await client.getNetworks(ircBridge),
availableNetworks: networks,
isOnline: isOnline,
};
}

View File

@ -11,6 +11,7 @@ import { ComplexBot } from "../../integrations/ComplexBot";
import { Bridge } from "../../integrations/Bridge";
import { BridgeStore } from "../../db/BridgeStore";
import { BotStore } from "../../db/BotStore";
import { AdminService } from "../admin/AdminService";
export interface IntegrationsResponse {
widgets: Widget[],
@ -65,6 +66,11 @@ export class DimensionIntegrationsService {
.map(b => SimpleBot.fromCached(b));
const bots = [...nebs, ...custom];
Cache.for(CACHE_INTEGRATIONS).put("simple_bots", bots);
if (!AdminService.isAdmin(userId)) {
return bots.filter(b => b.isOnline);
}
return bots;
}
@ -80,6 +86,11 @@ export class DimensionIntegrationsService {
const bots = await NebStore.listComplexBots(userId, roomId);
Cache.for(CACHE_INTEGRATIONS).put("complex_bots_" + roomId, bots);
if (!AdminService.isAdmin(userId)) {
return bots.filter(b => b.isOnline);
}
return bots;
}

View File

@ -34,6 +34,10 @@ export class BridgeStore {
} catch (e) {
LogService.error("BridgeStore", "Failed to load configuration for bridge: " + bridgeRecord.name);
LogService.error("BridgeStore", e);
const bridge = new Bridge(bridgeRecord, {});
bridge.isOnline = false;
enabledBridges.push(bridge);
}
}

View File

@ -129,7 +129,15 @@ export class NebStore {
const rawIntegrations = await NebStore.listEnabledNebSimpleBots();
return Promise.all(rawIntegrations.map(async i => {
const proxy = new NebProxy(i.neb, requestingUserId);
return SimpleBot.fromNeb(i.integration, await proxy.getBotUserId(i.integration));
let userId = null;
try {
userId = await proxy.getBotUserId(i.integration);
} catch (e) {
LogService.error("NebStore", e);
}
const bot = SimpleBot.fromNeb(i.integration, userId);
bot.isOnline = !!userId;
return bot;
}));
}
@ -137,10 +145,18 @@ export class NebStore {
const rawIntegrations = await NebStore.listEnabledNebComplexBots();
return Promise.all(rawIntegrations.map(async i => {
const proxy = new NebProxy(i.neb, requestingUserId);
const notifUserId = await proxy.getNotificationUserId(i.integration, roomId);
const botUserId = null; // TODO: For github
const botConfig = await proxy.getServiceConfiguration(i.integration, roomId);
return new ComplexBot(i.integration, notifUserId, botUserId, botConfig);
try {
const notifUserId = await proxy.getNotificationUserId(i.integration, roomId);
const botUserId = null; // TODO: For github
const botConfig = await proxy.getServiceConfiguration(i.integration, roomId);
return new ComplexBot(i.integration, notifUserId, botUserId, botConfig);
} catch (e) {
LogService.error("NebStore", e);
const bot = new ComplexBot(i.integration, null, null, null);
bot.isOnline = false;
return bot;
}
}));
}

View File

@ -13,6 +13,7 @@ export class Integration {
public description: string;
public isEnabled: boolean;
public isPublic: boolean;
public isOnline: boolean;
constructor(record: IntegrationRecord) {
this.type = record.type;
@ -21,6 +22,7 @@ export class Integration {
this.description = record.description;
this.isEnabled = record.isEnabled;
this.isPublic = record.isPublic;
this.isOnline = true;
}
}

View File

@ -27,10 +27,14 @@
{{ bridge.upstreamId ? "matrix.org's bridge" : "Self-hosted bridge" }}
<span class="text-muted" style="display: inline-block;" *ngIf="!bridge.upstreamId">({{ bridge.provisionUrl }})</span>
</td>
<td>
<td *ngIf="bridge.isOnline">
{{ getEnabledNetworksString(bridge) }}
</td>
<td class="text-center">
<td *ngIf="!bridge.isOnline" class="error-text" colspan="2">
<i class="fa fa-exclamation-triangle"></i>
This bridge is offline or unavailable.
</td>
<td class="text-center" *ngIf="bridge.isOnline">
<span class="editButton" (click)="editNetworks(bridge)">
<i class="fa fa-pencil-alt"></i>
</span>

View File

@ -4,5 +4,6 @@
<div class="integration-name">{{ integration.displayName }}</div>
<div class="integration-description">{{ integration.description }}</div>
<div class="integration-arrow"><i class="fa fa-chevron-right"></i></div>
<div class="integration-offline error-text" *ngIf="!integration.isOnline"><i class="fa fa-exclamation-triangle"></i></div>
</div>
</div>

View File

@ -55,4 +55,10 @@
margin-right: 5px;
color: themed(integrationDescriptionColor);
}
.integration .integration-offline {
position: absolute;
top: 5px;
right: 6px;
}
}

View File

@ -233,6 +233,12 @@ export class RiotHomeComponent {
}
private async updateIntegrationState(integration: FE_Integration) {
if (!integration.isOnline) {
integration._isSupported = false;
integration._notSupportedReason = "This integration is offline or unavailable";
return;
}
if (!integration.requirements) return;
let promises = integration.requirements.map(r => this.checkRequirement(r));

View File

@ -8,6 +8,7 @@ export interface FE_Integration {
description: string;
isEnabled: boolean;
isPublic: boolean;
isOnline: boolean;
// Used by us
_inRoom: boolean;

View File

@ -3,6 +3,7 @@ export interface FE_IrcBridge {
upstreamId?: number;
provisionUrl?: string;
isEnabled: boolean;
isOnline: boolean;
availableNetworks: FE_IrcBridgeAvailableNetworks;
}

View File

@ -52,3 +52,7 @@ button {
display: block;
font-size: 12px;
}
.error-text {
color: #bd362f;
}