Very simple terms of service auth requirement

This commit is contained in:
Travis Ralston 2019-06-30 19:38:24 -06:00
parent 4ae49702bd
commit d9637b1d3d
3 changed files with 92 additions and 3 deletions

View File

@ -0,0 +1,35 @@
import { AutoWired } from "typescript-ioc/es6";
import { IMSCUser } from "../security/MSCSecurity";
export interface ILanguagePolicy {
name: string;
url: string;
}
export interface IPolicy {
version: string;
// a string value is not allowed here, but TypeScript is angry otherwise.
[language: string]: string | ILanguagePolicy;
}
export interface ITermsNotSignedResponse {
policies: { [policyName: string]: IPolicy };
}
/**
* API controller for terms of service management
*/
@AutoWired
export default class TermsController {
constructor() {
}
public async doesUserNeedToSignTerms(user: IMSCUser): Promise<boolean> {
return Object.keys((await this.getMissingTermsForUser(user)).policies).length > 0;
}
public async getMissingTermsForUser(_user: IMSCUser): Promise<ITermsNotSignedResponse> {
return {policies: {}};
}
}

View File

@ -0,0 +1,25 @@
import { Context, GET, Path, Security, ServiceContext } from "typescript-rest";
import { AutoWired, Inject } from "typescript-ioc/es6";
import { ROLE_MSC_USER } from "../security/MSCSecurity";
import TermsController, { ITermsNotSignedResponse } from "../controllers/TermsController";
/**
* API for account management
*/
@Path("/_matrix/integrations/v1/terms")
@AutoWired
export class MSCTermsService {
@Inject
private termsController: TermsController;
@Context
private context: ServiceContext;
@GET
@Path("")
@Security(ROLE_MSC_USER)
public async needsSignatures(): Promise<ITermsNotSignedResponse> {
return this.termsController.getMissingTermsForUser(this.context.request.user);
}
}

View File

@ -3,6 +3,7 @@ import { Request, RequestHandler, Response, Router } from "express";
import { ApiError } from "../ApiError"; import { ApiError } from "../ApiError";
import { LogService } from "matrix-js-snippets"; import { LogService } from "matrix-js-snippets";
import AccountController from "../controllers/AccountController"; import AccountController from "../controllers/AccountController";
import TermsController from "../controllers/TermsController";
export interface IMSCUser { export interface IMSCUser {
userId: string; userId: string;
@ -10,18 +11,25 @@ export interface IMSCUser {
} }
export const ROLE_MSC_USER = "ROLE_MSC_USER"; export const ROLE_MSC_USER = "ROLE_MSC_USER";
export const ROLE_MSC_TERMS_SIGNED = "ROLE_MSC_TERMS_SIGNED";
const TERMS_IGNORED_ROUTES = [
{method: "GET", path: "/_matrix/integrations/v1/terms"},
{method: "POST", path: "/_matrix/integrations/v1/terms"},
{method: "POST", path: "/_matrix/integrations/v1/register"},
{method: "POST", path: "/_matrix/integrations/v1/logout"},
];
export default class MSCSecurity implements ServiceAuthenticator { export default class MSCSecurity implements ServiceAuthenticator {
private accountController = new AccountController(); private accountController = new AccountController();
private termsController = new TermsController();
public getRoles(req: Request): string[] { public getRoles(req: Request): string[] {
if (req.user) return [ROLE_MSC_USER]; if (req.user) return [ROLE_MSC_USER];
return []; return [];
} }
getMiddleware(): RequestHandler { public getMiddleware(): RequestHandler {
return (async (req: Request, res: Response, next: () => void) => { return (async (req: Request, res: Response, next: () => void) => {
try { try {
let token = null; let token = null;
@ -41,6 +49,27 @@ export default class MSCSecurity implements ServiceAuthenticator {
userId: await this.accountController.getTokenOwner(token), userId: await this.accountController.getTokenOwner(token),
token: token, token: token,
}; };
let needTerms = true;
if (req.method !== "OPTIONS") {
for (const route of TERMS_IGNORED_ROUTES) {
if (route.method === req.method && route.path === req.path) {
needTerms = false;
break;
}
}
} else needTerms = false;
if (needTerms) {
const signatureNeeded = await this.termsController.doesUserNeedToSignTerms(req.user);
if (signatureNeeded) {
return res.status(403).json({
errcode: "M_TERMS_NOT_SIGNED",
error: "The user has not accepted all terms of service for this integration manager",
});
}
}
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"});
@ -57,7 +86,7 @@ export default class MSCSecurity implements ServiceAuthenticator {
}); });
} }
initialize(_router: Router): void { public initialize(_router: Router): void {
} }
} }