mirror of
https://github.com/turt2live/matrix-dimension.git
synced 2024-10-01 05:05:53 +00:00
Make terms of service endpoint match MSC
This commit is contained in:
parent
9cc1454527
commit
e006851465
@ -24,7 +24,7 @@ export interface IPolicy {
|
||||
[language: string]: string | ILanguagePolicy;
|
||||
}
|
||||
|
||||
export interface ITermsNotSignedResponse {
|
||||
export interface ITermsResponse {
|
||||
policies: { [policyName: string]: IPolicy };
|
||||
}
|
||||
|
||||
@ -87,40 +87,57 @@ export default class TermsController {
|
||||
}
|
||||
|
||||
public async doesUserNeedToSignTerms(user: ILoggedInUser): Promise<boolean> {
|
||||
return Object.keys((await this.getMissingTermsForUser(user)).policies).length > 0;
|
||||
}
|
||||
|
||||
public async getMissingTermsForUser(user: ILoggedInUser): Promise<ITermsNotSignedResponse> {
|
||||
const latest = await this.getPublishedTerms();
|
||||
const signed = await TermsSignedRecord.findAll({where: {userId: user.userId}});
|
||||
|
||||
const missing = Object.values(latest).filter(d => !signed.find(s => s.termsId === d.id));
|
||||
const policies: ITermsNotSignedResponse = {policies: {}};
|
||||
if (missing.length > 0) return true;
|
||||
|
||||
for (const missingPolicy of missing) {
|
||||
policies.policies[missingPolicy.shortcode] = {
|
||||
version: missingPolicy.version,
|
||||
};
|
||||
|
||||
for (const language in missingPolicy.languages) {
|
||||
policies.policies[missingPolicy.shortcode][language] = {
|
||||
name: missingPolicy.languages[language].name,
|
||||
url: missingPolicy.languages[language].url,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Get upstream terms for the user
|
||||
// Test upstream terms for the user
|
||||
const tokensForUser = await UserScalarToken.findAll({where: {userId: user.userId}, include: [Upstream]});
|
||||
const upstreamTokens = tokensForUser.filter(t => t.upstream);
|
||||
const urlsToUpstream = {}; // {url: [upstreamId]}
|
||||
for (const upstreamToken of upstreamTokens) {
|
||||
try {
|
||||
const scalarClient = new ScalarClient(upstreamToken.upstream, ScalarClient.KIND_MATRIX_V1);
|
||||
const upstreamTerms = await scalarClient.getMissingTerms(upstreamToken.scalarToken);
|
||||
await scalarClient.getAccount(upstreamToken.scalarToken);
|
||||
// 200 OK means we're fine
|
||||
} catch (e) {
|
||||
if (e.statusCode === 403 && e.body && e.body.errcode === 'M_TERMS_NOT_SIGNED') {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public async getAvailableTerms(): Promise<ITermsResponse> {
|
||||
const latest = await this.getPublishedTerms();
|
||||
const policies: ITermsResponse = {policies: {}};
|
||||
|
||||
for (const termsPolicy of Object.values(latest)) {
|
||||
policies.policies[termsPolicy.shortcode] = {
|
||||
version: termsPolicy.version,
|
||||
};
|
||||
|
||||
for (const language in termsPolicy.languages) {
|
||||
policies.policies[termsPolicy.shortcode][language] = {
|
||||
name: termsPolicy.languages[language].name,
|
||||
url: termsPolicy.languages[language].url,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Get upstream terms
|
||||
const usptreams = await Upstream.findAll();
|
||||
const urlsToUpstream = {}; // {url: [upstreamId]}
|
||||
for (const upstream of usptreams) {
|
||||
try {
|
||||
const scalarClient = new ScalarClient(upstream, ScalarClient.KIND_MATRIX_V1);
|
||||
const upstreamTerms = await scalarClient.getAvailableTerms();
|
||||
|
||||
// rewrite the shortcodes to avoid conflicts
|
||||
const shortcodePrefix = `upstream_${md5(`${upstreamToken.id}:${upstreamToken.upstream.apiUrl}`)}`;
|
||||
const shortcodePrefix = `upstream_${md5(`${upstream.id}:${upstream.apiUrl}`)}`;
|
||||
for (const shortcode of Object.keys(upstreamTerms.policies)) {
|
||||
policies.policies[`${shortcodePrefix}_${shortcode}`] = upstreamTerms.policies[shortcode];
|
||||
|
||||
@ -129,8 +146,8 @@ export default class TermsController {
|
||||
const upstreamUrl = upstreamTerms.policies[shortcode][language]['url'];
|
||||
if (!urlsToUpstream[upstreamUrl]) urlsToUpstream[upstreamUrl] = [];
|
||||
const upstreamsArr = urlsToUpstream[upstreamUrl];
|
||||
if (!upstreamsArr.includes(upstreamToken.upstream.id)) {
|
||||
upstreamsArr.push(upstreamToken.upstream.id);
|
||||
if (!upstreamsArr.includes(upstream.id)) {
|
||||
upstreamsArr.push(upstream.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { Context, GET, Path, POST, Security, ServiceContext } from "typescript-rest";
|
||||
import { AutoWired, Inject } from "typescript-ioc/es6";
|
||||
import { ROLE_USER } from "../security/MatrixSecurity";
|
||||
import TermsController, { ITermsNotSignedResponse } from "../controllers/TermsController";
|
||||
import TermsController, { ITermsResponse } from "../controllers/TermsController";
|
||||
|
||||
export interface SignTermsRequest {
|
||||
user_accepts: string[];
|
||||
@ -22,9 +22,8 @@ export class MatrixTermsService {
|
||||
|
||||
@GET
|
||||
@Path("")
|
||||
@Security(ROLE_USER)
|
||||
public async needsSignatures(): Promise<ITermsNotSignedResponse> {
|
||||
return this.termsController.getMissingTermsForUser(this.context.request.user);
|
||||
public async getAllTerms(): Promise<ITermsResponse> {
|
||||
return this.termsController.getAvailableTerms();
|
||||
}
|
||||
|
||||
@POST
|
||||
|
@ -5,7 +5,7 @@ import { ScalarAccountResponse, ScalarRegisterResponse } from "../../models/Scal
|
||||
import { AutoWired, Inject } from "typescript-ioc/es6";
|
||||
import AccountController from "../controllers/AccountController";
|
||||
import { ROLE_USER } from "../security/MatrixSecurity";
|
||||
import TermsController, { ITermsNotSignedResponse } from "../controllers/TermsController";
|
||||
import TermsController, { ITermsResponse } from "../controllers/TermsController";
|
||||
import { SignTermsRequest } from "../matrix/MatrixTermsService";
|
||||
import { ScalarClient } from "../../scalar/ScalarClient";
|
||||
|
||||
@ -50,9 +50,8 @@ export class ScalarService {
|
||||
|
||||
@GET
|
||||
@Path("terms")
|
||||
@Security(ROLE_USER)
|
||||
public async getTerms(): Promise<ITermsNotSignedResponse> {
|
||||
return this.termsController.getMissingTermsForUser(this.context.request.user);
|
||||
public async getTerms(): Promise<ITermsResponse> {
|
||||
return this.termsController.getAvailableTerms();
|
||||
}
|
||||
|
||||
@POST
|
||||
|
@ -5,6 +5,7 @@ import User from "./models/User";
|
||||
import { MatrixStickerBot } from "../matrix/MatrixStickerBot";
|
||||
import { ScalarClient } from "../scalar/ScalarClient";
|
||||
import { Cache, CACHE_SCALAR_ONLINE_STATE } from "../MemoryCache";
|
||||
import { ILanguagePolicy } from "../api/controllers/TermsController";
|
||||
|
||||
export class ScalarStore {
|
||||
|
||||
@ -56,7 +57,7 @@ export class ScalarStore {
|
||||
return state;
|
||||
}
|
||||
|
||||
private static async checkIfUpstreamOnline(upstream: Upstream, scalarKind: string): Promise<boolean> {
|
||||
private static async checkIfUpstreamOnline(upstream: Upstream, scalarKind: string, signTerms = true): Promise<boolean> {
|
||||
try {
|
||||
// The sticker bot can be used for this for now
|
||||
|
||||
@ -78,8 +79,14 @@ export class ScalarStore {
|
||||
return true; // it's online
|
||||
} catch (e) {
|
||||
LogService.error("ScalarStore", e);
|
||||
if (!isNaN(Number(e))) {
|
||||
if (e === 401 || e === 403) {
|
||||
if (e && !isNaN(Number(e.statusCode))) {
|
||||
if (e.statusCode === 403 && e.body) {
|
||||
if (e.body.errcode === 'M_TERMS_NOT_SIGNED' && signTerms) {
|
||||
await ScalarStore.signAllTerms(existingTokens[0], scalarKind);
|
||||
return ScalarStore.checkIfUpstreamOnline(upstream, scalarKind, false);
|
||||
}
|
||||
}
|
||||
if (e.statusCode === 401 || e.statusCode === 403) {
|
||||
LogService.info("ScalarStore", "Test user token expired");
|
||||
} else {
|
||||
// Assume offline
|
||||
@ -107,13 +114,16 @@ export class ScalarStore {
|
||||
|
||||
const openId = await MatrixStickerBot.getOpenId();
|
||||
const token = await scalarClient.register(openId);
|
||||
await UserScalarToken.create({
|
||||
const scalarToken = await UserScalarToken.create({
|
||||
userId: testUserId,
|
||||
scalarToken: token.scalar_token,
|
||||
isDimensionToken: false,
|
||||
upstreamId: upstream.id,
|
||||
});
|
||||
|
||||
// Accept all terms of service for the user
|
||||
await ScalarStore.signAllTerms(scalarToken, scalarKind);
|
||||
|
||||
return true;
|
||||
} catch (e) {
|
||||
LogService.error("ScalarStore", e);
|
||||
@ -121,6 +131,21 @@ export class ScalarStore {
|
||||
}
|
||||
}
|
||||
|
||||
private static async signAllTerms(token: UserScalarToken, scalarKind: string) {
|
||||
try {
|
||||
const client = new ScalarClient(token.upstream, scalarKind);
|
||||
const terms = await client.getAvailableTerms();
|
||||
const urlsToSign = Object.values(terms.policies).map(p => {
|
||||
const englishCode = Object.keys(p).find(k => k.toLowerCase() === 'en' || k.toLowerCase().startsWith('en_'));
|
||||
if (!englishCode) return null;
|
||||
return (<ILanguagePolicy>p[englishCode]).url;
|
||||
}).filter(v => !!v);
|
||||
await client.signTermsUrls(token.scalarToken, urlsToSign);
|
||||
} catch (e) {
|
||||
LogService.error("ScalarStore", e);
|
||||
}
|
||||
}
|
||||
|
||||
private constructor() {
|
||||
}
|
||||
}
|
@ -4,7 +4,8 @@ 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 { ITermsNotSignedResponse } from "../api/controllers/TermsController";
|
||||
import { ITermsResponse } from "../api/controllers/TermsController";
|
||||
import { subscriptionLogsToBeFn } from "rxjs/internal/testing/TestScheduler";
|
||||
|
||||
const REGISTER_ROUTE = "/register";
|
||||
const ACCOUNT_INFO_ROUTE = "/account";
|
||||
@ -90,8 +91,16 @@ export class ScalarClient {
|
||||
LogService.error("ScalarClient", err);
|
||||
reject(err);
|
||||
} else if (res.statusCode !== 200) {
|
||||
if (typeof(res.body) === 'string') {
|
||||
try {
|
||||
res.body = JSON.parse(res.body);
|
||||
} catch (e) {
|
||||
LogService.error("ScalarClient", "Got error parsing error response:");
|
||||
LogService.error("ScalarClient", e);
|
||||
}
|
||||
}
|
||||
LogService.error("ScalarClient", "Got status code " + res.statusCode + " while getting information for token");
|
||||
reject(res.statusCode);
|
||||
reject(res);
|
||||
} else {
|
||||
resolve(res.body);
|
||||
}
|
||||
@ -124,8 +133,8 @@ export class ScalarClient {
|
||||
});
|
||||
}
|
||||
|
||||
public getMissingTerms(token: string): Promise<ITermsNotSignedResponse> {
|
||||
const {scalarUrl, headers, queryString} = this.makeRequestArguments(TERMS_ROUTE, token);
|
||||
public getAvailableTerms(): Promise<ITermsResponse> {
|
||||
const {scalarUrl, headers, queryString} = this.makeRequestArguments(TERMS_ROUTE, null);
|
||||
LogService.info("ScalarClient", "Doing upstream scalar request: GET " + scalarUrl);
|
||||
return new Promise((resolve, reject) => {
|
||||
request({
|
||||
|
Loading…
Reference in New Issue
Block a user