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