Use the right scalar route for the request

This commit is contained in:
Travis Ralston 2019-07-10 21:16:04 -06:00
parent cf212d8f4e
commit 18ee47fa49
7 changed files with 92 additions and 19 deletions

View File

@ -51,9 +51,10 @@ export default class AccountController {
/**
* Registers an account to use the Integration Manager
* @param {OpenId} openId The OpenID request information.
* @param {string} scalarKind The kind of scalar client to use.
* @returns {Promise<IAccountRegisteredResponse>} Resolves when registered.
*/
public async registerAccount(openId: OpenId): Promise<IAccountRegisteredResponse> {
public async registerAccount(openId: OpenId, scalarKind: string): Promise<IAccountRegisteredResponse> {
if (!openId || !openId.matrix_server_name || !openId.access_token) {
throw new ApiError(400, "Missing OpenID information");
}
@ -76,7 +77,7 @@ export default class AccountController {
const upstreams = await Upstream.findAll();
await Promise.all(upstreams.map(async upstream => {
if (!await ScalarStore.isUpstreamOnline(upstream)) {
if (!await ScalarStore.isUpstreamOnline(upstream, scalarKind)) {
LogService.warn("AccountController", `Skipping registration for ${mxUserId} on upstream ${upstream.id} (${upstream.name}) because it is offline`);
return null;
}

View File

@ -3,6 +3,7 @@ import { OpenId } from "../../models/OpenId";
import AccountController, { IAccountInfoResponse, IAccountRegisteredResponse } from "../controllers/AccountController";
import { AutoWired, Inject } from "typescript-ioc/es6";
import { IMSCUser, ROLE_MSC_USER } from "../security/MSCSecurity";
import { ScalarClient } from "../../scalar/ScalarClient";
/**
* API for account management
@ -20,7 +21,7 @@ export class MSCAccountService {
@POST
@Path("register")
public async register(request: OpenId): Promise<IAccountRegisteredResponse> {
return this.accountController.registerAccount(request);
return this.accountController.registerAccount(request, ScalarClient.KIND_MATRIX_V1);
}
@GET

View File

@ -7,6 +7,7 @@ import AccountController from "../controllers/AccountController";
import { ROLE_MSC_USER } from "../security/MSCSecurity";
import TermsController, { ITermsNotSignedResponse } from "../controllers/TermsController";
import { SignTermsRequest } from "../msc/MSCTermsService";
import { ScalarClient } from "../../scalar/ScalarClient";
/**
* API for the minimum Scalar API we need to implement to be compatible with clients. Used for registration
@ -32,7 +33,7 @@ export class ScalarService {
throw new ApiError(401, "Invalid API version.");
}
const response = await this.accountController.registerAccount(request);
const response = await this.accountController.registerAccount(request, ScalarClient.KIND_LEGACY);
return {scalar_token: response.token};
}

View File

@ -6,6 +6,7 @@ import AccountController from "../controllers/AccountController";
import TermsController from "../controllers/TermsController";
import config from "../../config";
import { ScalarStore } from "../../db/ScalarStore";
import { ScalarClient } from "../../scalar/ScalarClient";
export interface IMSCUser {
userId: string;
@ -74,7 +75,8 @@ export default class MSCSecurity implements ServiceAuthenticator {
const needUpstreams = !this.matchesAnyRoute(req, ADMIN_ROUTES);
if (needUpstreams) {
const hasUpstreams = await ScalarStore.doesUserHaveTokensForAllUpstreams(req.user.userId);
const scalarKind = req.path.startsWith("/_matrix/integrations/v1/") ? ScalarClient.KIND_MATRIX_V1 : ScalarClient.KIND_LEGACY;
const hasUpstreams = await ScalarStore.doesUserHaveTokensForAllUpstreams(req.user.userId, scalarKind);
if (!hasUpstreams) {
return res.status(401).json({errcode: "M_INVALID_TOKEN", error: "Invalid token"});
}

View File

@ -8,7 +8,7 @@ import { Cache, CACHE_SCALAR_ONLINE_STATE } from "../MemoryCache";
export class ScalarStore {
public static async doesUserHaveTokensForAllUpstreams(userId: string): Promise<boolean> {
public static async doesUserHaveTokensForAllUpstreams(userId: string, scalarKind: string): Promise<boolean> {
const scalarTokens = await UserScalarToken.findAll({where: {userId: userId}});
const upstreamTokenIds = scalarTokens.filter(t => !t.isDimensionToken).map(t => t.upstreamId);
const hasDimensionToken = scalarTokens.filter(t => t.isDimensionToken).length >= 1;
@ -20,7 +20,7 @@ export class ScalarStore {
const upstreams = await Upstream.findAll();
for (const upstream of upstreams) {
if (!await ScalarStore.isUpstreamOnline(upstream)) {
if (!await ScalarStore.isUpstreamOnline(upstream, scalarKind)) {
LogService.warn("ScalarStore", `Upstream ${upstream.apiUrl} is offline - assuming token is valid`);
continue;
}
@ -43,7 +43,7 @@ export class ScalarStore {
return tokens[0].user;
}
public static async isUpstreamOnline(upstream: Upstream): Promise<boolean> {
public static async isUpstreamOnline(upstream: Upstream, scalarKind: string): Promise<boolean> {
const cache = Cache.for(CACHE_SCALAR_ONLINE_STATE);
const cacheKey = `Upstream ${upstream.id}`;
const result = cache.get(cacheKey);
@ -51,17 +51,17 @@ export class ScalarStore {
return result;
}
const state = ScalarStore.checkIfUpstreamOnline(upstream);
const state = ScalarStore.checkIfUpstreamOnline(upstream, scalarKind);
cache.put(cacheKey, state, 60 * 60 * 1000); // 1 hour
return state;
}
private static async checkIfUpstreamOnline(upstream: Upstream): Promise<boolean> {
private static async checkIfUpstreamOnline(upstream: Upstream, scalarKind: string): Promise<boolean> {
try {
// The sticker bot can be used for this for now
const testUserId = await MatrixStickerBot.getUserId();
const scalarClient = new ScalarClient(upstream);
const scalarClient = new ScalarClient(upstream, scalarKind);
// First see if we have a token for the upstream so we can try it
const existingTokens = await UserScalarToken.findAll({

View File

@ -5,4 +5,8 @@ export interface ScalarRegisterResponse {
export interface ScalarAccountResponse {
user_id: string;
// credit: number; // present on scalar-web
}
export interface ScalarLogoutResponse {
// Nothing of interest
}

View File

@ -1,21 +1,58 @@
import { OpenId } from "../models/OpenId";
import { ScalarAccountResponse, ScalarRegisterResponse } from "../models/ScalarResponses";
import { ScalarAccountResponse, ScalarLogoutResponse, ScalarRegisterResponse } from "../models/ScalarResponses";
import * as request from "request";
import { LogService } from "matrix-js-snippets";
import Upstream from "../db/models/Upstream";
import { SCALAR_API_VERSION } from "../utils/common-constants";
import * as url from "url";
const REGISTER_ROUTE = "/register";
const ACCOUNT_INFO_ROUTE = "/account";
const LOGOUT_ROUTE = "/logout";
export class ScalarClient {
constructor(private upstream: Upstream) {
public static readonly KIND_LEGACY = "legacy";
public static readonly KIND_MATRIX_V1 = "matrix_v1";
constructor(private upstream: Upstream, private kind = ScalarClient.KIND_LEGACY) {
}
private makeRequestArguments(path: string, token: string): { scalarUrl: string, headers: any, queryString: any } {
if (this.kind === ScalarClient.KIND_LEGACY) {
const addlQuery = {};
if (token) addlQuery['scalar_token'] = token;
return {
scalarUrl: this.upstream.scalarUrl + path,
headers: {},
queryString: {
v: SCALAR_API_VERSION,
...addlQuery,
},
};
} else {
const parsed = url.parse(this.upstream.scalarUrl);
parsed.path = '/_matrix/integrations/v1' + (path === ACCOUNT_INFO_ROUTE ? path : `${ACCOUNT_INFO_ROUTE}${path}`);
const headers = {};
if (token) headers['Authorization'] = `Bearer ${token}`;
return {
scalarUrl: parsed.toString(),
headers: headers,
queryString: {},
};
}
}
public register(openId: OpenId): Promise<ScalarRegisterResponse> {
LogService.info("ScalarClient", "Doing upstream scalar request: " + this.upstream.scalarUrl + "/register");
const {scalarUrl, headers, queryString} = this.makeRequestArguments(REGISTER_ROUTE, null);
LogService.info("ScalarClient", "Doing upstream scalar request: " + scalarUrl);
return new Promise((resolve, reject) => {
request({
method: "POST",
url: this.upstream.scalarUrl + "/register",
qs: {v: SCALAR_API_VERSION},
url: scalarUrl,
qs: queryString,
headers: headers,
json: openId,
}, (err, res, _body) => {
if (err) {
@ -33,12 +70,39 @@ export class ScalarClient {
}
public getAccount(token: string): Promise<ScalarAccountResponse> {
LogService.info("ScalarClient", "Doing upstream scalar request: " + this.upstream.scalarUrl + "/account");
const {scalarUrl, headers, queryString} = this.makeRequestArguments(ACCOUNT_INFO_ROUTE, token);
LogService.info("ScalarClient", "Doing upstream scalar request: " + scalarUrl);
return new Promise((resolve, reject) => {
request({
method: "GET",
url: this.upstream.scalarUrl + "/account",
qs: {v: SCALAR_API_VERSION, scalar_token: token},
url: scalarUrl,
qs: queryString,
headers: headers,
json: true,
}, (err, res, _body) => {
if (err) {
LogService.error("ScalarClient", "Error getting information for token");
LogService.error("ScalarClient", err);
reject(err);
} else if (res.statusCode !== 200) {
LogService.error("ScalarClient", "Got status code " + res.statusCode + " while getting information for token");
reject(res.statusCode);
} else {
resolve(res.body);
}
});
});
}
public logout(token: string): Promise<ScalarLogoutResponse> {
const {scalarUrl, headers, queryString} = this.makeRequestArguments(LOGOUT_ROUTE, token);
LogService.info("ScalarClient", "Doing upstream scalar request: " + scalarUrl);
return new Promise((resolve, reject) => {
request({
method: "POST",
url: scalarUrl,
qs: queryString,
headers: headers,
json: true,
}, (err, res, _body) => {
if (err) {