Support explicit setting of the federation/client URLs

The client/server URL is needed for go-neb to work correctly, and to remove our complete dependence on federation.

The federation URL is also configurable so servers that don't wish to federate can specify a local address.
This commit is contained in:
Travis Ralston 2018-03-24 17:09:34 -06:00
parent cc921779ae
commit 8cdae3359d
13 changed files with 82 additions and 34 deletions

View File

@ -3,9 +3,23 @@ web:
port: 8184
address: '0.0.0.0'
# Homeserver configuration (used to proxy some requests to the homeserver for processing)
# Homeserver configuration
homeserver:
# The domain name of the homeserver. This is used in many places, such as with go-neb
# setups, to identify the homeserver.
name: "t2bot.io"
# The URL that Dimension, go-neb, and other services provisioned by Dimension should
# use to access the homeserver with.
clientServerUrl: "https://t2bot.io"
# The URL that Dimension should use when trying to communicate with federated APIs on
# the homeserver. If not supplied or left empty Dimension will try to resolve the address
# through the normal federation process.
#federationUrl: "https://t2bot.io:8448"
# The access token Dimension should use for miscellaneous access to the homeserver. This
# should be for a valid user.
accessToken: "something"
# These users can modify the integrations this Dimension supports.
@ -16,10 +30,10 @@ admins:
# IPs and CIDR ranges listed here will be blocked from being widgets.
# Note: Widgets may still be embedded with restricted content, although not through Dimension directly.
widgetBlacklist:
- 10.0.0.0/8
- 172.16.0.0/12
- 192.168.0.0/16
- 127.0.0.0/8
- 10.0.0.0/8
- 172.16.0.0/12
- 192.168.0.0/16
- 127.0.0.0/8
# Where the database for Dimension is
database:

View File

@ -4,7 +4,6 @@ import AppService from "../../db/models/AppService";
import { AppserviceStore } from "../../db/AppserviceStore";
import { ApiError } from "../ApiError";
import { MatrixAppserviceClient } from "../../matrix/MatrixAppserviceClient";
import config from "../../config";
import { LogService } from "matrix-js-snippets";
interface AppserviceResponse {
@ -67,7 +66,7 @@ export class AdminAppserviceService {
await AdminService.validateAndGetAdminTokenOwner(scalarToken);
const appservice = await AppserviceStore.getAppservice(asId);
const client = new MatrixAppserviceClient(config.homeserver.name, appservice);
const client = new MatrixAppserviceClient(appservice);
const userId = await client.whoAmI();
if (userId.startsWith("@" + appservice.userPrefix)) return {}; // 200 OK

View File

@ -4,6 +4,7 @@ import config from "../../config";
import { ApiError } from "../ApiError";
import { MatrixLiteClient } from "../../matrix/MatrixLiteClient";
import { CURRENT_VERSION } from "../../version";
import { getFederationUrl } from "../../matrix/helpers";
interface DimensionVersionResponse {
version: string;
@ -16,6 +17,7 @@ interface DimensionConfigResponse {
name: string;
userId: string;
federationUrl: string;
clientServerUrl: string;
};
}
@ -52,14 +54,15 @@ export class AdminService {
public async getConfig(@QueryParam("scalar_token") scalarToken: string): Promise<DimensionConfigResponse> {
await AdminService.validateAndGetAdminTokenOwner(scalarToken);
const client = new MatrixLiteClient(config.homeserver.name, config.homeserver.accessToken);
const client = new MatrixLiteClient(config.homeserver.accessToken);
return {
admins: config.admins,
widgetBlacklist: config.widgetBlacklist,
homeserver: {
name: config.homeserver.name,
userId: await client.whoAmI(),
federationUrl: await client.getFederationUrl(),
federationUrl: await getFederationUrl(config.homeserver.name),
clientServerUrl: config.homeserver.clientServerUrl,
},
};
}

View File

@ -32,7 +32,7 @@ export class ScalarWidgetService {
return cachedResult;
}
const client = new MatrixLiteClient(config.homeserver.name, config.homeserver.accessToken);
const client = new MatrixLiteClient(config.homeserver.accessToken);
try {
const preview = await client.getUrlPreview(url);

View File

@ -9,6 +9,8 @@ export interface DimensionConfig {
homeserver: {
name: string;
accessToken: string;
clientServerUrl: string;
federationUrl: string;
};
widgetBlacklist: string[];
database: {

View File

@ -2,7 +2,6 @@ import AppService from "./models/AppService";
import AppServiceUser from "./models/AppServiceUser";
import * as randomString from "random-string";
import { MatrixAppserviceClient } from "../matrix/MatrixAppserviceClient";
import config from "../config";
export class AppserviceStore {
@ -49,7 +48,7 @@ export class AppserviceStore {
const appservice = await AppService.findOne({where: {id: appserviceId}});
if (!appservice) throw new Error("Appservice not found");
const client = new MatrixAppserviceClient(config.homeserver.name, appservice);
const client = new MatrixAppserviceClient(appservice);
const localpart = AppserviceStore.getSafeUserId(userId.substring(1).split(":")[0]);
const response = await client.registerUser(localpart);

View File

@ -1,4 +1,4 @@
import { doFederatedApiCall } from "./helpers";
import { doClientApiCall } from "./helpers";
import AppService from "../db/models/AppService";
export interface MatrixUserResponse {
@ -10,13 +10,12 @@ export interface MatrixUserResponse {
export class MatrixAppserviceClient {
constructor(private homeserverName: string, private appservice: AppService) {
constructor(private appservice: AppService) {
}
public async registerUser(localpart: string): Promise<MatrixUserResponse> {
return doFederatedApiCall(
return doClientApiCall(
"POST",
this.homeserverName,
"/_matrix/client/r0/register",
{access_token: this.appservice.asToken},
{type: "m.login.application_service", username: localpart},
@ -24,9 +23,8 @@ export class MatrixAppserviceClient {
}
public async whoAmI(): Promise<string> {
const response = await doFederatedApiCall(
const response = await doClientApiCall(
"GET",
this.homeserverName,
"/_matrix/client/r0/account/whoami",
{access_token: this.appservice.asToken},
);

View File

@ -1,4 +1,4 @@
import { doFederatedApiCall, getFederationUrl as getFedUrl } from "./helpers";
import { doClientApiCall } from "./helpers";
export interface MatrixUrlPreview {
// This is really the only parameter we care about
@ -7,26 +7,20 @@ export interface MatrixUrlPreview {
export class MatrixLiteClient {
constructor(private homeserverName: string, private accessToken: string) {
}
public async getFederationUrl(): Promise<string> {
return getFedUrl(this.homeserverName);
constructor(private accessToken: string) {
}
public async getUrlPreview(url: string): Promise<MatrixUrlPreview> {
return doFederatedApiCall(
return doClientApiCall(
"GET",
this.homeserverName,
"/_matrix/media/r0/preview_url",
{access_token: this.accessToken, url: url}
);
}
public async whoAmI(): Promise<string> {
const response = await doFederatedApiCall(
const response = await doClientApiCall(
"GET",
this.homeserverName,
"/_matrix/client/r0/account/whoami",
{access_token: this.accessToken}
);

View File

@ -7,6 +7,8 @@ export class MatrixOpenIdClient {
}
public async getUserId(): Promise<string> {
// TODO: Implement/prefer https://github.com/matrix-org/matrix-doc/issues/1115
// #1115 also means this should become a client API call, not a federated one (finally)
const response = await doFederatedApiCall(
"GET",
this.openId.matrix_server_name,

View File

@ -2,6 +2,7 @@ import * as dns from "dns-then";
import { LogService } from "matrix-js-snippets";
import { Cache, CACHE_FEDERATION } from "../MemoryCache";
import * as request from "request";
import config from "../config";
export async function getFederationUrl(serverName: string): Promise<string> {
const cachedUrl = Cache.for(CACHE_FEDERATION).get(serverName);
@ -10,6 +11,17 @@ export async function getFederationUrl(serverName: string): Promise<string> {
return cachedUrl;
}
if (serverName === config.homeserver.name && config.homeserver.federationUrl) {
let url = config.homeserver.federationUrl;
if (url.endsWith("/")) {
url = url.substring(0, url.length - 1);
}
LogService.info("matrix", "Using configured federation URL for " + serverName);
Cache.for(CACHE_FEDERATION).put(serverName, url);
return url;
}
let serverUrl = null;
let expirationMs = 4 * 60 * 60 * 1000; // default is 4 hours
@ -49,7 +61,33 @@ export async function doFederatedApiCall(method: string, serverName: string, end
LogService.error("matrix", err);
reject(err);
} else if (res.statusCode !== 200) {
LogService.error("matrix", "Got status code " + res.statusCode + " while calling " + endpoint);
LogService.error("matrix", "Got status code " + res.statusCode + " while calling federated endpoint " + endpoint);
reject(new Error("Error in request: invalid status code"));
} else {
if (typeof(res.body) === "string") res.body = JSON.parse(res.body);
resolve(res.body);
}
});
});
}
export async function doClientApiCall(method: string, endpoint: string, query?: object, body?: object): Promise<any> {
let url = config.homeserver.clientServerUrl;
if (url.endsWith("/")) url = url.substring(0, url.length - 1);
return new Promise((resolve, reject) => {
request({
method: method,
url: url + endpoint,
qs: query,
json: body,
}, (err, res, _body) => {
if (err) {
LogService.error("matrix", "Error calling " + endpoint);
LogService.error("matrix", err);
reject(err);
} else if (res.statusCode !== 200) {
LogService.error("matrix", "Got status code " + res.statusCode + " while calling client endpoint " + endpoint);
reject(new Error("Error in request: invalid status code"));
} else {
if (typeof(res.body) === "string") res.body = JSON.parse(res.body);

View File

@ -2,7 +2,6 @@ import { NebConfig } from "../models/neb";
import { AppserviceStore } from "../db/AppserviceStore";
import { Client } from "./models/client";
import config from "../config";
import { getFederationUrl } from "../matrix/helpers";
import { LogService } from "matrix-js-snippets";
import { Service } from "./models/service";
import * as request from "request";
@ -19,7 +18,7 @@ export class NebClient {
public async updateUser(userId: string, isEnabled: boolean, sync = true, autoAcceptInvites = true): Promise<any> {
const nebRequest: Client = {
UserID: userId,
HomeserverURL: await getFederationUrl(config.homeserver.name),
HomeserverURL: config.homeserver.clientServerUrl,
AccessToken: (isEnabled ? await this.getAccessToken(userId) : "DISABLED"),
Sync: isEnabled ? sync : false,
AutoJoinRooms: autoAcceptInvites

View File

@ -23,9 +23,8 @@
<strong>Homeserver</strong><br />
Name: {{ config.homeserver.name }}<br />
Federation URL: {{ config.homeserver.federationUrl }}<br />
<!-- Dimension makes no distinction for the csapi -->
Client/Server URL: {{ config.homeserver.federationUrl }}<br />
User ID: {{ config.homeserver.userId }}
Client/Server URL: {{ config.homeserver.clientServerUrl }}<br />
Utility User ID: {{ config.homeserver.userId }}
</div>
</div>
</div>

View File

@ -7,6 +7,7 @@ export interface FE_DimensionConfig {
name: string;
userId: string;
federationUrl: string;
clientServerUrl: string;
};
}