mirror of
https://github.com/turt2live/matrix-dimension.git
synced 2024-10-01 01:05:53 -04:00
Simple implementation of listing and accepting policies
This commit is contained in:
parent
6c6ae5c5ed
commit
147d8a18ae
@ -2,6 +2,8 @@ import { AutoWired } from "typescript-ioc/es6";
|
|||||||
import { IMSCUser } from "../security/MSCSecurity";
|
import { IMSCUser } from "../security/MSCSecurity";
|
||||||
import TermsRecord from "../../db/models/TermsRecord";
|
import TermsRecord from "../../db/models/TermsRecord";
|
||||||
import TermsTextRecord from "../../db/models/TermsTextRecord";
|
import TermsTextRecord from "../../db/models/TermsTextRecord";
|
||||||
|
import TermsSignedRecord from "../../db/models/TermsSignedRecord";
|
||||||
|
import { Op } from "sequelize";
|
||||||
|
|
||||||
export interface ILanguagePolicy {
|
export interface ILanguagePolicy {
|
||||||
name: string;
|
name: string;
|
||||||
@ -45,9 +47,53 @@ export default class TermsController {
|
|||||||
return Object.keys((await this.getMissingTermsForUser(user)).policies).length > 0;
|
return Object.keys((await this.getMissingTermsForUser(user)).policies).length > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getMissingTermsForUser(_user: IMSCUser): Promise<ITermsNotSignedResponse> {
|
public async getMissingTermsForUser(user: IMSCUser): Promise<ITermsNotSignedResponse> {
|
||||||
// TODO: Abuse a cache for non-draft policies
|
// TODO: Abuse a cache for non-draft policies
|
||||||
return {policies: {}};
|
// TODO: Upstream policies
|
||||||
|
|
||||||
|
const notDrafts = await TermsRecord.findAll({
|
||||||
|
where: {version: {[Op.ne]: VERSION_DRAFT}},
|
||||||
|
include: [TermsTextRecord],
|
||||||
|
});
|
||||||
|
const signed = await TermsSignedRecord.findAll({where: {userId: user.userId}});
|
||||||
|
|
||||||
|
const latest: { [shortcode: string]: TermsRecord } = {};
|
||||||
|
for (const record of notDrafts) {
|
||||||
|
if (!latest[record.shortcode]) {
|
||||||
|
latest[record.shortcode] = record;
|
||||||
|
}
|
||||||
|
if (latest[record.shortcode].id < record.id) {
|
||||||
|
latest[record.shortcode] = record;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const missing = Object.values(latest).filter(d => !signed.find(s => s.termsId === d.id));
|
||||||
|
const policies: ITermsNotSignedResponse = {policies: {}};
|
||||||
|
|
||||||
|
for (const missingPolicy of missing) {
|
||||||
|
policies.policies[missingPolicy.shortcode] = {
|
||||||
|
version: missingPolicy.version,
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const text of missingPolicy.texts) {
|
||||||
|
policies.policies[missingPolicy.shortcode][text.language] = {
|
||||||
|
name: text.name,
|
||||||
|
url: text.url,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return policies;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async signTermsMatching(user: IMSCUser, urls: string[]): Promise<any> {
|
||||||
|
const terms = await TermsTextRecord.findAll({where: {url: {[Op.in]: urls}}});
|
||||||
|
const signed = await TermsSignedRecord.findAll({where: {userId: user.userId}});
|
||||||
|
|
||||||
|
const toAdd = terms.filter(t => !signed.find(s => s.termsId === t.termsId));
|
||||||
|
for (const termsToSign of toAdd) {
|
||||||
|
await TermsSignedRecord.create({termsId: termsToSign.id, userId: user.userId});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getPoliciesForAdmin(): Promise<ITerms[]> {
|
public async getPoliciesForAdmin(): Promise<ITerms[]> {
|
||||||
|
@ -1,8 +1,12 @@
|
|||||||
import { Context, GET, Path, Security, ServiceContext } from "typescript-rest";
|
import { Context, GET, Path, POST, Security, ServiceContext } from "typescript-rest";
|
||||||
import { AutoWired, Inject } from "typescript-ioc/es6";
|
import { AutoWired, Inject } from "typescript-ioc/es6";
|
||||||
import { ROLE_MSC_USER } from "../security/MSCSecurity";
|
import { ROLE_MSC_USER } from "../security/MSCSecurity";
|
||||||
import TermsController, { ITermsNotSignedResponse } from "../controllers/TermsController";
|
import TermsController, { ITermsNotSignedResponse } from "../controllers/TermsController";
|
||||||
|
|
||||||
|
interface SignTermsRequest {
|
||||||
|
user_accepts: string[];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* API for account management
|
* API for account management
|
||||||
*/
|
*/
|
||||||
@ -22,4 +26,12 @@ export class MSCTermsService {
|
|||||||
public async needsSignatures(): Promise<ITermsNotSignedResponse> {
|
public async needsSignatures(): Promise<ITermsNotSignedResponse> {
|
||||||
return this.termsController.getMissingTermsForUser(this.context.request.user);
|
return this.termsController.getMissingTermsForUser(this.context.request.user);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("")
|
||||||
|
@Security(ROLE_MSC_USER)
|
||||||
|
public async signTerms(request: SignTermsRequest): Promise<any> {
|
||||||
|
await this.termsController.signTermsMatching(this.context.request.user, request.user_accepts);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
}
|
}
|
@ -28,6 +28,7 @@ import CustomSimpleBotRecord from "./models/CustomSimpleBotRecord";
|
|||||||
import SlackBridgeRecord from "./models/SlackBridgeRecord";
|
import SlackBridgeRecord from "./models/SlackBridgeRecord";
|
||||||
import TermsRecord from "./models/TermsRecord";
|
import TermsRecord from "./models/TermsRecord";
|
||||||
import TermsTextRecord from "./models/TermsTextRecord";
|
import TermsTextRecord from "./models/TermsTextRecord";
|
||||||
|
import TermsSignedRecord from "./models/TermsSignedRecord";
|
||||||
|
|
||||||
class _DimensionStore {
|
class _DimensionStore {
|
||||||
private sequelize: Sequelize;
|
private sequelize: Sequelize;
|
||||||
@ -67,6 +68,7 @@ class _DimensionStore {
|
|||||||
SlackBridgeRecord,
|
SlackBridgeRecord,
|
||||||
TermsRecord,
|
TermsRecord,
|
||||||
TermsTextRecord,
|
TermsTextRecord,
|
||||||
|
TermsSignedRecord,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ export default {
|
|||||||
.then(() => queryInterface.createTable("dimension_terms_text", {
|
.then(() => queryInterface.createTable("dimension_terms_text", {
|
||||||
"id": {type: DataType.INTEGER, primaryKey: true, autoIncrement: true, allowNull: false},
|
"id": {type: DataType.INTEGER, primaryKey: true, autoIncrement: true, allowNull: false},
|
||||||
"termsId": {
|
"termsId": {
|
||||||
type: DataType.INTEGER, allowNull: true,
|
type: DataType.INTEGER, allowNull: false,
|
||||||
references: {model: "dimension_terms", key: "id"},
|
references: {model: "dimension_terms", key: "id"},
|
||||||
onUpdate: "cascade", onDelete: "cascade",
|
onUpdate: "cascade", onDelete: "cascade",
|
||||||
},
|
},
|
||||||
|
25
src/db/migrations/20190706154345-AddUserSignedTerms.ts
Normal file
25
src/db/migrations/20190706154345-AddUserSignedTerms.ts
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import { QueryInterface } from "sequelize";
|
||||||
|
import { DataType } from "sequelize-typescript";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
up: (queryInterface: QueryInterface) => {
|
||||||
|
return Promise.resolve()
|
||||||
|
.then(() => queryInterface.createTable("dimension_terms_signed", {
|
||||||
|
"id": {type: DataType.INTEGER, primaryKey: true, autoIncrement: true, allowNull: false},
|
||||||
|
"termsId": {
|
||||||
|
type: DataType.INTEGER, allowNull: false,
|
||||||
|
references: {model: "dimension_terms", key: "id"},
|
||||||
|
onUpdate: "cascade", onDelete: "cascade",
|
||||||
|
},
|
||||||
|
"userId": {
|
||||||
|
type: DataType.STRING, allowNull: false,
|
||||||
|
references: {model: "dimension_users", key: "userId"},
|
||||||
|
onUpdate: "cascade", onDelete: "cascade",
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
down: (queryInterface: QueryInterface) => {
|
||||||
|
return Promise.resolve()
|
||||||
|
.then(() => queryInterface.dropTable("dimension_terms_signed"));
|
||||||
|
}
|
||||||
|
}
|
25
src/db/models/TermsSignedRecord.ts
Normal file
25
src/db/models/TermsSignedRecord.ts
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import { AllowNull, AutoIncrement, Column, ForeignKey, Model, PrimaryKey, Table } from "sequelize-typescript";
|
||||||
|
import User from "./User";
|
||||||
|
import TermsRecord from "./TermsRecord";
|
||||||
|
|
||||||
|
@Table({
|
||||||
|
tableName: "dimension_terms_signed",
|
||||||
|
underscored: false,
|
||||||
|
timestamps: false,
|
||||||
|
})
|
||||||
|
export default class TermsSignedRecord extends Model<TermsSignedRecord> {
|
||||||
|
@PrimaryKey
|
||||||
|
@AutoIncrement
|
||||||
|
@Column
|
||||||
|
id: number;
|
||||||
|
|
||||||
|
@AllowNull
|
||||||
|
@Column
|
||||||
|
@ForeignKey(() => TermsRecord)
|
||||||
|
termsId?: number;
|
||||||
|
|
||||||
|
@AllowNull
|
||||||
|
@Column
|
||||||
|
@ForeignKey(() => User)
|
||||||
|
userId?: string;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user