From 1c3507bc117a4fef33401b0bc17d2f79f4d4bfde Mon Sep 17 00:00:00 2001
From: David Baker <dbkr@users.noreply.github.com>
Date: Fri, 11 Feb 2022 17:03:22 +0000
Subject: [PATCH] Use the shared secret registration API directly (#7774)

* Use the shared secret registration API directly

rather than invoking the synapse module to do it. It's probably
a bit simpler, if anything, and allows for synapse to be run in
a separate container (or rather, avoids the javascript having to have
a copy of synapse source & server config).

* Make registration secret required

Update commander (8 major versions!) to get requiredOption

* Wrong options object :/
---
 test/end-to-end-tests/package.json        |  2 +-
 test/end-to-end-tests/run.sh              |  3 +-
 test/end-to-end-tests/src/rest/creator.ts | 53 +++++++++--------------
 test/end-to-end-tests/start.ts            | 24 +++++-----
 test/end-to-end-tests/synapse/getcfg.sh   | 17 ++++++++
 test/end-to-end-tests/yarn.lock           |  8 ++--
 6 files changed, 58 insertions(+), 49 deletions(-)
 create mode 100755 test/end-to-end-tests/synapse/getcfg.sh

diff --git a/test/end-to-end-tests/package.json b/test/end-to-end-tests/package.json
index 53df635ce1..08e2a36cac 100644
--- a/test/end-to-end-tests/package.json
+++ b/test/end-to-end-tests/package.json
@@ -11,7 +11,7 @@
     "license": "ISC",
     "dependencies": {
         "cheerio": "^1.0.0-rc.2",
-        "commander": "^2.19.0",
+        "commander": "^9",
         "puppeteer": "10.0.0",
         "request": "^2.88.0",
         "request-promise-native": "^1.0.7",
diff --git a/test/end-to-end-tests/run.sh b/test/end-to-end-tests/run.sh
index 1a9009a886..b65ce2a956 100755
--- a/test/end-to-end-tests/run.sh
+++ b/test/end-to-end-tests/run.sh
@@ -32,9 +32,10 @@ handle_error() {
 trap 'handle_error' ERR
 
 ./synapse/start.sh
+reg_secret=`./synapse/getcfg.sh registration_shared_secret`
 if [ $has_custom_app -ne "1" ]; then
     ./element/start.sh
 fi
 yarn build
-node lib/start.js $@
+node lib/start.js --registration-shared-secret=$reg_secret $@
 stop_servers
diff --git a/test/end-to-end-tests/src/rest/creator.ts b/test/end-to-end-tests/src/rest/creator.ts
index b88501e48d..33eea675d9 100644
--- a/test/end-to-end-tests/src/rest/creator.ts
+++ b/test/end-to-end-tests/src/rest/creator.ts
@@ -15,29 +15,12 @@ See the License for the specific language governing permissions and
 limitations under the License.
 */
 
-import { exec } from 'child_process';
 import request = require('request-promise-native');
+import * as crypto from 'crypto';
 
 import { RestSession } from './session';
 import { RestMultiSession } from './multi';
 
-interface ExecResult {
-    stdout: string;
-    stderr: string;
-}
-
-function execAsync(command: string, options: Parameters<typeof exec>[1]): Promise<ExecResult> {
-    return new Promise((resolve, reject) => {
-        exec(command, options, (error, stdout, stderr) => {
-            if (error) {
-                reject(error);
-            } else {
-                resolve({ stdout, stderr });
-            }
-        });
-    });
-}
-
 export interface Credentials {
     accessToken: string;
     homeServer: string;
@@ -47,7 +30,7 @@ export interface Credentials {
 }
 
 export class RestSessionCreator {
-    constructor(private readonly synapseSubdir: string, private readonly hsUrl: string, private readonly cwd: string) {}
+    constructor(private readonly hsUrl: string, private readonly regSecret: string) {}
 
     public async createSessionRange(usernames: string[], password: string,
         groupName: string): Promise<RestMultiSession> {
@@ -64,21 +47,25 @@ export class RestSessionCreator {
     }
 
     private async register(username: string, password: string): Promise<void> {
-        const registerArgs = [
-            '-c homeserver.yaml',
-            `-u ${username}`,
-            `-p ${password}`,
-            '--no-admin',
-            this.hsUrl,
-        ];
-        const registerCmd = `./register_new_matrix_user ${registerArgs.join(' ')}`;
-        const allCmds = [
-            `cd ${this.synapseSubdir}`,
-            ". ./activate",
-            registerCmd,
-        ].join(' && ');
+        // get a nonce
+        const regUrl = `${this.hsUrl}/_synapse/admin/v1/register`;
+        const nonceResp = await request.get({ uri: regUrl, json: true });
 
-        await execAsync(allCmds, { cwd: this.cwd, encoding: 'utf-8' });
+        const mac = crypto.createHmac('sha1', this.regSecret).update(
+            `${nonceResp.nonce}\0${username}\0${password}\0notadmin`,
+        ).digest('hex');
+
+        await request.post({
+            uri: regUrl,
+            json: true,
+            body: {
+                nonce: nonceResp.nonce,
+                username,
+                password,
+                mac,
+                admin: false,
+            },
+        });
     }
 
     private async authenticate(username: string, password: string): Promise<Credentials> {
diff --git a/test/end-to-end-tests/start.ts b/test/end-to-end-tests/start.ts
index 5e6b9b6066..f6c7400a15 100644
--- a/test/end-to-end-tests/start.ts
+++ b/test/end-to-end-tests/start.ts
@@ -15,12 +15,14 @@ limitations under the License.
 */
 
 import * as fs from "fs";
-import program = require('commander');
+import { Command } from "commander";
 
 import { ElementSession } from './src/session';
 import { scenario } from './src/scenario';
 import { RestSessionCreator } from './src/rest/creator';
 
+const program = new Command();
+
 program
     .option('--no-logs', "don't output logs, document html on error", false)
     .option('--app-url [url]', "url to test", "http://localhost:5000")
@@ -30,6 +32,7 @@ program
     .option('--throttle-cpu [factor]', "factor to slow down the cpu with", parseFloat, 1.0)
     .option('--no-sandbox', "same as puppeteer arg", false)
     .option('--log-directory <dir>', 'a directory to dump html and network logs in when the tests fail')
+    .requiredOption('--registration-shared-secret <secret>', 'the secret to use for registering users')
     .parse(process.argv);
 
 const hsUrl = 'http://localhost:5005';
@@ -37,12 +40,12 @@ const hsUrl = 'http://localhost:5005';
 async function runTests() {
     const sessions = [];
     const options = {
-        slowMo: program.slowMo ? 20 : undefined,
-        devtools: program.devTools,
-        headless: !program.windowed,
+        slowMo: program.opts().slowMo ? 20 : undefined,
+        devtools: program.opts().devTools,
+        headless: !program.opts().windowed,
         args: [],
     };
-    if (!program.sandbox) {
+    if (!program.opts().sandbox) {
         options.args.push('--no-sandbox', '--disable-setuid-sandbox');
     }
     if (process.env.CHROME_PATH) {
@@ -52,13 +55,14 @@ async function runTests() {
     }
 
     const restCreator = new RestSessionCreator(
-        '../synapse/installations/consent/env/bin',
         hsUrl,
-        __dirname,
+        program.opts().registrationSharedSecret,
     );
 
     async function createSession(username) {
-        const session = await ElementSession.create(username, options, program.appUrl, hsUrl, program.throttleCpu);
+        const session = await ElementSession.create(
+            username, options, program.opts().appUrl, hsUrl, program.opts().throttleCpu,
+        );
         sessions.push(session);
         return session;
     }
@@ -69,8 +73,8 @@ async function runTests() {
     } catch (err) {
         failure = true;
         console.log('failure: ', err);
-        if (program.logDirectory) {
-            await writeLogs(sessions, program.logDirectory);
+        if (program.opts().logDirectory) {
+            await writeLogs(sessions, program.opts().logDirectory);
         }
     }
 
diff --git a/test/end-to-end-tests/synapse/getcfg.sh b/test/end-to-end-tests/synapse/getcfg.sh
new file mode 100755
index 0000000000..e5e81586a4
--- /dev/null
+++ b/test/end-to-end-tests/synapse/getcfg.sh
@@ -0,0 +1,17 @@
+#!/bin/bash
+set -e
+
+if [ $# -eq 0 ]
+then
+    echo "Prints a configuration directive from the synapse installation"
+    echo "Usage: getcfg.sh <synapse config file directive>"
+    exit 1
+fi
+
+# activate the virtualenv so we have pyyaml
+BASE_DIR=$(cd $(dirname $0) && pwd)
+cd $BASE_DIR
+cd installations/consent/env/bin/
+source activate
+
+python -c "from yaml import load, Loader; import sys; print(load(sys.stdin, Loader=Loader)['$1'])" < homeserver.yaml
diff --git a/test/end-to-end-tests/yarn.lock b/test/end-to-end-tests/yarn.lock
index df8ee6c8b3..d03828fa79 100644
--- a/test/end-to-end-tests/yarn.lock
+++ b/test/end-to-end-tests/yarn.lock
@@ -146,10 +146,10 @@ combined-stream@^1.0.6, combined-stream@~1.0.6:
   dependencies:
     delayed-stream "~1.0.0"
 
-commander@^2.19.0:
-  version "2.19.0"
-  resolved "https://registry.yarnpkg.com/commander/-/commander-2.19.0.tgz#f6198aa84e5b83c46054b94ddedbfed5ee9ff12a"
-  integrity sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==
+commander@^9:
+  version "9.0.0"
+  resolved "https://registry.yarnpkg.com/commander/-/commander-9.0.0.tgz#86d58f24ee98126568936bd1d3574e0308a99a40"
+  integrity sha512-JJfP2saEKbQqvW+FI93OYUB4ByV5cizMpFMiiJI8xDbBvQvSkIk0VvQdn1CZ8mqAO8Loq2h0gYTYtDFUZUeERw==
 
 concat-map@0.0.1:
   version "0.0.1"