mirror of
https://github.com/turt2live/matrix-dimension.git
synced 2024-10-01 05:05:53 +00:00
Use MSCSecurity for all auth
This commit is contained in:
parent
7d54948d7e
commit
3b4aa7b0ba
@ -26,7 +26,7 @@ export class ApiError {
|
||||
* then converted to JSON as {message: "your_message"})
|
||||
* @param {string} errCode The internal error code to describe what went wrong
|
||||
*/
|
||||
constructor(statusCode: number, json: string | object, errCode = "D_UNKNOWN") {
|
||||
constructor(statusCode: number, json: string | object, errCode = "M_UNKNOWN") {
|
||||
// Because typescript is just plain dumb
|
||||
// https://stackoverflow.com/questions/31626231/custom-error-class-in-typescript
|
||||
Error.apply(this, ["ApiError"]);
|
||||
@ -37,5 +37,8 @@ export class ApiError {
|
||||
this.errorCode = errCode;
|
||||
|
||||
this.jsonResponse["dim_errcode"] = this.errorCode;
|
||||
|
||||
if (!this.jsonResponse['error']) this.jsonResponse['error'] = this.jsonResponse['message'];
|
||||
if (!this.jsonResponse['errcode']) this.jsonResponse['errcode'] = errCode;
|
||||
}
|
||||
}
|
@ -71,6 +71,11 @@ export default class Webserver {
|
||||
next();
|
||||
});
|
||||
this.app.use((_req, res, next) => {
|
||||
if (res.headersSent) {
|
||||
next();
|
||||
return;
|
||||
}
|
||||
|
||||
res.setHeader("Access-Control-Allow-Origin", "*");
|
||||
res.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
|
||||
next();
|
||||
|
@ -1,10 +1,10 @@
|
||||
import { GET, Path, PathParam, POST, QueryParam } from "typescript-rest";
|
||||
import { AdminService } from "./AdminService";
|
||||
import { Context, GET, Path, PathParam, POST, Security, ServiceContext } from "typescript-rest";
|
||||
import AppService from "../../db/models/AppService";
|
||||
import { AppserviceStore } from "../../db/AppserviceStore";
|
||||
import { ApiError } from "../ApiError";
|
||||
import { MatrixAppserviceClient } from "../../matrix/MatrixAppserviceClient";
|
||||
import { LogService } from "matrix-js-snippets";
|
||||
import { ROLE_MSC_ADMIN, ROLE_MSC_USER } from "../security/MSCSecurity";
|
||||
|
||||
interface AppserviceResponse {
|
||||
id: string;
|
||||
@ -23,18 +23,20 @@ interface AppserviceCreateRequest {
|
||||
@Path("/api/v1/dimension/admin/appservices")
|
||||
export class AdminAppserviceService {
|
||||
|
||||
@Context
|
||||
private context: ServiceContext;
|
||||
|
||||
@GET
|
||||
@Path("all")
|
||||
public async getAppservices(@QueryParam("scalar_token") scalarToken: string): Promise<AppserviceResponse[]> {
|
||||
await AdminService.validateAndGetAdminTokenOwner(scalarToken);
|
||||
@Security([ROLE_MSC_USER, ROLE_MSC_ADMIN])
|
||||
public async getAppservices(): Promise<AppserviceResponse[]> {
|
||||
return (await AppService.findAll()).map(a => this.mapAppservice(a));
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path(":appserviceId")
|
||||
public async getAppservice(@QueryParam("scalar_token") scalarToken: string, @PathParam("appserviceId") asId: string): Promise<AppserviceResponse> {
|
||||
await AdminService.validateAndGetAdminTokenOwner(scalarToken);
|
||||
|
||||
@Security([ROLE_MSC_USER, ROLE_MSC_ADMIN])
|
||||
public async getAppservice(@PathParam("appserviceId") asId: string): Promise<AppserviceResponse> {
|
||||
try {
|
||||
const appservice = await AppserviceStore.getAppservice(asId);
|
||||
return this.mapAppservice(appservice);
|
||||
@ -46,8 +48,9 @@ export class AdminAppserviceService {
|
||||
|
||||
@POST
|
||||
@Path("new")
|
||||
public async createAppservice(@QueryParam("scalar_token") scalarToken: string, request: AppserviceCreateRequest): Promise<AppserviceResponse> {
|
||||
const userId = await AdminService.validateAndGetAdminTokenOwner(scalarToken);
|
||||
@Security([ROLE_MSC_USER, ROLE_MSC_ADMIN])
|
||||
public async createAppservice(request: AppserviceCreateRequest): Promise<AppserviceResponse> {
|
||||
const userId = this.context.request.user.userId;
|
||||
|
||||
// Trim off the @ sign if it's on the prefix
|
||||
if (request.userPrefix[0] === "@") {
|
||||
@ -66,9 +69,8 @@ export class AdminAppserviceService {
|
||||
|
||||
@POST
|
||||
@Path(":appserviceId/test")
|
||||
public async test(@QueryParam("scalar_token") scalarToken: string, @PathParam("appserviceId") asId: string): Promise<any> {
|
||||
await AdminService.validateAndGetAdminTokenOwner(scalarToken);
|
||||
|
||||
@Security([ROLE_MSC_USER, ROLE_MSC_ADMIN])
|
||||
public async test(@PathParam("appserviceId") asId: string): Promise<any> {
|
||||
const appservice = await AppserviceStore.getAppservice(asId);
|
||||
const client = new MatrixAppserviceClient(appservice);
|
||||
const userId = await client.whoAmI();
|
||||
|
@ -1,9 +1,9 @@
|
||||
import { DELETE, GET, Path, PathParam, POST, QueryParam } from "typescript-rest";
|
||||
import { AdminService } from "./AdminService";
|
||||
import { Context, DELETE, GET, Path, PathParam, POST, Security, ServiceContext } from "typescript-rest";
|
||||
import { ApiError } from "../ApiError";
|
||||
import { LogService } from "matrix-js-snippets";
|
||||
import { BotStore } from "../../db/BotStore";
|
||||
import { Cache, CACHE_INTEGRATIONS } from "../../MemoryCache";
|
||||
import { ROLE_MSC_ADMIN, ROLE_MSC_USER } from "../security/MSCSecurity";
|
||||
|
||||
interface BotResponse extends BotRequest {
|
||||
id: number;
|
||||
@ -31,18 +31,20 @@ interface BotProfile {
|
||||
@Path("/api/v1/dimension/admin/bots/simple/custom")
|
||||
export class AdminCustomSimpleBotService {
|
||||
|
||||
@Context
|
||||
private context: ServiceContext;
|
||||
|
||||
@GET
|
||||
@Path("all")
|
||||
public async getBots(@QueryParam("scalar_token") scalarToken: string): Promise<BotResponse[]> {
|
||||
await AdminService.validateAndGetAdminTokenOwner(scalarToken);
|
||||
@Security([ROLE_MSC_USER, ROLE_MSC_ADMIN])
|
||||
public async getBots(): Promise<BotResponse[]> {
|
||||
return BotStore.getCustomBots();
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path(":botId")
|
||||
public async getBot(@QueryParam("scalar_token") scalarToken: string, @PathParam("botId") botId: number): Promise<BotResponse> {
|
||||
await AdminService.validateAndGetAdminTokenOwner(scalarToken);
|
||||
|
||||
@Security([ROLE_MSC_USER, ROLE_MSC_ADMIN])
|
||||
public async getBot(@PathParam("botId") botId: number): Promise<BotResponse> {
|
||||
const bot = await BotStore.getCustomBot(botId);
|
||||
if (!bot) throw new ApiError(404, "Bot not found");
|
||||
return bot;
|
||||
@ -50,9 +52,9 @@ export class AdminCustomSimpleBotService {
|
||||
|
||||
@POST
|
||||
@Path("new")
|
||||
public async createBot(@QueryParam("scalar_token") scalarToken: string, request: BotRequest): Promise<BotResponse> {
|
||||
const userId = await AdminService.validateAndGetAdminTokenOwner(scalarToken);
|
||||
|
||||
@Security([ROLE_MSC_USER, ROLE_MSC_ADMIN])
|
||||
public async createBot(request: BotRequest): Promise<BotResponse> {
|
||||
const userId = this.context.request.user.userId;
|
||||
const bot = await BotStore.createCustom(request);
|
||||
LogService.info("AdminCustomSimpleBotService", userId + " created a simple bot");
|
||||
Cache.for(CACHE_INTEGRATIONS).clear();
|
||||
@ -61,9 +63,9 @@ export class AdminCustomSimpleBotService {
|
||||
|
||||
@POST
|
||||
@Path(":botId")
|
||||
public async updateBot(@QueryParam("scalar_token") scalarToken: string, @PathParam("botId") botId: number, request: BotRequest): Promise<BotResponse> {
|
||||
const userId = await AdminService.validateAndGetAdminTokenOwner(scalarToken);
|
||||
|
||||
@Security([ROLE_MSC_USER, ROLE_MSC_ADMIN])
|
||||
public async updateBot(@PathParam("botId") botId: number, request: BotRequest): Promise<BotResponse> {
|
||||
const userId = this.context.request.user.userId;
|
||||
const bot = await BotStore.updateCustom(botId, request);
|
||||
LogService.info("AdminCustomSimpleBotService", userId + " updated a simple bot");
|
||||
Cache.for(CACHE_INTEGRATIONS).clear();
|
||||
@ -72,9 +74,9 @@ export class AdminCustomSimpleBotService {
|
||||
|
||||
@DELETE
|
||||
@Path(":botId")
|
||||
public async deleteBot(@QueryParam("scalar_token") scalarToken: string, @PathParam("botId") botId: number): Promise<any> {
|
||||
const userId = await AdminService.validateAndGetAdminTokenOwner(scalarToken);
|
||||
|
||||
@Security([ROLE_MSC_USER, ROLE_MSC_ADMIN])
|
||||
public async deleteBot(@PathParam("botId") botId: number): Promise<any> {
|
||||
const userId = this.context.request.user.userId;
|
||||
await BotStore.deleteCustom(botId);
|
||||
LogService.info("AdminCustomSimpleBotService", userId + " deleted a simple bot");
|
||||
Cache.for(CACHE_INTEGRATIONS).clear();
|
||||
@ -83,9 +85,8 @@ export class AdminCustomSimpleBotService {
|
||||
|
||||
@GET
|
||||
@Path("profile/:userId")
|
||||
public async getProfile(@QueryParam("scalar_token") scalarToken: string, @PathParam("userId") userId: string): Promise<BotProfile> {
|
||||
await AdminService.validateAndGetAdminTokenOwner(scalarToken);
|
||||
|
||||
@Security([ROLE_MSC_USER, ROLE_MSC_ADMIN])
|
||||
public async getProfile(@PathParam("userId") userId: string): Promise<BotProfile> {
|
||||
const profile = await BotStore.getProfile(userId);
|
||||
return {name: profile.displayName, avatarUrl: profile.avatarMxc};
|
||||
}
|
||||
|
@ -1,10 +1,10 @@
|
||||
import { GET, Path, PathParam, POST, QueryParam } from "typescript-rest";
|
||||
import { AdminService } from "./AdminService";
|
||||
import { Context, GET, Path, PathParam, POST, Security, ServiceContext } from "typescript-rest";
|
||||
import { Cache, CACHE_GITTER_BRIDGE, CACHE_INTEGRATIONS } from "../../MemoryCache";
|
||||
import { LogService } from "matrix-js-snippets";
|
||||
import { ApiError } from "../ApiError";
|
||||
import GitterBridgeRecord from "../../db/models/GitterBridgeRecord";
|
||||
import Upstream from "../../db/models/Upstream";
|
||||
import { ROLE_MSC_ADMIN, ROLE_MSC_USER } from "../security/MSCSecurity";
|
||||
|
||||
interface CreateWithUpstream {
|
||||
upstreamId: number;
|
||||
@ -27,11 +27,13 @@ interface BridgeResponse {
|
||||
@Path("/api/v1/dimension/admin/gitter")
|
||||
export class AdminGitterService {
|
||||
|
||||
@Context
|
||||
private context: ServiceContext;
|
||||
|
||||
@GET
|
||||
@Path("all")
|
||||
public async getBridges(@QueryParam("scalar_token") scalarToken: string): Promise<BridgeResponse[]> {
|
||||
await AdminService.validateAndGetAdminTokenOwner(scalarToken);
|
||||
|
||||
@Security([ROLE_MSC_USER, ROLE_MSC_ADMIN])
|
||||
public async getBridges(): Promise<BridgeResponse[]> {
|
||||
const bridges = await GitterBridgeRecord.findAll();
|
||||
return Promise.all(bridges.map(async b => {
|
||||
return {
|
||||
@ -45,9 +47,8 @@ export class AdminGitterService {
|
||||
|
||||
@GET
|
||||
@Path(":bridgeId")
|
||||
public async getBridge(@QueryParam("scalar_token") scalarToken: string, @PathParam("bridgeId") bridgeId: number): Promise<BridgeResponse> {
|
||||
await AdminService.validateAndGetAdminTokenOwner(scalarToken);
|
||||
|
||||
@Security([ROLE_MSC_USER, ROLE_MSC_ADMIN])
|
||||
public async getBridge(@PathParam("bridgeId") bridgeId: number): Promise<BridgeResponse> {
|
||||
const telegramBridge = await GitterBridgeRecord.findByPk(bridgeId);
|
||||
if (!telegramBridge) throw new ApiError(404, "Gitter Bridge not found");
|
||||
|
||||
@ -61,9 +62,9 @@ export class AdminGitterService {
|
||||
|
||||
@POST
|
||||
@Path(":bridgeId")
|
||||
public async updateBridge(@QueryParam("scalar_token") scalarToken: string, @PathParam("bridgeId") bridgeId: number, request: CreateSelfhosted): Promise<BridgeResponse> {
|
||||
const userId = await AdminService.validateAndGetAdminTokenOwner(scalarToken);
|
||||
|
||||
@Security([ROLE_MSC_USER, ROLE_MSC_ADMIN])
|
||||
public async updateBridge(@PathParam("bridgeId") bridgeId: number, request: CreateSelfhosted): Promise<BridgeResponse> {
|
||||
const userId = this.context.request.user.userId;
|
||||
const bridge = await GitterBridgeRecord.findByPk(bridgeId);
|
||||
if (!bridge) throw new ApiError(404, "Bridge not found");
|
||||
|
||||
@ -74,14 +75,14 @@ export class AdminGitterService {
|
||||
|
||||
Cache.for(CACHE_GITTER_BRIDGE).clear();
|
||||
Cache.for(CACHE_INTEGRATIONS).clear();
|
||||
return this.getBridge(scalarToken, bridge.id);
|
||||
return this.getBridge(bridge.id);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("new/upstream")
|
||||
public async newConfigForUpstream(@QueryParam("scalar_token") scalarToken: string, request: CreateWithUpstream): Promise<BridgeResponse> {
|
||||
const userId = await AdminService.validateAndGetAdminTokenOwner(scalarToken);
|
||||
|
||||
@Security([ROLE_MSC_USER, ROLE_MSC_ADMIN])
|
||||
public async newConfigForUpstream(request: CreateWithUpstream): Promise<BridgeResponse> {
|
||||
const userId = this.context.request.user.userId;
|
||||
const upstream = await Upstream.findByPk(request.upstreamId);
|
||||
if (!upstream) throw new ApiError(400, "Upstream not found");
|
||||
|
||||
@ -93,14 +94,14 @@ export class AdminGitterService {
|
||||
|
||||
Cache.for(CACHE_GITTER_BRIDGE).clear();
|
||||
Cache.for(CACHE_INTEGRATIONS).clear();
|
||||
return this.getBridge(scalarToken, bridge.id);
|
||||
return this.getBridge(bridge.id);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("new/selfhosted")
|
||||
public async newSelfhosted(@QueryParam("scalar_token") scalarToken: string, request: CreateSelfhosted): Promise<BridgeResponse> {
|
||||
const userId = await AdminService.validateAndGetAdminTokenOwner(scalarToken);
|
||||
|
||||
@Security([ROLE_MSC_USER, ROLE_MSC_ADMIN])
|
||||
public async newSelfhosted(request: CreateSelfhosted): Promise<BridgeResponse> {
|
||||
const userId = this.context.request.user.userId;
|
||||
const bridge = await GitterBridgeRecord.create({
|
||||
provisionUrl: request.provisionUrl,
|
||||
isEnabled: true,
|
||||
@ -109,6 +110,6 @@ export class AdminGitterService {
|
||||
|
||||
Cache.for(CACHE_GITTER_BRIDGE).clear();
|
||||
Cache.for(CACHE_INTEGRATIONS).clear();
|
||||
return this.getBridge(scalarToken, bridge.id);
|
||||
return this.getBridge(bridge.id);
|
||||
}
|
||||
}
|
@ -1,12 +1,12 @@
|
||||
import { GET, Path, PathParam, POST, QueryParam } from "typescript-rest";
|
||||
import { Context, GET, Path, PathParam, POST, Security, ServiceContext } from "typescript-rest";
|
||||
import { ApiError } from "../ApiError";
|
||||
import { AdminService } from "./AdminService";
|
||||
import { DimensionIntegrationsService } from "../dimension/DimensionIntegrationsService";
|
||||
import { WidgetStore } from "../../db/WidgetStore";
|
||||
import { Cache, CACHE_INTEGRATIONS } from "../../MemoryCache";
|
||||
import { Integration } from "../../integrations/Integration";
|
||||
import { LogService } from "matrix-js-snippets";
|
||||
import { BridgeStore } from "../../db/BridgeStore";
|
||||
import { ROLE_MSC_ADMIN, ROLE_MSC_USER } from "../security/MSCSecurity";
|
||||
|
||||
interface SetEnabledRequest {
|
||||
enabled: boolean;
|
||||
@ -23,10 +23,14 @@ interface SetOptionsRequest {
|
||||
@Path("/api/v1/dimension/admin/integrations")
|
||||
export class AdminIntegrationsService {
|
||||
|
||||
@Context
|
||||
private context: ServiceContext;
|
||||
|
||||
@POST
|
||||
@Path(":category/:type/options")
|
||||
public async setOptions(@QueryParam("scalar_token") scalarToken: string, @PathParam("category") category: string, @PathParam("type") type: string, body: SetOptionsRequest): Promise<any> {
|
||||
const userId = await AdminService.validateAndGetAdminTokenOwner(scalarToken);
|
||||
@Security([ROLE_MSC_USER, ROLE_MSC_ADMIN])
|
||||
public async setOptions(@PathParam("category") category: string, @PathParam("type") type: string, body: SetOptionsRequest): Promise<any> {
|
||||
const userId = this.context.request.user.userId;
|
||||
|
||||
if (category === "widget") await WidgetStore.setOptions(type, body.options);
|
||||
else throw new ApiError(400, "Unrecognized category");
|
||||
@ -39,8 +43,9 @@ export class AdminIntegrationsService {
|
||||
|
||||
@POST
|
||||
@Path(":category/:type/enabled")
|
||||
public async setEnabled(@QueryParam("scalar_token") scalarToken: string, @PathParam("category") category: string, @PathParam("type") type: string, body: SetEnabledRequest): Promise<any> {
|
||||
const userId = await AdminService.validateAndGetAdminTokenOwner(scalarToken);
|
||||
@Security([ROLE_MSC_USER, ROLE_MSC_ADMIN])
|
||||
public async setEnabled(@PathParam("category") category: string, @PathParam("type") type: string, body: SetEnabledRequest): Promise<any> {
|
||||
const userId = this.context.request.user.userId;
|
||||
|
||||
if (category === "widget") await WidgetStore.setEnabled(type, body.enabled);
|
||||
else if (category === "bridge") await BridgeStore.setEnabled(type, body.enabled);
|
||||
@ -53,8 +58,9 @@ export class AdminIntegrationsService {
|
||||
|
||||
@GET
|
||||
@Path(":category/all")
|
||||
public async getAllIntegrations(@QueryParam("scalar_token") scalarToken: string, @PathParam("category") category: string): Promise<Integration[]> {
|
||||
const userId = await AdminService.validateAndGetAdminTokenOwner(scalarToken);
|
||||
@Security([ROLE_MSC_USER, ROLE_MSC_ADMIN])
|
||||
public async getAllIntegrations(@PathParam("category") category: string): Promise<Integration[]> {
|
||||
const userId = this.context.request.user.userId;
|
||||
|
||||
if (category === "widget") return await DimensionIntegrationsService.getWidgets(false);
|
||||
else if (category === "bridge") return await DimensionIntegrationsService.getBridges(false, userId);
|
||||
|
@ -1,5 +1,4 @@
|
||||
import { GET, Path, PathParam, POST, QueryParam } from "typescript-rest";
|
||||
import { AdminService } from "./AdminService";
|
||||
import { Context, GET, Path, PathParam, POST, Security, ServiceContext } from "typescript-rest";
|
||||
import { Cache, CACHE_INTEGRATIONS, CACHE_IRC_BRIDGE } from "../../MemoryCache";
|
||||
import { LogService } from "matrix-js-snippets";
|
||||
import { ApiError } from "../ApiError";
|
||||
@ -7,6 +6,7 @@ import IrcBridgeRecord from "../../db/models/IrcBridgeRecord";
|
||||
import { AvailableNetworks, IrcBridge } from "../../bridges/IrcBridge";
|
||||
import Upstream from "../../db/models/Upstream";
|
||||
import IrcBridgeNetwork from "../../db/models/IrcBridgeNetwork";
|
||||
import { ROLE_MSC_ADMIN, ROLE_MSC_USER } from "../security/MSCSecurity";
|
||||
|
||||
interface CreateWithUpstream {
|
||||
upstreamId: number;
|
||||
@ -35,10 +35,14 @@ interface SetEnabledRequest {
|
||||
@Path("/api/v1/dimension/admin/irc")
|
||||
export class AdminIrcService {
|
||||
|
||||
@Context
|
||||
private context: ServiceContext;
|
||||
|
||||
@GET
|
||||
@Path("all")
|
||||
public async getBridges(@QueryParam("scalar_token") scalarToken: string): Promise<BridgeResponse[]> {
|
||||
const userId = await AdminService.validateAndGetAdminTokenOwner(scalarToken);
|
||||
@Security([ROLE_MSC_USER, ROLE_MSC_ADMIN])
|
||||
public async getBridges(): Promise<BridgeResponse[]> {
|
||||
const userId = this.context.request.user.userId;
|
||||
|
||||
const bridges = await IrcBridgeRecord.findAll();
|
||||
const client = new IrcBridge(userId);
|
||||
@ -64,8 +68,9 @@ export class AdminIrcService {
|
||||
|
||||
@GET
|
||||
@Path(":bridgeId")
|
||||
public async getBridge(@QueryParam("scalar_token") scalarToken: string, @PathParam("bridgeId") bridgeId: number): Promise<BridgeResponse> {
|
||||
const userId = await AdminService.validateAndGetAdminTokenOwner(scalarToken);
|
||||
@Security([ROLE_MSC_USER, ROLE_MSC_ADMIN])
|
||||
public async getBridge(@PathParam("bridgeId") bridgeId: number): Promise<BridgeResponse> {
|
||||
const userId = this.context.request.user.userId;
|
||||
|
||||
const ircBridge = await IrcBridgeRecord.findByPk(bridgeId);
|
||||
if (!ircBridge) throw new ApiError(404, "IRC Bridge not found");
|
||||
@ -91,8 +96,9 @@ export class AdminIrcService {
|
||||
|
||||
@POST
|
||||
@Path(":bridgeId/network/:networkId/enabled")
|
||||
public async setNetworkEnabled(@QueryParam("scalar_token") scalarToken: string, @PathParam("bridgeId") bridgeId: number, @PathParam("networkId") networkId: string, request: SetEnabledRequest): Promise<any> {
|
||||
const userId = await AdminService.validateAndGetAdminTokenOwner(scalarToken);
|
||||
@Security([ROLE_MSC_USER, ROLE_MSC_ADMIN])
|
||||
public async setNetworkEnabled(@PathParam("bridgeId") bridgeId: number, @PathParam("networkId") networkId: string, request: SetEnabledRequest): Promise<any> {
|
||||
const userId = this.context.request.user.userId;
|
||||
|
||||
const ircBridge = await IrcBridgeRecord.findByPk(bridgeId);
|
||||
if (!ircBridge) throw new ApiError(404, "IRC Bridge not found");
|
||||
@ -116,8 +122,9 @@ export class AdminIrcService {
|
||||
|
||||
@POST
|
||||
@Path("new/upstream")
|
||||
public async newConfigForUpstream(@QueryParam("scalar_token") scalarToken: string, request: CreateWithUpstream): Promise<BridgeResponse> {
|
||||
const userId = await AdminService.validateAndGetAdminTokenOwner(scalarToken);
|
||||
@Security([ROLE_MSC_USER, ROLE_MSC_ADMIN])
|
||||
public async newConfigForUpstream(request: CreateWithUpstream): Promise<BridgeResponse> {
|
||||
const userId = this.context.request.user.userId;
|
||||
|
||||
const upstream = await Upstream.findByPk(request.upstreamId);
|
||||
if (!upstream) throw new ApiError(400, "Upstream not found");
|
||||
@ -130,13 +137,14 @@ export class AdminIrcService {
|
||||
|
||||
Cache.for(CACHE_IRC_BRIDGE).clear();
|
||||
Cache.for(CACHE_INTEGRATIONS).clear();
|
||||
return this.getBridge(scalarToken, bridge.id);
|
||||
return this.getBridge(bridge.id);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("new/selfhosted")
|
||||
public async newSelfhosted(@QueryParam("scalar_token") scalarToken: string, request: CreateSelfhosted): Promise<BridgeResponse> {
|
||||
const userId = await AdminService.validateAndGetAdminTokenOwner(scalarToken);
|
||||
@Security([ROLE_MSC_USER, ROLE_MSC_ADMIN])
|
||||
public async newSelfhosted(request: CreateSelfhosted): Promise<BridgeResponse> {
|
||||
const userId = this.context.request.user.userId;
|
||||
|
||||
const bridge = await IrcBridgeRecord.create({
|
||||
provisionUrl: request.provisionUrl,
|
||||
@ -146,6 +154,6 @@ export class AdminIrcService {
|
||||
|
||||
Cache.for(CACHE_IRC_BRIDGE).clear();
|
||||
Cache.for(CACHE_INTEGRATIONS).clear();
|
||||
return this.getBridge(scalarToken, bridge.id);
|
||||
return this.getBridge(bridge.id);
|
||||
}
|
||||
}
|
@ -1,10 +1,10 @@
|
||||
import { GET, Path, PathParam, POST, QueryParam } from "typescript-rest";
|
||||
import { AdminService } from "./AdminService";
|
||||
import { Context, GET, Path, PathParam, POST, Security, ServiceContext } from "typescript-rest";
|
||||
import { Cache, CACHE_INTEGRATIONS, CACHE_NEB } from "../../MemoryCache";
|
||||
import { NebStore } from "../../db/NebStore";
|
||||
import { NebConfig } from "../../models/neb";
|
||||
import { LogService } from "matrix-js-snippets";
|
||||
import { ApiError } from "../ApiError";
|
||||
import { ROLE_MSC_ADMIN, ROLE_MSC_USER } from "../security/MSCSecurity";
|
||||
|
||||
interface CreateWithUpstream {
|
||||
upstreamId: number;
|
||||
@ -26,11 +26,13 @@ interface SetEnabledRequest {
|
||||
@Path("/api/v1/dimension/admin/neb")
|
||||
export class AdminNebService {
|
||||
|
||||
@Context
|
||||
private context: ServiceContext;
|
||||
|
||||
@GET
|
||||
@Path("all")
|
||||
public async getNebConfigs(@QueryParam("scalar_token") scalarToken: string): Promise<NebConfig[]> {
|
||||
await AdminService.validateAndGetAdminTokenOwner(scalarToken);
|
||||
|
||||
@Security([ROLE_MSC_USER, ROLE_MSC_ADMIN])
|
||||
public async getNebConfigs(): Promise<NebConfig[]> {
|
||||
const cachedConfigs = Cache.for(CACHE_NEB).get("configurations");
|
||||
if (cachedConfigs) return cachedConfigs;
|
||||
|
||||
@ -41,8 +43,9 @@ export class AdminNebService {
|
||||
|
||||
@GET
|
||||
@Path(":id/config")
|
||||
public async getNebConfig(@QueryParam("scalar_token") scalarToken: string, @PathParam("id") nebId: number): Promise<NebConfig> {
|
||||
const configs = await this.getNebConfigs(scalarToken); // does auth for us
|
||||
@Security([ROLE_MSC_USER, ROLE_MSC_ADMIN])
|
||||
public async getNebConfig(@PathParam("id") nebId: number): Promise<NebConfig> {
|
||||
const configs = await this.getNebConfigs();
|
||||
const firstConfig = configs.filter(c => c.id === nebId)[0];
|
||||
if (!firstConfig) throw new ApiError(404, "Configuration not found");
|
||||
return firstConfig;
|
||||
@ -50,8 +53,9 @@ export class AdminNebService {
|
||||
|
||||
@POST
|
||||
@Path(":id/integration/:type/enabled")
|
||||
public async setIntegrationEnabled(@QueryParam("scalar_token") scalarToken: string, @PathParam("id") nebId: number, @PathParam("type") integrationType: string, request: SetEnabledRequest): Promise<any> {
|
||||
const userId = await AdminService.validateAndGetAdminTokenOwner(scalarToken);
|
||||
@Security([ROLE_MSC_USER, ROLE_MSC_ADMIN])
|
||||
public async setIntegrationEnabled(@PathParam("id") nebId: number, @PathParam("type") integrationType: string, request: SetEnabledRequest): Promise<any> {
|
||||
const userId = this.context.request.user.userId;
|
||||
|
||||
await NebStore.setIntegrationEnabled(nebId, integrationType, request.enabled);
|
||||
LogService.info("AdminNebService", userId + " set the " + integrationType + " on NEB " + nebId + " to " + (request.enabled ? "enabled" : "disabled"));
|
||||
@ -63,8 +67,9 @@ export class AdminNebService {
|
||||
|
||||
@POST
|
||||
@Path(":id/integration/:type/config")
|
||||
public async setIntegrationConfig(@QueryParam("scalar_token") scalarToken: string, @PathParam("id") nebId: number, @PathParam("type") integrationType: string, newConfig: any): Promise<any> {
|
||||
const userId = await AdminService.validateAndGetAdminTokenOwner(scalarToken);
|
||||
@Security([ROLE_MSC_USER, ROLE_MSC_ADMIN])
|
||||
public async setIntegrationConfig(@PathParam("id") nebId: number, @PathParam("type") integrationType: string, newConfig: any): Promise<any> {
|
||||
const userId = this.context.request.user.userId;
|
||||
|
||||
await NebStore.setIntegrationConfig(nebId, integrationType, newConfig);
|
||||
LogService.info("AdminNebService", userId + " updated the configuration for " + integrationType + " on NEB " + nebId);
|
||||
@ -76,15 +81,16 @@ export class AdminNebService {
|
||||
|
||||
@GET
|
||||
@Path(":id/integration/:type/config")
|
||||
public async getIntegrationConfig(@QueryParam("scalar_token") scalarToken: string, @PathParam("id") nebId: number, @PathParam("type") integrationType: string): Promise<any> {
|
||||
await AdminService.validateAndGetAdminTokenOwner(scalarToken);
|
||||
@Security([ROLE_MSC_USER, ROLE_MSC_ADMIN])
|
||||
public async getIntegrationConfig(@PathParam("id") nebId: number, @PathParam("type") integrationType: string): Promise<any> {
|
||||
return NebStore.getIntegrationConfig(nebId, integrationType);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("new/upstream")
|
||||
public async newConfigForUpstream(@QueryParam("scalar_token") scalarToken: string, request: CreateWithUpstream): Promise<NebConfig> {
|
||||
const userId = await AdminService.validateAndGetAdminTokenOwner(scalarToken);
|
||||
@Security([ROLE_MSC_USER, ROLE_MSC_ADMIN])
|
||||
public async newConfigForUpstream(request: CreateWithUpstream): Promise<NebConfig> {
|
||||
const userId = this.context.request.user.userId;
|
||||
|
||||
try {
|
||||
const neb = await NebStore.createForUpstream(request.upstreamId);
|
||||
@ -101,8 +107,9 @@ export class AdminNebService {
|
||||
|
||||
@POST
|
||||
@Path("new/appservice")
|
||||
public async newConfigForAppservice(@QueryParam("scalar_token") scalarToken: string, request: CreateWithAppservice): Promise<NebConfig> {
|
||||
const userId = await AdminService.validateAndGetAdminTokenOwner(scalarToken);
|
||||
@Security([ROLE_MSC_USER, ROLE_MSC_ADMIN])
|
||||
public async newConfigForAppservice(request: CreateWithAppservice): Promise<NebConfig> {
|
||||
const userId = this.context.request.user.userId;
|
||||
|
||||
try {
|
||||
const neb = await NebStore.createForAppservice(request.appserviceId, request.adminUrl);
|
||||
|
@ -1,12 +1,11 @@
|
||||
import { GET, Path, POST, QueryParam } from "typescript-rest";
|
||||
import { GET, Path, POST, QueryParam, Security } from "typescript-rest";
|
||||
import config from "../../config";
|
||||
import { ApiError } from "../ApiError";
|
||||
import { MatrixLiteClient } from "../../matrix/MatrixLiteClient";
|
||||
import { CURRENT_VERSION } from "../../version";
|
||||
import { getFederationConnInfo } from "../../matrix/helpers";
|
||||
import UserScalarToken from "../../db/models/UserScalarToken";
|
||||
import { Cache, CACHE_SCALAR_ACCOUNTS } from "../../MemoryCache";
|
||||
import AccountController from "../controllers/AccountController";
|
||||
import { ROLE_MSC_ADMIN, ROLE_MSC_USER } from "../security/MSCSecurity";
|
||||
|
||||
interface DimensionVersionResponse {
|
||||
version: string;
|
||||
@ -33,49 +32,24 @@ interface DimensionConfigResponse {
|
||||
@Path("/api/v1/dimension/admin")
|
||||
export class AdminService {
|
||||
|
||||
/**
|
||||
* Determines if a given user is an administrator
|
||||
* @param {string} userId The user ID to validate
|
||||
* @returns {boolean} True if the user is an administrator
|
||||
*/
|
||||
public static isAdmin(userId: string) {
|
||||
return config.admins.indexOf(userId) >= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the given scalar token to ensure the owner is an administrator. If the
|
||||
* given scalar token does not belong to an administrator, an ApiError is raised.
|
||||
* @param {string} scalarToken The scalar token to validate
|
||||
* @returns {Promise<string>} Resolves to the owner's user ID if they are an administrator
|
||||
* @throws {ApiError} Thrown with a status code of 401 if the owner is not an administrator
|
||||
*/
|
||||
public static async validateAndGetAdminTokenOwner(scalarToken: string): Promise<string> {
|
||||
const accountController = new AccountController();
|
||||
const userId = await accountController.getTokenOwner(scalarToken, true);
|
||||
if (!AdminService.isAdmin(userId))
|
||||
throw new ApiError(401, "You must be an administrator to use this API");
|
||||
return userId;
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("check")
|
||||
public async checkIfAdmin(@QueryParam("scalar_token") scalarToken: string): Promise<{}> {
|
||||
await AdminService.validateAndGetAdminTokenOwner(scalarToken);
|
||||
@Security([ROLE_MSC_USER, ROLE_MSC_ADMIN])
|
||||
public async checkIfAdmin(): Promise<{}> {
|
||||
return {}; // A 200 OK essentially means "you're an admin".
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("version")
|
||||
public async getVersion(@QueryParam("scalar_token") scalarToken: string): Promise<DimensionVersionResponse> {
|
||||
await AdminService.validateAndGetAdminTokenOwner(scalarToken);
|
||||
@Security([ROLE_MSC_USER, ROLE_MSC_ADMIN])
|
||||
public async getVersion(): Promise<DimensionVersionResponse> {
|
||||
return {version: CURRENT_VERSION};
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("config")
|
||||
public async getConfig(@QueryParam("scalar_token") scalarToken: string): Promise<DimensionConfigResponse> {
|
||||
await AdminService.validateAndGetAdminTokenOwner(scalarToken);
|
||||
|
||||
@Security([ROLE_MSC_USER, ROLE_MSC_ADMIN])
|
||||
public async getConfig(): Promise<DimensionConfigResponse> {
|
||||
const client = new MatrixLiteClient(config.homeserver.accessToken);
|
||||
const fedInfo = await getFederationConnInfo(config.homeserver.name);
|
||||
return {
|
||||
@ -96,9 +70,8 @@ export class AdminService {
|
||||
|
||||
@GET
|
||||
@Path("test/federation")
|
||||
public async testFederationRouting(@QueryParam("scalar_token") scalarToken: string, @QueryParam("server_name") serverName: string): Promise<any> {
|
||||
await AdminService.validateAndGetAdminTokenOwner(scalarToken);
|
||||
|
||||
@Security([ROLE_MSC_USER, ROLE_MSC_ADMIN])
|
||||
public async testFederationRouting(@QueryParam("server_name") serverName: string): Promise<any> {
|
||||
return {
|
||||
inputServerName: serverName,
|
||||
resolvedServer: await getFederationConnInfo(serverName),
|
||||
@ -107,9 +80,8 @@ export class AdminService {
|
||||
|
||||
@POST
|
||||
@Path("sessions/logout/all")
|
||||
public async logoutAll(@QueryParam("scalar_token") scalarToken: string): Promise<any> {
|
||||
await AdminService.validateAndGetAdminTokenOwner(scalarToken);
|
||||
|
||||
@Security([ROLE_MSC_USER, ROLE_MSC_ADMIN])
|
||||
public async logoutAll(): Promise<any> {
|
||||
// Clear the cache first to hopefully invalidate a bunch of them
|
||||
Cache.for(CACHE_SCALAR_ACCOUNTS).clear();
|
||||
|
||||
|
@ -1,10 +1,10 @@
|
||||
import { GET, Path, PathParam, POST, QueryParam } from "typescript-rest";
|
||||
import { AdminService } from "./AdminService";
|
||||
import { Context, GET, Path, PathParam, POST, Security, ServiceContext } from "typescript-rest";
|
||||
import { Cache, CACHE_INTEGRATIONS, CACHE_SLACK_BRIDGE } from "../../MemoryCache";
|
||||
import { LogService } from "matrix-js-snippets";
|
||||
import { ApiError } from "../ApiError";
|
||||
import Upstream from "../../db/models/Upstream";
|
||||
import SlackBridgeRecord from "../../db/models/SlackBridgeRecord";
|
||||
import { ROLE_MSC_ADMIN, ROLE_MSC_USER } from "../security/MSCSecurity";
|
||||
|
||||
interface CreateWithUpstream {
|
||||
upstreamId: number;
|
||||
@ -27,11 +27,13 @@ interface BridgeResponse {
|
||||
@Path("/api/v1/dimension/admin/slack")
|
||||
export class AdminSlackService {
|
||||
|
||||
@Context
|
||||
private context: ServiceContext;
|
||||
|
||||
@GET
|
||||
@Path("all")
|
||||
public async getBridges(@QueryParam("scalar_token") scalarToken: string): Promise<BridgeResponse[]> {
|
||||
await AdminService.validateAndGetAdminTokenOwner(scalarToken);
|
||||
|
||||
@Security([ROLE_MSC_USER, ROLE_MSC_ADMIN])
|
||||
public async getBridges(): Promise<BridgeResponse[]> {
|
||||
const bridges = await SlackBridgeRecord.findAll();
|
||||
return Promise.all(bridges.map(async b => {
|
||||
return {
|
||||
@ -45,9 +47,8 @@ export class AdminSlackService {
|
||||
|
||||
@GET
|
||||
@Path(":bridgeId")
|
||||
public async getBridge(@QueryParam("scalar_token") scalarToken: string, @PathParam("bridgeId") bridgeId: number): Promise<BridgeResponse> {
|
||||
await AdminService.validateAndGetAdminTokenOwner(scalarToken);
|
||||
|
||||
@Security([ROLE_MSC_USER, ROLE_MSC_ADMIN])
|
||||
public async getBridge(@PathParam("bridgeId") bridgeId: number): Promise<BridgeResponse> {
|
||||
const telegramBridge = await SlackBridgeRecord.findByPk(bridgeId);
|
||||
if (!telegramBridge) throw new ApiError(404, "Slack Bridge not found");
|
||||
|
||||
@ -61,9 +62,9 @@ export class AdminSlackService {
|
||||
|
||||
@POST
|
||||
@Path(":bridgeId")
|
||||
public async updateBridge(@QueryParam("scalar_token") scalarToken: string, @PathParam("bridgeId") bridgeId: number, request: CreateSelfhosted): Promise<BridgeResponse> {
|
||||
const userId = await AdminService.validateAndGetAdminTokenOwner(scalarToken);
|
||||
|
||||
@Security([ROLE_MSC_USER, ROLE_MSC_ADMIN])
|
||||
public async updateBridge(@PathParam("bridgeId") bridgeId: number, request: CreateSelfhosted): Promise<BridgeResponse> {
|
||||
const userId = this.context.request.user.userId;
|
||||
const bridge = await SlackBridgeRecord.findByPk(bridgeId);
|
||||
if (!bridge) throw new ApiError(404, "Bridge not found");
|
||||
|
||||
@ -74,14 +75,14 @@ export class AdminSlackService {
|
||||
|
||||
Cache.for(CACHE_SLACK_BRIDGE).clear();
|
||||
Cache.for(CACHE_INTEGRATIONS).clear();
|
||||
return this.getBridge(scalarToken, bridge.id);
|
||||
return this.getBridge(bridge.id);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("new/upstream")
|
||||
public async newConfigForUpstream(@QueryParam("scalar_token") scalarToken: string, request: CreateWithUpstream): Promise<BridgeResponse> {
|
||||
const userId = await AdminService.validateAndGetAdminTokenOwner(scalarToken);
|
||||
|
||||
@Security([ROLE_MSC_USER, ROLE_MSC_ADMIN])
|
||||
public async newConfigForUpstream(request: CreateWithUpstream): Promise<BridgeResponse> {
|
||||
const userId = this.context.request.user.userId;
|
||||
const upstream = await Upstream.findByPk(request.upstreamId);
|
||||
if (!upstream) throw new ApiError(400, "Upstream not found");
|
||||
|
||||
@ -93,14 +94,14 @@ export class AdminSlackService {
|
||||
|
||||
Cache.for(CACHE_SLACK_BRIDGE).clear();
|
||||
Cache.for(CACHE_INTEGRATIONS).clear();
|
||||
return this.getBridge(scalarToken, bridge.id);
|
||||
return this.getBridge(bridge.id);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("new/selfhosted")
|
||||
public async newSelfhosted(@QueryParam("scalar_token") scalarToken: string, request: CreateSelfhosted): Promise<BridgeResponse> {
|
||||
const userId = await AdminService.validateAndGetAdminTokenOwner(scalarToken);
|
||||
|
||||
@Security([ROLE_MSC_USER, ROLE_MSC_ADMIN])
|
||||
public async newSelfhosted(request: CreateSelfhosted): Promise<BridgeResponse> {
|
||||
const userId = this.context.request.user.userId;
|
||||
const bridge = await SlackBridgeRecord.create({
|
||||
provisionUrl: request.provisionUrl,
|
||||
isEnabled: true,
|
||||
@ -109,6 +110,6 @@ export class AdminSlackService {
|
||||
|
||||
Cache.for(CACHE_SLACK_BRIDGE).clear();
|
||||
Cache.for(CACHE_INTEGRATIONS).clear();
|
||||
return this.getBridge(scalarToken, bridge.id);
|
||||
return this.getBridge(bridge.id);
|
||||
}
|
||||
}
|
@ -1,5 +1,4 @@
|
||||
import { GET, Path, PathParam, POST, QueryParam } from "typescript-rest";
|
||||
import { AdminService } from "./AdminService";
|
||||
import { Context, GET, Path, PathParam, POST, Security, ServiceContext } from "typescript-rest";
|
||||
import StickerPack from "../../db/models/StickerPack";
|
||||
import { ApiError } from "../ApiError";
|
||||
import { DimensionStickerService, MemoryStickerPack } from "../dimension/DimensionStickerService";
|
||||
@ -10,6 +9,7 @@ import config from "../../config";
|
||||
import Sticker from "../../db/models/Sticker";
|
||||
import { LogService } from "matrix-js-snippets";
|
||||
import * as sharp from "sharp";
|
||||
import { ROLE_MSC_ADMIN, ROLE_MSC_USER } from "../security/MSCSecurity";
|
||||
|
||||
interface SetEnabledRequest {
|
||||
isEnabled: boolean;
|
||||
@ -25,17 +25,20 @@ interface ImportTelegramRequest {
|
||||
@Path("/api/v1/dimension/admin/stickers")
|
||||
export class AdminStickerService {
|
||||
|
||||
@Context
|
||||
private context: ServiceContext;
|
||||
|
||||
@GET
|
||||
@Path("packs")
|
||||
public async getStickerPacks(@QueryParam("scalar_token") scalarToken: string): Promise<MemoryStickerPack[]> {
|
||||
await AdminService.validateAndGetAdminTokenOwner(scalarToken);
|
||||
@Security([ROLE_MSC_USER, ROLE_MSC_ADMIN])
|
||||
public async getStickerPacks(): Promise<MemoryStickerPack[]> {
|
||||
return await DimensionStickerService.getStickerPacks(false);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("packs/:id/enabled")
|
||||
public async setPackEnabled(@QueryParam("scalar_token") scalarToken: string, @PathParam("id") packId: number, request: SetEnabledRequest): Promise<any> {
|
||||
await AdminService.validateAndGetAdminTokenOwner(scalarToken);
|
||||
@Security([ROLE_MSC_USER, ROLE_MSC_ADMIN])
|
||||
public async setPackEnabled(@PathParam("id") packId: number, request: SetEnabledRequest): Promise<any> {
|
||||
const pack = await StickerPack.findByPk(packId);
|
||||
if (!pack) throw new ApiError(404, "Sticker pack not found");
|
||||
|
||||
@ -48,8 +51,9 @@ export class AdminStickerService {
|
||||
|
||||
@POST
|
||||
@Path("packs/import/telegram")
|
||||
public async importFromTelegram(@QueryParam("scalar_token") scalarToken: string, request: ImportTelegramRequest): Promise<MemoryStickerPack> {
|
||||
const userId = await AdminService.validateAndGetAdminTokenOwner(scalarToken);
|
||||
@Security([ROLE_MSC_USER, ROLE_MSC_ADMIN])
|
||||
public async importFromTelegram(request: ImportTelegramRequest): Promise<MemoryStickerPack> {
|
||||
const userId = this.context.request.user.userId;
|
||||
|
||||
if (!request.packUrl || (!request.packUrl.startsWith("https://t.me/addstickers/") && !request.packUrl.startsWith("https://telegram.me/addstickers/"))) {
|
||||
throw new ApiError(400, "Invalid pack URL");
|
||||
|
@ -1,9 +1,9 @@
|
||||
import { GET, Path, PathParam, POST, QueryParam } from "typescript-rest";
|
||||
import { AdminService } from "./AdminService";
|
||||
import { Context, GET, Path, PathParam, POST, QueryParam, Security, ServiceContext } from "typescript-rest";
|
||||
import { Cache, CACHE_INTEGRATIONS, CACHE_TELEGRAM_BRIDGE } from "../../MemoryCache";
|
||||
import { LogService } from "matrix-js-snippets";
|
||||
import { ApiError } from "../ApiError";
|
||||
import TelegramBridgeRecord from "../../db/models/TelegramBridgeRecord";
|
||||
import { ROLE_MSC_ADMIN, ROLE_MSC_USER } from "../security/MSCSecurity";
|
||||
|
||||
interface CreateWithUpstream {
|
||||
upstreamId: number;
|
||||
@ -32,11 +32,13 @@ interface BridgeResponse {
|
||||
@Path("/api/v1/dimension/admin/telegram")
|
||||
export class AdminTelegramService {
|
||||
|
||||
@Context
|
||||
private context: ServiceContext;
|
||||
|
||||
@GET
|
||||
@Path("all")
|
||||
public async getBridges(@QueryParam("scalar_token") scalarToken: string): Promise<BridgeResponse[]> {
|
||||
await AdminService.validateAndGetAdminTokenOwner(scalarToken);
|
||||
|
||||
@Security([ROLE_MSC_USER, ROLE_MSC_ADMIN])
|
||||
public async getBridges(): Promise<BridgeResponse[]> {
|
||||
const bridges = await TelegramBridgeRecord.findAll();
|
||||
return Promise.all(bridges.map(async b => {
|
||||
return {
|
||||
@ -53,9 +55,8 @@ export class AdminTelegramService {
|
||||
|
||||
@GET
|
||||
@Path(":bridgeId")
|
||||
public async getBridge(@QueryParam("scalar_token") scalarToken: string, @PathParam("bridgeId") bridgeId: number): Promise<BridgeResponse> {
|
||||
await AdminService.validateAndGetAdminTokenOwner(scalarToken);
|
||||
|
||||
@Security([ROLE_MSC_USER, ROLE_MSC_ADMIN])
|
||||
public async getBridge(@PathParam("bridgeId") bridgeId: number): Promise<BridgeResponse> {
|
||||
const telegramBridge = await TelegramBridgeRecord.findByPk(bridgeId);
|
||||
if (!telegramBridge) throw new ApiError(404, "Telegram Bridge not found");
|
||||
|
||||
@ -72,8 +73,9 @@ export class AdminTelegramService {
|
||||
|
||||
@POST
|
||||
@Path(":bridgeId")
|
||||
public async updateBridge(@QueryParam("scalar_token") scalarToken: string, @PathParam("bridgeId") bridgeId: number, request: CreateSelfhosted): Promise<BridgeResponse> {
|
||||
const userId = await AdminService.validateAndGetAdminTokenOwner(scalarToken);
|
||||
@Security([ROLE_MSC_USER, ROLE_MSC_ADMIN])
|
||||
public async updateBridge(@PathParam("bridgeId") bridgeId: number, request: CreateSelfhosted): Promise<BridgeResponse> {
|
||||
const userId = this.context.request.user.userId;
|
||||
|
||||
const bridge = await TelegramBridgeRecord.findByPk(bridgeId);
|
||||
if (!bridge) throw new ApiError(404, "Bridge not found");
|
||||
@ -88,19 +90,21 @@ export class AdminTelegramService {
|
||||
|
||||
Cache.for(CACHE_TELEGRAM_BRIDGE).clear();
|
||||
Cache.for(CACHE_INTEGRATIONS).clear();
|
||||
return this.getBridge(scalarToken, bridge.id);
|
||||
return this.getBridge(bridge.id);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("new/upstream")
|
||||
@Security([ROLE_MSC_USER, ROLE_MSC_ADMIN])
|
||||
public async newConfigForUpstream(@QueryParam("scalar_token") _scalarToken: string, _request: CreateWithUpstream): Promise<BridgeResponse> {
|
||||
throw new ApiError(400, "Cannot create a telegram bridge from an upstream");
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("new/selfhosted")
|
||||
public async newSelfhosted(@QueryParam("scalar_token") scalarToken: string, request: CreateSelfhosted): Promise<BridgeResponse> {
|
||||
const userId = await AdminService.validateAndGetAdminTokenOwner(scalarToken);
|
||||
@Security([ROLE_MSC_USER, ROLE_MSC_ADMIN])
|
||||
public async newSelfhosted(request: CreateSelfhosted): Promise<BridgeResponse> {
|
||||
const userId = this.context.request.user.userId;
|
||||
|
||||
const bridge = await TelegramBridgeRecord.create({
|
||||
provisionUrl: request.provisionUrl,
|
||||
@ -113,6 +117,6 @@ export class AdminTelegramService {
|
||||
|
||||
Cache.for(CACHE_TELEGRAM_BRIDGE).clear();
|
||||
Cache.for(CACHE_INTEGRATIONS).clear();
|
||||
return this.getBridge(scalarToken, bridge.id);
|
||||
return this.getBridge(bridge.id);
|
||||
}
|
||||
}
|
@ -1,8 +1,8 @@
|
||||
import { GET, Path, POST, QueryParam } from "typescript-rest";
|
||||
import { AdminService } from "./AdminService";
|
||||
import { Context, GET, Path, POST, Security, ServiceContext } from "typescript-rest";
|
||||
import { Cache, CACHE_SCALAR_ACCOUNTS, CACHE_UPSTREAM } from "../../MemoryCache";
|
||||
import Upstream from "../../db/models/Upstream";
|
||||
import { LogService } from "matrix-js-snippets";
|
||||
import { ROLE_MSC_ADMIN, ROLE_MSC_USER } from "../security/MSCSecurity";
|
||||
|
||||
interface UpstreamRepsonse {
|
||||
id: number;
|
||||
@ -26,11 +26,13 @@ interface NewUpstreamRequest {
|
||||
@Path("/api/v1/dimension/admin/upstreams")
|
||||
export class AdminUpstreamService {
|
||||
|
||||
@Context
|
||||
private context: ServiceContext;
|
||||
|
||||
@GET
|
||||
@Path("all")
|
||||
public async getUpstreams(@QueryParam("scalar_token") scalarToken: string): Promise<UpstreamRepsonse[]> {
|
||||
await AdminService.validateAndGetAdminTokenOwner(scalarToken);
|
||||
|
||||
@Security([ROLE_MSC_USER, ROLE_MSC_ADMIN])
|
||||
public async getUpstreams(): Promise<UpstreamRepsonse[]> {
|
||||
const cachedUpstreams = Cache.for(CACHE_UPSTREAM).get("upstreams");
|
||||
if (cachedUpstreams) return cachedUpstreams;
|
||||
|
||||
@ -42,8 +44,9 @@ export class AdminUpstreamService {
|
||||
|
||||
@POST
|
||||
@Path("new")
|
||||
public async createUpstream(@QueryParam("scalar_token") scalarToken: string, request: NewUpstreamRequest): Promise<UpstreamRepsonse> {
|
||||
const userId = await AdminService.validateAndGetAdminTokenOwner(scalarToken);
|
||||
@Security([ROLE_MSC_USER, ROLE_MSC_ADMIN])
|
||||
public async createUpstream(request: NewUpstreamRequest): Promise<UpstreamRepsonse> {
|
||||
const userId = this.context.request.user.userId;
|
||||
|
||||
const upstream = await Upstream.create({
|
||||
name: request.name,
|
||||
|
@ -1,9 +1,9 @@
|
||||
import { GET, Path, PathParam, POST, QueryParam } from "typescript-rest";
|
||||
import { AdminService } from "./AdminService";
|
||||
import { Context, GET, Path, PathParam, POST, QueryParam, Security, ServiceContext } from "typescript-rest";
|
||||
import { Cache, CACHE_INTEGRATIONS, CACHE_WEBHOOKS_BRIDGE } from "../../MemoryCache";
|
||||
import { LogService } from "matrix-js-snippets";
|
||||
import { ApiError } from "../ApiError";
|
||||
import WebhookBridgeRecord from "../../db/models/WebhookBridgeRecord";
|
||||
import { ROLE_MSC_ADMIN, ROLE_MSC_USER } from "../security/MSCSecurity";
|
||||
|
||||
interface CreateWithUpstream {
|
||||
upstreamId: number;
|
||||
@ -28,11 +28,13 @@ interface BridgeResponse {
|
||||
@Path("/api/v1/dimension/admin/webhooks")
|
||||
export class AdminWebhooksService {
|
||||
|
||||
@Context
|
||||
private context: ServiceContext;
|
||||
|
||||
@GET
|
||||
@Path("all")
|
||||
public async getBridges(@QueryParam("scalar_token") scalarToken: string): Promise<BridgeResponse[]> {
|
||||
await AdminService.validateAndGetAdminTokenOwner(scalarToken);
|
||||
|
||||
@Security([ROLE_MSC_USER, ROLE_MSC_ADMIN])
|
||||
public async getBridges(): Promise<BridgeResponse[]> {
|
||||
const bridges = await WebhookBridgeRecord.findAll();
|
||||
return Promise.all(bridges.map(async b => {
|
||||
return {
|
||||
@ -47,9 +49,8 @@ export class AdminWebhooksService {
|
||||
|
||||
@GET
|
||||
@Path(":bridgeId")
|
||||
public async getBridge(@QueryParam("scalar_token") scalarToken: string, @PathParam("bridgeId") bridgeId: number): Promise<BridgeResponse> {
|
||||
await AdminService.validateAndGetAdminTokenOwner(scalarToken);
|
||||
|
||||
@Security([ROLE_MSC_USER, ROLE_MSC_ADMIN])
|
||||
public async getBridge(@PathParam("bridgeId") bridgeId: number): Promise<BridgeResponse> {
|
||||
const webhookBridge = await WebhookBridgeRecord.findByPk(bridgeId);
|
||||
if (!webhookBridge) throw new ApiError(404, "Webhook Bridge not found");
|
||||
|
||||
@ -64,8 +65,9 @@ export class AdminWebhooksService {
|
||||
|
||||
@POST
|
||||
@Path(":bridgeId")
|
||||
public async updateBridge(@QueryParam("scalar_token") scalarToken: string, @PathParam("bridgeId") bridgeId: number, request: CreateSelfhosted): Promise<BridgeResponse> {
|
||||
const userId = await AdminService.validateAndGetAdminTokenOwner(scalarToken);
|
||||
@Security([ROLE_MSC_USER, ROLE_MSC_ADMIN])
|
||||
public async updateBridge(@PathParam("bridgeId") bridgeId: number, request: CreateSelfhosted): Promise<BridgeResponse> {
|
||||
const userId = this.context.request.user.userId;
|
||||
|
||||
const bridge = await WebhookBridgeRecord.findByPk(bridgeId);
|
||||
if (!bridge) throw new ApiError(404, "Bridge not found");
|
||||
@ -78,19 +80,21 @@ export class AdminWebhooksService {
|
||||
|
||||
Cache.for(CACHE_WEBHOOKS_BRIDGE).clear();
|
||||
Cache.for(CACHE_INTEGRATIONS).clear();
|
||||
return this.getBridge(scalarToken, bridge.id);
|
||||
return this.getBridge(bridge.id);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("new/upstream")
|
||||
@Security([ROLE_MSC_USER, ROLE_MSC_ADMIN])
|
||||
public async newConfigForUpstream(@QueryParam("scalar_token") _scalarToken: string, _request: CreateWithUpstream): Promise<BridgeResponse> {
|
||||
throw new ApiError(400, "Cannot create a webhook bridge from an upstream");
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("new/selfhosted")
|
||||
public async newSelfhosted(@QueryParam("scalar_token") scalarToken: string, request: CreateSelfhosted): Promise<BridgeResponse> {
|
||||
const userId = await AdminService.validateAndGetAdminTokenOwner(scalarToken);
|
||||
@Security([ROLE_MSC_USER, ROLE_MSC_ADMIN])
|
||||
public async newSelfhosted(request: CreateSelfhosted): Promise<BridgeResponse> {
|
||||
const userId = this.context.request.user.userId;
|
||||
|
||||
const bridge = await WebhookBridgeRecord.create({
|
||||
provisionUrl: request.provisionUrl,
|
||||
@ -101,6 +105,6 @@ export class AdminWebhooksService {
|
||||
|
||||
Cache.for(CACHE_WEBHOOKS_BRIDGE).clear();
|
||||
Cache.for(CACHE_INTEGRATIONS).clear();
|
||||
return this.getBridge(scalarToken, bridge.id);
|
||||
return this.getBridge(bridge.id);
|
||||
}
|
||||
}
|
@ -31,16 +31,15 @@ export default class AccountController {
|
||||
/**
|
||||
* Gets the owner of a given scalar token, throwing an ApiError if the token is invalid.
|
||||
* @param {string} scalarToken The scalar token to validate
|
||||
* @param {boolean} ignoreUpstreams True to consider the token valid if it is missing links to other upstreams
|
||||
* @returns {Promise<string>} Resolves to the owner's user ID if the token is valid.
|
||||
* @throws {ApiError} Thrown with a status code of 401 if the token is invalid.
|
||||
*/
|
||||
public async getTokenOwner(scalarToken: string, ignoreUpstreams = false): Promise<string> {
|
||||
public async getTokenOwner(scalarToken: string): Promise<string> {
|
||||
const cachedUserId = Cache.for(CACHE_SCALAR_ACCOUNTS).get(scalarToken);
|
||||
if (cachedUserId) return cachedUserId;
|
||||
|
||||
try {
|
||||
const user = await ScalarStore.getTokenOwner(scalarToken, ignoreUpstreams);
|
||||
const user = await ScalarStore.getTokenOwner(scalarToken);
|
||||
Cache.for(CACHE_SCALAR_ACCOUNTS).put(scalarToken, user.userId, 30 * 60 * 1000); // 30 minutes
|
||||
return user.userId;
|
||||
} catch (err) {
|
||||
|
@ -1,9 +1,8 @@
|
||||
import { DELETE, GET, Path, PathParam, POST, QueryParam } from "typescript-rest";
|
||||
import { Context, DELETE, GET, Path, PathParam, POST, Security, ServiceContext } from "typescript-rest";
|
||||
import { ApiError } from "../ApiError";
|
||||
import { BridgedRoom, GitterBridge } from "../../bridges/GitterBridge";
|
||||
import { LogService } from "matrix-js-snippets";
|
||||
import { AutoWired, Inject } from "typescript-ioc/es6";
|
||||
import AccountController from "../controllers/AccountController";
|
||||
import { ROLE_MSC_USER } from "../security/MSCSecurity";
|
||||
|
||||
interface BridgeRoomRequest {
|
||||
gitterRoomName: string;
|
||||
@ -13,17 +12,16 @@ interface BridgeRoomRequest {
|
||||
* API for interacting with the Gitter bridge
|
||||
*/
|
||||
@Path("/api/v1/dimension/gitter")
|
||||
@AutoWired
|
||||
export class DimensionGitterService {
|
||||
|
||||
@Inject
|
||||
private accountController: AccountController;
|
||||
@Context
|
||||
private context: ServiceContext;
|
||||
|
||||
@GET
|
||||
@Path("room/:roomId/link")
|
||||
public async getLink(@QueryParam("scalar_token") scalarToken: string, @PathParam("roomId") roomId: string): Promise<BridgedRoom> {
|
||||
const userId = await this.accountController.getTokenOwner(scalarToken);
|
||||
|
||||
@Security(ROLE_MSC_USER)
|
||||
public async getLink(@PathParam("roomId") roomId: string): Promise<BridgedRoom> {
|
||||
const userId = this.context.request.user.userId;
|
||||
try {
|
||||
const gitter = new GitterBridge(userId);
|
||||
return gitter.getLink(roomId);
|
||||
@ -35,9 +33,9 @@ export class DimensionGitterService {
|
||||
|
||||
@POST
|
||||
@Path("room/:roomId/link")
|
||||
public async bridgeRoom(@QueryParam("scalar_token") scalarToken: string, @PathParam("roomId") roomId: string, request: BridgeRoomRequest): Promise<BridgedRoom> {
|
||||
const userId = await this.accountController.getTokenOwner(scalarToken);
|
||||
|
||||
@Security(ROLE_MSC_USER)
|
||||
public async bridgeRoom(@PathParam("roomId") roomId: string, request: BridgeRoomRequest): Promise<BridgedRoom> {
|
||||
const userId = this.context.request.user.userId;
|
||||
try {
|
||||
const gitter = new GitterBridge(userId);
|
||||
await gitter.requestLink(roomId, request.gitterRoomName);
|
||||
@ -50,9 +48,9 @@ export class DimensionGitterService {
|
||||
|
||||
@DELETE
|
||||
@Path("room/:roomId/link")
|
||||
public async unbridgeRoom(@QueryParam("scalar_token") scalarToken: string, @PathParam("roomId") roomId: string): Promise<any> {
|
||||
const userId = await this.accountController.getTokenOwner(scalarToken);
|
||||
|
||||
@Security(ROLE_MSC_USER)
|
||||
public async unbridgeRoom(@PathParam("roomId") roomId: string): Promise<any> {
|
||||
const userId = this.context.request.user.userId;
|
||||
try {
|
||||
const gitter = new GitterBridge(userId);
|
||||
const link = await gitter.getLink(roomId);
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { DELETE, GET, Path, PathParam, POST, QueryParam } from "typescript-rest";
|
||||
import { Context, DELETE, GET, Path, PathParam, POST, Security, ServiceContext } from "typescript-rest";
|
||||
import { Widget } from "../../integrations/Widget";
|
||||
import { Cache, CACHE_INTEGRATIONS } from "../../MemoryCache";
|
||||
import { Integration } from "../../integrations/Integration";
|
||||
@ -10,8 +10,7 @@ import { ComplexBot } from "../../integrations/ComplexBot";
|
||||
import { Bridge } from "../../integrations/Bridge";
|
||||
import { BridgeStore } from "../../db/BridgeStore";
|
||||
import { BotStore } from "../../db/BotStore";
|
||||
import AccountController from "../controllers/AccountController";
|
||||
import { AutoWired, Inject } from "typescript-ioc/es6";
|
||||
import { ROLE_MSC_USER } from "../security/MSCSecurity";
|
||||
|
||||
export interface IntegrationsResponse {
|
||||
widgets: Widget[],
|
||||
@ -24,11 +23,10 @@ export interface IntegrationsResponse {
|
||||
* API for managing integrations, primarily for a given room
|
||||
*/
|
||||
@Path("/api/v1/dimension/integrations")
|
||||
@AutoWired
|
||||
export class DimensionIntegrationsService {
|
||||
|
||||
@Inject
|
||||
private accountController: AccountController;
|
||||
@Context
|
||||
private context: ServiceContext;
|
||||
|
||||
/**
|
||||
* Gets a list of widgets
|
||||
@ -90,8 +88,9 @@ export class DimensionIntegrationsService {
|
||||
|
||||
@GET
|
||||
@Path("room/:roomId")
|
||||
public async getIntegrationsInRoom(@QueryParam("scalar_token") scalarToken: string, @PathParam("roomId") roomId: string): Promise<IntegrationsResponse> {
|
||||
const userId = await this.accountController.getTokenOwner(scalarToken);
|
||||
@Security(ROLE_MSC_USER)
|
||||
public async getIntegrationsInRoom(@PathParam("roomId") roomId: string): Promise<IntegrationsResponse> {
|
||||
const userId = this.context.request.user.userId;
|
||||
return {
|
||||
widgets: await DimensionIntegrationsService.getWidgets(true),
|
||||
bots: await DimensionIntegrationsService.getSimpleBots(userId),
|
||||
@ -102,8 +101,9 @@ export class DimensionIntegrationsService {
|
||||
|
||||
@GET
|
||||
@Path("room/:roomId/integrations/:category/:type")
|
||||
public async getIntegrationInRoom(@QueryParam("scalar_token") scalarToken: string, @PathParam("roomId") roomId: string, @PathParam("category") category: string, @PathParam("type") integrationType: string): Promise<any> {
|
||||
const roomConfig = await this.getIntegrationsInRoom(scalarToken, roomId); // does auth for us
|
||||
@Security(ROLE_MSC_USER)
|
||||
public async getIntegrationInRoom(@PathParam("roomId") roomId: string, @PathParam("category") category: string, @PathParam("type") integrationType: string): Promise<any> {
|
||||
const roomConfig = await this.getIntegrationsInRoom(roomId); // does auth for us
|
||||
|
||||
if (category === "widget") return roomConfig.widgets.find(i => i.type === integrationType);
|
||||
else if (category === "bot") return roomConfig.bots.find(i => i.type === integrationType);
|
||||
@ -114,8 +114,9 @@ export class DimensionIntegrationsService {
|
||||
|
||||
@POST
|
||||
@Path("room/:roomId/integrations/:category/:type/config")
|
||||
public async setIntegrationConfigurationInRoom(@QueryParam("scalar_token") scalarToken: string, @PathParam("roomId") roomId: string, @PathParam("category") category: string, @PathParam("type") integrationType: string, newConfig: any): Promise<any> {
|
||||
const userId = await this.accountController.getTokenOwner(scalarToken);
|
||||
@Security(ROLE_MSC_USER)
|
||||
public async setIntegrationConfigurationInRoom(@PathParam("roomId") roomId: string, @PathParam("category") category: string, @PathParam("type") integrationType: string, newConfig: any): Promise<any> {
|
||||
const userId = this.context.request.user.userId;
|
||||
|
||||
if (category === "complex-bot") await NebStore.setComplexBotConfig(userId, integrationType, roomId, newConfig);
|
||||
else if (category === "bridge") await BridgeStore.setBridgeRoomConfig(userId, integrationType, roomId, newConfig);
|
||||
@ -127,8 +128,9 @@ export class DimensionIntegrationsService {
|
||||
|
||||
@DELETE
|
||||
@Path("room/:roomId/integrations/:category/:type")
|
||||
public async removeIntegrationInRoom(@QueryParam("scalar_token") scalarToken: string, @PathParam("roomId") roomId: string, @PathParam("category") category: string, @PathParam("type") integrationType: string): Promise<any> {
|
||||
const userId = await this.accountController.getTokenOwner(scalarToken);
|
||||
@Security(ROLE_MSC_USER)
|
||||
public async removeIntegrationInRoom(@PathParam("roomId") roomId: string, @PathParam("category") category: string, @PathParam("type") integrationType: string): Promise<any> {
|
||||
const userId = this.context.request.user.userId;
|
||||
|
||||
if (category === "widget") throw new ApiError(400, "Widgets should be removed client-side");
|
||||
else if (category === "bot") {
|
||||
|
@ -1,10 +1,9 @@
|
||||
import { GET, Path, PathParam, POST, QueryParam } from "typescript-rest";
|
||||
import { Context, GET, Path, PathParam, POST, Security, ServiceContext } from "typescript-rest";
|
||||
import { LogService } from "matrix-js-snippets";
|
||||
import { IrcBridge } from "../../bridges/IrcBridge";
|
||||
import IrcBridgeRecord from "../../db/models/IrcBridgeRecord";
|
||||
import { ApiError } from "../ApiError";
|
||||
import AccountController from "../controllers/AccountController";
|
||||
import { AutoWired, Inject } from "typescript-ioc/es6";
|
||||
import { ROLE_MSC_USER } from "../security/MSCSecurity";
|
||||
|
||||
interface RequestLinkRequest {
|
||||
op: string;
|
||||
@ -14,17 +13,16 @@ interface RequestLinkRequest {
|
||||
* API for interacting with the IRC bridge
|
||||
*/
|
||||
@Path("/api/v1/dimension/irc")
|
||||
@AutoWired
|
||||
export class DimensionIrcService {
|
||||
|
||||
@Inject
|
||||
private accountController: AccountController;
|
||||
@Context
|
||||
private context: ServiceContext;
|
||||
|
||||
@GET
|
||||
@Path(":networkId/channel/:channel/ops")
|
||||
public async getOps(@QueryParam("scalar_token") scalarToken: string, @PathParam("networkId") networkId: string, @PathParam("channel") channelNoHash: string): Promise<string[]> {
|
||||
const userId = await this.accountController.getTokenOwner(scalarToken);
|
||||
|
||||
@Security(ROLE_MSC_USER)
|
||||
public async getOps(@PathParam("networkId") networkId: string, @PathParam("channel") channelNoHash: string): Promise<string[]> {
|
||||
const userId = this.context.request.user.userId;
|
||||
const parsed = IrcBridge.parseNetworkId(networkId);
|
||||
const bridge = await IrcBridgeRecord.findByPk(parsed.bridgeId);
|
||||
if (!bridge) throw new ApiError(404, "Bridge not found");
|
||||
@ -38,9 +36,9 @@ export class DimensionIrcService {
|
||||
|
||||
@POST
|
||||
@Path(":networkId/channel/:channel/link/:roomId")
|
||||
public async requestLink(@QueryParam("scalar_token") scalarToken: string, @PathParam("networkId") networkId: string, @PathParam("channel") channelNoHash: string, @PathParam("roomId") roomId: string, request: RequestLinkRequest): Promise<any> {
|
||||
const userId = await this.accountController.getTokenOwner(scalarToken);
|
||||
|
||||
@Security(ROLE_MSC_USER)
|
||||
public async requestLink(@PathParam("networkId") networkId: string, @PathParam("channel") channelNoHash: string, @PathParam("roomId") roomId: string, request: RequestLinkRequest): Promise<any> {
|
||||
const userId = this.context.request.user.userId;
|
||||
const parsed = IrcBridge.parseNetworkId(networkId);
|
||||
const bridge = await IrcBridgeRecord.findByPk(parsed.bridgeId);
|
||||
if (!bridge) throw new ApiError(404, "Bridge not found");
|
||||
@ -54,9 +52,9 @@ export class DimensionIrcService {
|
||||
|
||||
@POST
|
||||
@Path(":networkId/channel/:channel/unlink/:roomId")
|
||||
public async unlink(@QueryParam("scalar_token") scalarToken: string, @PathParam("networkId") networkId: string, @PathParam("channel") channelNoHash: string, @PathParam("roomId") roomId: string): Promise<any> {
|
||||
const userId = await this.accountController.getTokenOwner(scalarToken);
|
||||
|
||||
@Security(ROLE_MSC_USER)
|
||||
public async unlink(@PathParam("networkId") networkId: string, @PathParam("channel") channelNoHash: string, @PathParam("roomId") roomId: string): Promise<any> {
|
||||
const userId = this.context.request.user.userId;
|
||||
const parsed = IrcBridge.parseNetworkId(networkId);
|
||||
const bridge = await IrcBridgeRecord.findByPk(parsed.bridgeId);
|
||||
if (!bridge) throw new ApiError(404, "Bridge not found");
|
||||
|
@ -1,10 +1,9 @@
|
||||
import { DELETE, GET, Path, PathParam, POST, QueryParam } from "typescript-rest";
|
||||
import { Context, DELETE, GET, Path, PathParam, POST, Security, ServiceContext } from "typescript-rest";
|
||||
import { ApiError } from "../ApiError";
|
||||
import { LogService } from "matrix-js-snippets";
|
||||
import { BridgedChannel, SlackBridge } from "../../bridges/SlackBridge";
|
||||
import { SlackChannel, SlackTeam } from "../../bridges/models/slack";
|
||||
import { AutoWired, Inject } from "typescript-ioc/es6";
|
||||
import AccountController from "../controllers/AccountController";
|
||||
import { ROLE_MSC_USER } from "../security/MSCSecurity";
|
||||
|
||||
interface BridgeRoomRequest {
|
||||
teamId: string;
|
||||
@ -15,16 +14,16 @@ interface BridgeRoomRequest {
|
||||
* API for interacting with the Slack bridge
|
||||
*/
|
||||
@Path("/api/v1/dimension/slack")
|
||||
@AutoWired
|
||||
export class DimensionSlackService {
|
||||
|
||||
@Inject
|
||||
private accountController: AccountController;
|
||||
@Context
|
||||
private context: ServiceContext;
|
||||
|
||||
@GET
|
||||
@Path("room/:roomId/link")
|
||||
public async getLink(@QueryParam("scalar_token") scalarToken: string, @PathParam("roomId") roomId: string): Promise<BridgedChannel> {
|
||||
const userId = await this.accountController.getTokenOwner(scalarToken);
|
||||
@Security(ROLE_MSC_USER)
|
||||
public async getLink(@PathParam("roomId") roomId: string): Promise<BridgedChannel> {
|
||||
const userId = this.context.request.user.userId;
|
||||
|
||||
try {
|
||||
const slack = new SlackBridge(userId);
|
||||
@ -37,8 +36,9 @@ export class DimensionSlackService {
|
||||
|
||||
@POST
|
||||
@Path("room/:roomId/link")
|
||||
public async bridgeRoom(@QueryParam("scalar_token") scalarToken: string, @PathParam("roomId") roomId: string, request: BridgeRoomRequest): Promise<BridgedChannel> {
|
||||
const userId = await this.accountController.getTokenOwner(scalarToken);
|
||||
@Security(ROLE_MSC_USER)
|
||||
public async bridgeRoom(@PathParam("roomId") roomId: string, request: BridgeRoomRequest): Promise<BridgedChannel> {
|
||||
const userId = this.context.request.user.userId;
|
||||
|
||||
try {
|
||||
const slack = new SlackBridge(userId);
|
||||
@ -52,8 +52,8 @@ export class DimensionSlackService {
|
||||
|
||||
@DELETE
|
||||
@Path("room/:roomId/link")
|
||||
public async unbridgeRoom(@QueryParam("scalar_token") scalarToken: string, @PathParam("roomId") roomId: string): Promise<any> {
|
||||
const userId = await this.accountController.getTokenOwner(scalarToken);
|
||||
public async unbridgeRoom(@PathParam("roomId") roomId: string): Promise<any> {
|
||||
const userId = this.context.request.user.userId;
|
||||
|
||||
try {
|
||||
const slack = new SlackBridge(userId);
|
||||
@ -69,8 +69,9 @@ export class DimensionSlackService {
|
||||
|
||||
@GET
|
||||
@Path("teams")
|
||||
public async getTeams(@QueryParam("scalar_token") scalarToken: string): Promise<SlackTeam[]> {
|
||||
const userId = await this.accountController.getTokenOwner(scalarToken);
|
||||
@Security(ROLE_MSC_USER)
|
||||
public async getTeams(): Promise<SlackTeam[]> {
|
||||
const userId = this.context.request.user.userId;
|
||||
|
||||
const slack = new SlackBridge(userId);
|
||||
const teams = await slack.getTeams();
|
||||
@ -80,8 +81,9 @@ export class DimensionSlackService {
|
||||
|
||||
@GET
|
||||
@Path("teams/:teamId/channels")
|
||||
public async getChannels(@QueryParam("scalar_token") scalarToken: string, @PathParam("teamId") teamId: string): Promise<SlackChannel[]> {
|
||||
const userId = await this.accountController.getTokenOwner(scalarToken);
|
||||
@Security(ROLE_MSC_USER)
|
||||
public async getChannels(@PathParam("teamId") teamId: string): Promise<SlackChannel[]> {
|
||||
const userId = this.context.request.user.userId;
|
||||
|
||||
try {
|
||||
const slack = new SlackBridge(userId);
|
||||
@ -94,8 +96,9 @@ export class DimensionSlackService {
|
||||
|
||||
@GET
|
||||
@Path("auth")
|
||||
public async getAuthUrl(@QueryParam("scalar_token") scalarToken: string): Promise<{ authUrl: string }> {
|
||||
const userId = await this.accountController.getTokenOwner(scalarToken);
|
||||
@Security(ROLE_MSC_USER)
|
||||
public async getAuthUrl(): Promise<{ authUrl: string }> {
|
||||
const userId = this.context.request.user.userId;
|
||||
|
||||
try {
|
||||
const slack = new SlackBridge(userId);
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { GET, Path, PathParam, POST, QueryParam } from "typescript-rest";
|
||||
import { Context, GET, Path, PathParam, POST, Security, ServiceContext } from "typescript-rest";
|
||||
import { Cache, CACHE_STICKERS } from "../../MemoryCache";
|
||||
import StickerPack from "../../db/models/StickerPack";
|
||||
import Sticker from "../../db/models/Sticker";
|
||||
@ -7,8 +7,7 @@ import { ApiError } from "../ApiError";
|
||||
import { StickerpackMetadataDownloader } from "../../utils/StickerpackMetadataDownloader";
|
||||
import { MatrixStickerBot } from "../../matrix/MatrixStickerBot";
|
||||
import config from "../../config";
|
||||
import { AutoWired, Inject } from "typescript-ioc/es6";
|
||||
import AccountController from "../controllers/AccountController";
|
||||
import { ROLE_MSC_USER } from "../security/MSCSecurity";
|
||||
|
||||
export interface MemoryStickerPack {
|
||||
id: number;
|
||||
@ -64,11 +63,10 @@ interface StickerConfig {
|
||||
* API for stickers
|
||||
*/
|
||||
@Path("/api/v1/dimension/stickers")
|
||||
@AutoWired
|
||||
export class DimensionStickerService {
|
||||
|
||||
@Inject
|
||||
private accountController: AccountController;
|
||||
@Context
|
||||
private context: ServiceContext;
|
||||
|
||||
public static async getStickerPacks(enabledOnly = false): Promise<MemoryStickerPack[]> {
|
||||
const cachedPacks = Cache.for(CACHE_STICKERS).get("packs");
|
||||
@ -90,9 +88,8 @@ export class DimensionStickerService {
|
||||
|
||||
@GET
|
||||
@Path("config")
|
||||
public async getConfig(@QueryParam("scalar_token") scalarToken: string): Promise<StickerConfig> {
|
||||
await this.accountController.getTokenOwner(scalarToken);
|
||||
|
||||
@Security(ROLE_MSC_USER)
|
||||
public async getConfig(): Promise<StickerConfig> {
|
||||
return {
|
||||
enabled: config.stickers.enabled,
|
||||
stickerBot: config.stickers.stickerBot,
|
||||
@ -102,9 +99,9 @@ export class DimensionStickerService {
|
||||
|
||||
@GET
|
||||
@Path("packs")
|
||||
public async getStickerPacks(@QueryParam("scalar_token") scalarToken: string): Promise<MemoryStickerPack[]> {
|
||||
const userId = await this.accountController.getTokenOwner(scalarToken);
|
||||
|
||||
@Security(ROLE_MSC_USER)
|
||||
public async getStickerPacks(): Promise<MemoryStickerPack[]> {
|
||||
const userId = this.context.request.user.userId;
|
||||
const cachedPacks = Cache.for(CACHE_STICKERS).get("packs_" + userId);
|
||||
if (cachedPacks) return cachedPacks;
|
||||
|
||||
@ -129,9 +126,9 @@ export class DimensionStickerService {
|
||||
|
||||
@POST
|
||||
@Path("packs/:packId/selected")
|
||||
public async setPackSelected(@QueryParam("scalar_token") scalarToken: string, @PathParam("packId") packId: number, request: SetSelectedRequest): Promise<any> {
|
||||
const userId = await this.accountController.getTokenOwner(scalarToken);
|
||||
|
||||
@Security(ROLE_MSC_USER)
|
||||
public async setPackSelected(@PathParam("packId") packId: number, request: SetSelectedRequest): Promise<any> {
|
||||
const userId = this.context.request.user.userId;
|
||||
const pack = await StickerPack.findByPk(packId);
|
||||
if (!pack) throw new ApiError(404, "Sticker pack not found");
|
||||
|
||||
@ -153,9 +150,8 @@ export class DimensionStickerService {
|
||||
|
||||
@POST
|
||||
@Path("packs/import")
|
||||
public async importPack(@QueryParam("scalar_token") scalarToken: string, request: ImportPackRequest): Promise<MemoryUserStickerPack> {
|
||||
await this.accountController.getTokenOwner(scalarToken);
|
||||
|
||||
@Security(ROLE_MSC_USER)
|
||||
public async importPack(request: ImportPackRequest): Promise<MemoryUserStickerPack> {
|
||||
if (!config.stickers.enabled) {
|
||||
throw new ApiError(400, "Custom stickerpacks are disabled on this homeserver");
|
||||
}
|
||||
@ -171,7 +167,7 @@ export class DimensionStickerService {
|
||||
const pack = stickerPacks[0];
|
||||
|
||||
// Simulate a call to setPackSelected
|
||||
await this.setPackSelected(scalarToken, pack.id, {isSelected: true});
|
||||
await this.setPackSelected(pack.id, {isSelected: true});
|
||||
|
||||
const memoryPack = await DimensionStickerService.packToMemory(pack);
|
||||
return Object.assign({isSelected: true}, memoryPack);
|
||||
|
@ -1,8 +1,7 @@
|
||||
import { DELETE, GET, Path, PathParam, POST, QueryParam } from "typescript-rest";
|
||||
import { Context, DELETE, GET, Path, PathParam, POST, QueryParam, Security, ServiceContext } from "typescript-rest";
|
||||
import { TelegramBridge } from "../../bridges/TelegramBridge";
|
||||
import { ApiError } from "../ApiError";
|
||||
import { AutoWired, Inject } from "typescript-ioc/es6";
|
||||
import AccountController from "../controllers/AccountController";
|
||||
import { ROLE_MSC_USER } from "../security/MSCSecurity";
|
||||
|
||||
interface PortalInfoResponse {
|
||||
bridged: boolean;
|
||||
@ -20,16 +19,16 @@ interface BridgeRoomRequest {
|
||||
* API for interacting with the Telegram bridge
|
||||
*/
|
||||
@Path("/api/v1/dimension/telegram")
|
||||
@AutoWired
|
||||
export class DimensionTelegramService {
|
||||
|
||||
@Inject
|
||||
private accountController: AccountController;
|
||||
@Context
|
||||
private context: ServiceContext;
|
||||
|
||||
@GET
|
||||
@Path("chat/:chatId")
|
||||
public async getPortalInfo(@QueryParam("scalar_token") scalarToken: string, @PathParam("chatId") chatId: number, @QueryParam("roomId") roomId: string): Promise<PortalInfoResponse> {
|
||||
const userId = await this.accountController.getTokenOwner(scalarToken);
|
||||
@Security(ROLE_MSC_USER)
|
||||
public async getPortalInfo(@PathParam("chatId") chatId: number, @QueryParam("roomId") roomId: string): Promise<PortalInfoResponse> {
|
||||
const userId = this.context.request.user.userId;
|
||||
|
||||
try {
|
||||
const telegram = new TelegramBridge(userId);
|
||||
@ -51,8 +50,9 @@ export class DimensionTelegramService {
|
||||
|
||||
@POST
|
||||
@Path("chat/:chatId/room/:roomId")
|
||||
public async bridgeRoom(@QueryParam("scalar_token") scalarToken: string, @PathParam("chatId") chatId: number, @PathParam("roomId") roomId: string, request: BridgeRoomRequest): Promise<PortalInfoResponse> {
|
||||
const userId = await this.accountController.getTokenOwner(scalarToken);
|
||||
@Security(ROLE_MSC_USER)
|
||||
public async bridgeRoom(@PathParam("chatId") chatId: number, @PathParam("roomId") roomId: string, request: BridgeRoomRequest): Promise<PortalInfoResponse> {
|
||||
const userId = this.context.request.user.userId;
|
||||
|
||||
try {
|
||||
const telegram = new TelegramBridge(userId);
|
||||
@ -73,8 +73,9 @@ export class DimensionTelegramService {
|
||||
|
||||
@DELETE
|
||||
@Path("room/:roomId")
|
||||
public async unbridgeRoom(@QueryParam("scalar_token") scalarToken: string, @PathParam("roomId") roomId: string): Promise<PortalInfoResponse> {
|
||||
const userId = await this.accountController.getTokenOwner(scalarToken);
|
||||
@Security(ROLE_MSC_USER)
|
||||
public async unbridgeRoom(@PathParam("roomId") roomId: string): Promise<PortalInfoResponse> {
|
||||
const userId = this.context.request.user.userId;
|
||||
|
||||
try {
|
||||
const telegram = new TelegramBridge(userId);
|
||||
|
@ -1,23 +1,31 @@
|
||||
import { DELETE, FormParam, HeaderParam, Path, PathParam, POST, QueryParam } from "typescript-rest";
|
||||
import {
|
||||
Context,
|
||||
DELETE,
|
||||
FormParam,
|
||||
HeaderParam,
|
||||
Path,
|
||||
PathParam,
|
||||
POST,
|
||||
Security,
|
||||
ServiceContext
|
||||
} from "typescript-rest";
|
||||
import { SuccessResponse, WebhookConfiguration, WebhookOptions } from "../../bridges/models/webhooks";
|
||||
import { WebhooksBridge } from "../../bridges/WebhooksBridge";
|
||||
import Webhook from "../../db/models/Webhook";
|
||||
import { ApiError } from "../ApiError";
|
||||
import { LogService } from "matrix-js-snippets";
|
||||
import * as request from "request";
|
||||
import { AutoWired, Inject } from "typescript-ioc/es6";
|
||||
import AccountController from "../controllers/AccountController";
|
||||
import { ROLE_MSC_USER } from "../security/MSCSecurity";
|
||||
|
||||
/**
|
||||
* API for interacting with the Webhooks bridge, and for setting up proxies to other
|
||||
* services.
|
||||
*/
|
||||
@Path("/api/v1/dimension/webhooks")
|
||||
@AutoWired
|
||||
export class DimensionWebhooksService {
|
||||
|
||||
@Inject
|
||||
private accountController: AccountController;
|
||||
@Context
|
||||
private context: ServiceContext;
|
||||
|
||||
@POST
|
||||
@Path("/travisci/:webhookId")
|
||||
@ -47,27 +55,27 @@ export class DimensionWebhooksService {
|
||||
|
||||
@POST
|
||||
@Path("room/:roomId/webhooks/new")
|
||||
public async newWebhook(@QueryParam("scalar_token") scalarToken: string, @PathParam("roomId") roomId: string, options: WebhookOptions): Promise<WebhookConfiguration> {
|
||||
const userId = await this.accountController.getTokenOwner(scalarToken);
|
||||
|
||||
@Security(ROLE_MSC_USER)
|
||||
public async newWebhook(@PathParam("roomId") roomId: string, options: WebhookOptions): Promise<WebhookConfiguration> {
|
||||
const userId = this.context.request.user.userId;
|
||||
const webhooks = new WebhooksBridge(userId);
|
||||
return webhooks.createWebhook(roomId, options);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("room/:roomId/webhooks/:hookId")
|
||||
public async updateWebhook(@QueryParam("scalar_token") scalarToken: string, @PathParam("roomId") roomId: string, @PathParam("hookId") hookId: string, options: WebhookOptions): Promise<WebhookConfiguration> {
|
||||
const userId = await this.accountController.getTokenOwner(scalarToken);
|
||||
|
||||
@Security(ROLE_MSC_USER)
|
||||
public async updateWebhook(@PathParam("roomId") roomId: string, @PathParam("hookId") hookId: string, options: WebhookOptions): Promise<WebhookConfiguration> {
|
||||
const userId = this.context.request.user.userId;
|
||||
const webhooks = new WebhooksBridge(userId);
|
||||
return webhooks.updateWebhook(roomId, hookId, options);
|
||||
}
|
||||
|
||||
@DELETE
|
||||
@Path("room/:roomId/webhooks/:hookId")
|
||||
public async deleteWebhook(@QueryParam("scalar_token") scalarToken: string, @PathParam("roomId") roomId: string, @PathParam("hookId") hookId: string): Promise<SuccessResponse> {
|
||||
const userId = await this.accountController.getTokenOwner(scalarToken);
|
||||
|
||||
@Security(ROLE_MSC_USER)
|
||||
public async deleteWebhook(@PathParam("roomId") roomId: string, @PathParam("hookId") hookId: string): Promise<SuccessResponse> {
|
||||
const userId = this.context.request.user.userId;
|
||||
const webhooks = new WebhooksBridge(userId);
|
||||
return webhooks.deleteWebhook(roomId, hookId);
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { GET, Path, POST, QueryParam, Security } from "typescript-rest";
|
||||
import { Context, GET, Path, POST, QueryParam, Security, ServiceContext } from "typescript-rest";
|
||||
import { ApiError } from "../ApiError";
|
||||
import { OpenId } from "../../models/OpenId";
|
||||
import { ScalarAccountResponse, ScalarRegisterResponse } from "../../models/ScalarResponses";
|
||||
@ -17,6 +17,9 @@ export class ScalarService {
|
||||
@Inject
|
||||
private accountController: AccountController;
|
||||
|
||||
@Context
|
||||
private context: ServiceContext;
|
||||
|
||||
@POST
|
||||
@Path("register")
|
||||
public async register(request: OpenId, @QueryParam("v") apiVersion: string): Promise<ScalarRegisterResponse> {
|
||||
@ -31,13 +34,12 @@ export class ScalarService {
|
||||
@GET
|
||||
@Path("account")
|
||||
@Security(ROLE_MSC_USER)
|
||||
public async getAccount(@QueryParam("scalar_token") scalarToken: string, @QueryParam("v") apiVersion: string): Promise<ScalarAccountResponse> {
|
||||
public async getAccount(@QueryParam("v") apiVersion: string): Promise<ScalarAccountResponse> {
|
||||
if (apiVersion !== "1.1") {
|
||||
throw new ApiError(401, "Invalid API version.");
|
||||
}
|
||||
|
||||
const userId = await this.accountController.getTokenOwner(scalarToken);
|
||||
return {user_id: userId};
|
||||
return {user_id: this.context.request.user.userId};
|
||||
}
|
||||
|
||||
@GET
|
||||
|
@ -1,11 +1,10 @@
|
||||
import { GET, Path, QueryParam } from "typescript-rest";
|
||||
import { GET, Path, QueryParam, Security } from "typescript-rest";
|
||||
import { LogService } from "matrix-js-snippets";
|
||||
import { Cache, CACHE_WIDGET_TITLES } from "../../MemoryCache";
|
||||
import { MatrixLiteClient } from "../../matrix/MatrixLiteClient";
|
||||
import config from "../../config";
|
||||
import { ROLE_MSC_USER } from "../security/MSCSecurity";
|
||||
import moment = require("moment");
|
||||
import { AutoWired, Inject } from "typescript-ioc/es6";
|
||||
import AccountController from "../controllers/AccountController";
|
||||
|
||||
interface UrlPreviewResponse {
|
||||
cached_response: boolean;
|
||||
@ -23,17 +22,12 @@ interface UrlPreviewResponse {
|
||||
* API for the minimum Scalar API for widget functionality in clients.
|
||||
*/
|
||||
@Path("/api/v1/scalar/widgets")
|
||||
@AutoWired
|
||||
export class ScalarWidgetService {
|
||||
|
||||
@Inject
|
||||
private accountController: AccountController;
|
||||
|
||||
@GET
|
||||
@Path("title_lookup")
|
||||
public async titleLookup(@QueryParam("scalar_token") scalarToken: string, @QueryParam("curl") url: string): Promise<UrlPreviewResponse> {
|
||||
await this.accountController.getTokenOwner(scalarToken);
|
||||
|
||||
@Security(ROLE_MSC_USER)
|
||||
public async titleLookup(@QueryParam("curl") url: string): Promise<UrlPreviewResponse> {
|
||||
const cachedResult = Cache.for(CACHE_WIDGET_TITLES).get(url);
|
||||
if (cachedResult) {
|
||||
cachedResult.cached_response = true;
|
||||
|
@ -5,6 +5,7 @@ import { LogService } from "matrix-js-snippets";
|
||||
import AccountController from "../controllers/AccountController";
|
||||
import TermsController from "../controllers/TermsController";
|
||||
import config from "../../config";
|
||||
import { ScalarStore } from "../../db/ScalarStore";
|
||||
|
||||
export interface IMSCUser {
|
||||
userId: string;
|
||||
@ -22,6 +23,10 @@ const TERMS_IGNORED_ROUTES = [
|
||||
{method: "POST", path: "/_matrix/integrations/v1/logout"},
|
||||
];
|
||||
|
||||
const ADMIN_ROUTES = [
|
||||
{method: "*", path: "/api/v1/dimension/admin/"},
|
||||
];
|
||||
|
||||
export default class MSCSecurity implements ServiceAuthenticator {
|
||||
|
||||
private accountController = new AccountController();
|
||||
@ -62,20 +67,15 @@ export default class MSCSecurity implements ServiceAuthenticator {
|
||||
token: token,
|
||||
};
|
||||
|
||||
let needTerms = true;
|
||||
if (req.method !== "OPTIONS") {
|
||||
for (const route of TERMS_IGNORED_ROUTES) {
|
||||
if (route.method === "*" && req.path.startsWith(route.path)) {
|
||||
needTerms = false;
|
||||
break;
|
||||
}
|
||||
if (route.method === req.method && route.path === req.path) {
|
||||
needTerms = false;
|
||||
break;
|
||||
}
|
||||
const needUpstreams = !this.matchesAnyRoute(req, ADMIN_ROUTES);
|
||||
if (needUpstreams) {
|
||||
const hasUpstreams = await ScalarStore.doesUserHaveTokensForAllUpstreams(req.user.userId);
|
||||
if (!hasUpstreams) {
|
||||
return res.status(401).json({errcode: "M_INVALID_TOKEN", error: "Invalid token"});
|
||||
}
|
||||
} else needTerms = false;
|
||||
}
|
||||
|
||||
const needTerms = !this.matchesAnyRoute(req, TERMS_IGNORED_ROUTES);
|
||||
if (needTerms) {
|
||||
const signatureNeeded = await this.termsController.doesUserNeedToSignTerms(req.user);
|
||||
if (signatureNeeded) {
|
||||
@ -86,14 +86,17 @@ export default class MSCSecurity implements ServiceAuthenticator {
|
||||
}
|
||||
}
|
||||
|
||||
if (this.matchesAnyRoute(req, ADMIN_ROUTES, false) && !this.getRoles(req).includes(ROLE_MSC_ADMIN)) {
|
||||
return res.status(403).json({errcode: "M_UNAUTHORIZED", error: "User is not an admin"});
|
||||
}
|
||||
|
||||
return next();
|
||||
} else {
|
||||
return res.status(401).json({errcode: "M_INVALID_TOKEN", error: "Invalid token"});
|
||||
}
|
||||
} catch (e) {
|
||||
if (e instanceof ApiError) {
|
||||
// TODO: Proper error message
|
||||
res.status(e.statusCode).json({errcode: e.errorCode, error: "Error"});
|
||||
res.status(e.statusCode).json(e.jsonResponse);
|
||||
} else {
|
||||
LogService.error("MSCSecurity", e);
|
||||
res.status(500).json({errcode: "M_UNKNOWN", error: "Unknown server error"});
|
||||
@ -105,4 +108,19 @@ export default class MSCSecurity implements ServiceAuthenticator {
|
||||
public initialize(_router: Router): void {
|
||||
}
|
||||
|
||||
private matchesAnyRoute(req: Request, routes: { method: string, path: string }[], valForOptions = true): boolean {
|
||||
if (req.method === 'OPTIONS') return valForOptions;
|
||||
|
||||
for (const route of routes) {
|
||||
if (route.method === '*' && req.path.startsWith(route.path)) {
|
||||
return true;
|
||||
}
|
||||
if (route.method === req.method && route.path === req.path) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
@ -4,7 +4,7 @@ import Upstream from "./models/Upstream";
|
||||
import User from "./models/User";
|
||||
import { MatrixStickerBot } from "../matrix/MatrixStickerBot";
|
||||
import { ScalarClient } from "../scalar/ScalarClient";
|
||||
import { CACHE_SCALAR_ONLINE_STATE, Cache } from "../MemoryCache";
|
||||
import { Cache, CACHE_SCALAR_ONLINE_STATE } from "../MemoryCache";
|
||||
|
||||
export class ScalarStore {
|
||||
|
||||
@ -33,21 +33,14 @@ export class ScalarStore {
|
||||
return true;
|
||||
}
|
||||
|
||||
public static async getTokenOwner(scalarToken: string, ignoreUpstreams = false): Promise<User> {
|
||||
public static async getTokenOwner(scalarToken: string): Promise<User> {
|
||||
const tokens = await UserScalarToken.findAll({
|
||||
where: {isDimensionToken: true, scalarToken: scalarToken},
|
||||
include: [User]
|
||||
});
|
||||
if (!tokens || tokens.length === 0) throw new Error("Invalid token");
|
||||
|
||||
const user = tokens[0].user;
|
||||
if (ignoreUpstreams) return user; // skip upstreams check
|
||||
|
||||
const hasAllTokens = await ScalarStore.doesUserHaveTokensForAllUpstreams(user.userId);
|
||||
if (!hasAllTokens) {
|
||||
throw new Error("Invalid token"); // They are missing an upstream, so we'll lie and say they are not authorized
|
||||
}
|
||||
return user;
|
||||
return tokens[0].user;
|
||||
}
|
||||
|
||||
public static async isUpstreamOnline(upstream: Upstream): Promise<boolean> {
|
||||
|
Loading…
Reference in New Issue
Block a user