Assume upstream tokens are valid if they are offline

This commit is contained in:
Travis Ralston 2019-04-13 16:58:20 -06:00
parent 52f3c72db6
commit 8cd5067e73
5 changed files with 116 additions and 1 deletions

View File

@ -45,6 +45,7 @@ export const CACHE_INTEGRATIONS = "integrations";
export const CACHE_NEB = "neb";
export const CACHE_UPSTREAM = "upstream";
export const CACHE_SCALAR_ACCOUNTS = "scalar-accounts";
export const CACHE_SCALAR_ONLINE_STATE = "scalar-online-state";
export const CACHE_WIDGET_TITLES = "widget-titles";
export const CACHE_FEDERATION = "federation";
export const CACHE_IRC_BRIDGE = "irc-bridge";

View File

@ -2,6 +2,9 @@ import UserScalarToken from "./models/UserScalarToken";
import { LogService } from "matrix-js-snippets";
import Upstream from "./models/Upstream";
import User from "./models/User";
import { MatrixStickerBot } from "../matrix/MatrixStickerBot";
import { ScalarClient } from "../scalar/ScalarClient";
import { CACHE_SCALAR_ONLINE_STATE, Cache } from "../MemoryCache";
export class ScalarStore {
@ -17,6 +20,10 @@ export class ScalarStore {
const upstreams = await Upstream.findAll();
for (const upstream of upstreams) {
if (!await ScalarStore.isUpstreamOnline(upstream)) {
LogService.warn("ScalarStore", `Upstream ${upstream.apiUrl} is offline - assuming token is valid`);
continue;
}
if (upstreamTokenIds.indexOf(upstream.id) === -1) {
LogService.warn("ScalarStore", "user " + userId + " is missing a scalar token for upstream " + upstream.id + " (" + upstream.name + ")");
return false;
@ -43,6 +50,76 @@ export class ScalarStore {
return user;
}
public static async isUpstreamOnline(upstream: Upstream): Promise<boolean> {
const cache = Cache.for(CACHE_SCALAR_ONLINE_STATE);
const cacheKey = `Upstream ${upstream.id}`;
const result = cache.get(cacheKey);
if (typeof (result) === 'boolean') {
return result;
}
const state = ScalarStore.checkIfUpstreamOnline(upstream);
cache.put(cacheKey, state, 60 * 60 * 1000); // 1 hour
return state;
}
private static async checkIfUpstreamOnline(upstream: Upstream): Promise<boolean> {
try {
// The sticker bot can be used for this for now
const testUserId = await MatrixStickerBot.getUserId();
const scalarClient = new ScalarClient(upstream);
// First see if we have a token for the upstream so we can try it
const existingTokens = await UserScalarToken.findAll({
where: {isDimensionToken: false, userId: testUserId, upstreamId: upstream.id},
});
if (existingTokens && existingTokens.length) {
// Test that the token works
try {
const result = await scalarClient.getAccount(existingTokens[0].scalarToken);
if (result.user_id !== testUserId) {
// noinspection ExceptionCaughtLocallyJS
throw new Error(`Unexpected error: Upstream ${upstream.id} did not return account info for the right token`);
}
return true; // it's online
} catch (e) {
LogService.error("ScalarStore", e);
if (!isNaN(Number(e))) {
if (e === 401 || e === 403) {
LogService.info("ScalarStore", "Test user token expired");
} else {
// Assume offline
return false;
}
}
}
}
// If we're here, we need to register a new token
if (existingTokens && existingTokens.length) {
for (const token of existingTokens) {
await token.destroy();
}
}
const openId = await MatrixStickerBot.getOpenId();
const token = await scalarClient.register(openId);
await UserScalarToken.create({
userId: testUserId,
scalarToken: token.scalar_token,
isDimensionToken: false,
upstreamId: upstream.id,
});
return true;
} catch (e) {
LogService.error("ScalarStore", e);
return false;
}
}
private constructor() {
}
}

View File

@ -2,6 +2,7 @@ import { doClientApiCall } from "./helpers";
import config from "../config";
import * as request from "request";
import { LogService } from "matrix-js-snippets";
import { OpenId } from "../models/OpenId";
export interface MatrixUrlPreview {
// This is really the only parameter we care about
@ -44,6 +45,14 @@ export class MatrixLiteClient {
return response['user_id'];
}
public async getOpenId(): Promise<OpenId> {
return await doClientApiCall(
"POST",
`/_matrix/client/r0/user/${await this.whoAmI()}/openid/request_token`,
{access_token: this.accessToken},
);
}
public async leaveRoom(roomId: string): Promise<string> {
return doClientApiCall(
"POST",

View File

@ -11,6 +11,7 @@ import Sticker from "../db/models/Sticker";
import { MatrixLiteClient } from "./MatrixLiteClient";
import { Cache, CACHE_STICKERS } from "../MemoryCache";
import { LicenseMap } from "../utils/LicenseMap";
import { OpenId } from "../models/OpenId";
class _MatrixStickerBot {
@ -36,6 +37,11 @@ class _MatrixStickerBot {
return this.client.getUserId();
}
public getOpenId(): Promise<OpenId> {
const liteClient = new MatrixLiteClient(config.homeserver.accessToken);
return liteClient.getOpenId();
}
private async onEvent(roomId, event) {
LogService.info("MatrixStickerBot", `Event ${event.type} in ${roomId}`);
if (event.type !== "io.t2bot.stickers.metadata" || event.state_key !== "") return;

View File

@ -1,5 +1,5 @@
import { OpenId } from "../models/OpenId";
import { ScalarRegisterResponse } from "../models/ScalarResponses";
import { ScalarAccountResponse, ScalarRegisterResponse } from "../models/ScalarResponses";
import * as request from "request";
import { LogService } from "matrix-js-snippets";
import Upstream from "../db/models/Upstream";
@ -31,4 +31,26 @@ export class ScalarClient {
});
});
}
public getAccount(token: string): Promise<ScalarAccountResponse> {
LogService.info("ScalarClient", "Doing upstream scalar request: " + this.upstream.scalarUrl + "/account");
return new Promise((resolve, reject) => {
request({
method: "GET",
url: this.upstream.scalarUrl + "/account",
qs: {v: SCALAR_API_VERSION, scalar_token: token},
}, (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);
}
});
});
}
}