From efed8b2774340793bac01d828b18e170bb0a46d2 Mon Sep 17 00:00:00 2001 From: Dirk Klimpel <5740567+dklimpel@users.noreply.github.com> Date: Thu, 17 Feb 2022 20:45:21 +0100 Subject: [PATCH] Add token based registration creation and management (#200) * Add token based registration creation and management * yarn fix * Apply suggestions from code review Remove empty line * move date to `const date_format` --- README.md | 2 +- src/App.js | 13 +++ src/components/RegistrationTokens.js | 132 +++++++++++++++++++++++++++ src/i18n/de.js | 13 +++ src/i18n/en.js | 13 +++ src/synapse/dataProvider.js | 23 ++++- 6 files changed, 194 insertions(+), 2 deletions(-) create mode 100644 src/components/RegistrationTokens.js diff --git a/README.md b/README.md index 1ab9552..85ee974 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ This project is built using [react-admin](https://marmelab.com/react-admin/). -It needs at least Synapse v1.41.0 for all functions to work as expected! +It needs at least Synapse v1.42.0 for all functions to work as expected! You get your server version with the request `/_synapse/admin/v1/server_version`. See also [Synapse version API](https://matrix-org.github.io/synapse/develop/admin_api/version_api.html). diff --git a/src/App.js b/src/App.js index 018f18c..ee24fac 100644 --- a/src/App.js +++ b/src/App.js @@ -8,12 +8,18 @@ import { RoomList, RoomShow } from "./components/rooms"; import { ReportList, ReportShow } from "./components/EventReports"; import LoginPage from "./components/LoginPage"; import UserIcon from "@material-ui/icons/Group"; +import ConfirmationNumberIcon from "@material-ui/icons/ConfirmationNumber"; import EqualizerIcon from "@material-ui/icons/Equalizer"; import { UserMediaStatsList } from "./components/statistics"; import RoomIcon from "@material-ui/icons/ViewList"; import ReportIcon from "@material-ui/icons/Warning"; import FolderSharedIcon from "@material-ui/icons/FolderShared"; import { ImportFeature } from "./components/ImportFeature"; +import { + RegistrationTokenCreate, + RegistrationTokenEdit, + RegistrationTokenList, +} from "./components/RegistrationTokens"; import { RoomDirectoryList } from "./components/RoomDirectory"; import { Route } from "react-router-dom"; import germanMessages from "./i18n/de"; @@ -66,6 +72,13 @@ const App = () => ( list={RoomDirectoryList} icon={FolderSharedIcon} /> + diff --git a/src/components/RegistrationTokens.js b/src/components/RegistrationTokens.js new file mode 100644 index 0000000..28213e5 --- /dev/null +++ b/src/components/RegistrationTokens.js @@ -0,0 +1,132 @@ +import React from "react"; +import { + BooleanInput, + Create, + Datagrid, + DateField, + DateTimeInput, + Edit, + Filter, + List, + maxValue, + number, + NumberField, + NumberInput, + regex, + SimpleForm, + TextInput, + TextField, + Toolbar, +} from "react-admin"; + +const date_format = { + year: "numeric", + month: "2-digit", + day: "2-digit", + hour: "2-digit", + minute: "2-digit", + second: "2-digit", +}; + +const validateToken = [regex(/^[A-Za-z0-9._~-]{0,64}$/)]; +const validateUsesAllowed = [number()]; +const validateLength = [number(), maxValue(64)]; + +const dateParser = v => { + const d = new Date(v); + if (isNaN(d)) return 0; + return d.getTime(); +}; + +const dateFormatter = v => { + if (v === undefined || v === null) return; + const d = new Date(v); + + const pad = "00"; + const year = d.getFullYear().toString(); + const month = (pad + (d.getMonth() + 1).toString()).slice(-2); + const day = (pad + d.getDate().toString()).slice(-2); + const hour = (pad + d.getHours().toString()).slice(-2); + const minute = (pad + d.getMinutes().toString()).slice(-2); + + // target format yyyy-MM-ddThh:mm + return `${year}-${month}-${day}T${hour}:${minute}`; +}; + +const RegistrationTokenFilter = props => ( + + + +); + +export const RegistrationTokenList = props => { + return ( + } + filterDefaultValues={{ valid: true }} + pagination={false} + perPage={500} + > + + + + + + + + + ); +}; + +export const RegistrationTokenCreate = props => ( + + }> + + + + + + +); + +export const RegistrationTokenEdit = props => { + return ( + + + + + + + + + + ); +}; diff --git a/src/i18n/de.js b/src/i18n/de.js index 9688468..81889ed 100644 --- a/src/i18n/de.js +++ b/src/i18n/de.js @@ -352,6 +352,19 @@ const de = { send_failure: "Beim Entfernen ist ein Fehler aufgetreten.", }, }, + registration_tokens: { + name: "Registrierungstoken", + fields: { + token: "Token", + valid: "Gültige Token", + uses_allowed: "Anzahl", + pending: "Ausstehend", + completed: "Abgeschlossen", + expiry_time: "Ablaufzeit", + length: "Länge", + }, + helper: { length: "Länge des Tokens, wenn kein Token vorgegeben wird." }, + }, }, ra: { ...germanMessages.ra, diff --git a/src/i18n/en.js b/src/i18n/en.js index b1e379a..fb63005 100644 --- a/src/i18n/en.js +++ b/src/i18n/en.js @@ -351,5 +351,18 @@ const en = { }, }, }, + registration_tokens: { + name: "Registration tokens", + fields: { + token: "Token", + valid: "Valid token", + uses_allowed: "Uses allowed", + pending: "Pending", + completed: "Completed", + expiry_time: "Expiry time", + length: "Length", + }, + helper: { length: "Length of the token if no token is given." }, + }, }; export default en; diff --git a/src/synapse/dataProvider.js b/src/synapse/dataProvider.js index 5b11139..b80b075 100644 --- a/src/synapse/dataProvider.js +++ b/src/synapse/dataProvider.js @@ -275,6 +275,25 @@ const resourceMap = { method: "PUT", }), }, + registration_tokens: { + path: "/_synapse/admin/v1/registration_tokens", + map: rt => ({ + ...rt, + id: rt.token, + }), + data: "registration_tokens", + total: json => { + return json.registration_tokens.length; + }, + create: params => ({ + endpoint: "/_synapse/admin/v1/registration_tokens/new", + body: params, + method: "POST", + }), + delete: params => ({ + endpoint: `/_synapse/admin/v1/registration_tokens/${params.id}`, + }), + }, }; function filterNullValues(key, value) { @@ -296,7 +315,8 @@ function getSearchOrder(order) { const dataProvider = { getList: (resource, params) => { console.log("getList " + resource); - const { user_id, name, guests, deactivated, search_term } = params.filter; + const { user_id, name, guests, deactivated, search_term, valid } = + params.filter; const { page, perPage } = params.pagination; const { field, order } = params.sort; const from = (page - 1) * perPage; @@ -308,6 +328,7 @@ const dataProvider = { name: name, guests: guests, deactivated: deactivated, + valid: valid, order_by: field, dir: getSearchOrder(order), };