From 85bb5203553c4acf823d2200850d88757a6fcc8f Mon Sep 17 00:00:00 2001 From: SamTV12345 <40429738+samtv12345@users.noreply.github.com> Date: Mon, 25 Mar 2024 21:17:33 +0100 Subject: [PATCH] Moved api tests to oauth2. --- pnpm-lock.yaml | 81 ++++++++ src/node/handler/APIHandler.ts | 51 ++--- src/node/hooks/express/openapi.ts | 2 +- src/node/security/OAuth2Provider.ts | 44 ++-- src/package.json | 3 +- src/tests/backend/common.ts | 68 ++++--- src/tests/backend/specs/api/api.ts | 2 +- .../backend/specs/api/characterEncoding.ts | 17 +- src/tests/backend/specs/api/chat.ts | 25 ++- src/tests/backend/specs/api/importexport.ts | 15 +- .../backend/specs/api/importexportGetPost.ts | 77 +++++-- src/tests/backend/specs/api/instance.ts | 4 +- src/tests/backend/specs/api/pad.ts | 192 ++++++++++++------ .../backend/specs/api/restoreRevision.ts | 1 + .../backend/specs/api/sessionsAndGroups.ts | 113 +++++++---- src/tests/settings.json | 20 +- 16 files changed, 501 insertions(+), 214 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0cc9bd4bc..994335dc5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -193,6 +193,9 @@ importers: jsonminify: specifier: 0.4.2 version: 0.4.2 + jsonwebtoken: + specifier: ^9.0.2 + version: 9.0.2 languages4translatewiki: specifier: 0.1.3 version: 0.1.3 @@ -290,6 +293,9 @@ importers: '@types/jsdom': specifier: ^21.1.6 version: 21.1.6 + '@types/jsonwebtoken': + specifier: ^9.0.6 + version: 9.0.6 '@types/mocha': specifier: ^10.0.6 version: 10.0.6 @@ -2263,6 +2269,12 @@ packages: resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} dev: true + /@types/jsonwebtoken@9.0.6: + resolution: {integrity: sha512-/5hndP5dCjloafCXns6SZyESp3Ldq7YjH3zwzwczYnjxIT0Fqzk5ROSYVGfFyczIue7IUEj8hkvLbPoLQ18vQw==} + dependencies: + '@types/node': 20.11.30 + dev: true + /@types/keygrip@1.0.6: resolution: {integrity: sha512-lZuNAY9xeJt7Bx4t4dx0rYCDqGPW8RXhQZK1td7d4H6E9zYbLoOtjBvfwdTKpsyxQI/2jv+armjX/RW+ZNpXOQ==} dev: true @@ -3099,6 +3111,10 @@ packages: update-browserslist-db: 1.0.13(browserslist@4.23.0) dev: true + /buffer-equal-constant-time@1.0.1: + resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==} + dev: false + /buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} dev: false @@ -3556,6 +3572,12 @@ packages: tslib: 2.6.2 dev: true + /ecdsa-sig-formatter@1.0.11: + resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==} + dependencies: + safe-buffer: 5.2.1 + dev: false + /ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} dev: false @@ -5235,10 +5257,41 @@ packages: resolution: {integrity: sha512-lz1nOH69GbsVHeVgEdvyavc/33oymY1AZwtePMiMj4HZPMbP5OIKK3zT9INMWjwua/V4Z4yq7wSlBbSG+g4AEw==} dev: true + /jsonwebtoken@9.0.2: + resolution: {integrity: sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==} + engines: {node: '>=12', npm: '>=6'} + dependencies: + jws: 3.2.2 + lodash.includes: 4.3.0 + lodash.isboolean: 3.0.3 + lodash.isinteger: 4.0.4 + lodash.isnumber: 3.0.3 + lodash.isplainobject: 4.0.6 + lodash.isstring: 4.0.1 + lodash.once: 4.1.1 + ms: 2.1.3 + semver: 7.6.0 + dev: false + /just-extend@6.2.0: resolution: {integrity: sha512-cYofQu2Xpom82S6qD778jBDpwvvy39s1l/hrYij2u9AMdQcGRpaBu6kY4mVhuno5kJVi1DAz4aiphA2WI1/OAw==} dev: true + /jwa@1.4.1: + resolution: {integrity: sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==} + dependencies: + buffer-equal-constant-time: 1.0.1 + ecdsa-sig-formatter: 1.0.11 + safe-buffer: 5.2.1 + dev: false + + /jws@3.2.2: + resolution: {integrity: sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==} + dependencies: + jwa: 1.4.1 + safe-buffer: 5.2.1 + dev: false + /kebab-case@1.0.2: resolution: {integrity: sha512-7n6wXq4gNgBELfDCpzKc+mRrZFs7D+wgfF5WRFLNAr4DA/qtr9Js8uOAVAfHhuLMfAcQ0pRKqbpjx+TcJVdE1Q==} dev: true @@ -5357,9 +5410,37 @@ packages: resolution: {integrity: sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==} dev: true + /lodash.includes@4.3.0: + resolution: {integrity: sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==} + dev: false + + /lodash.isboolean@3.0.3: + resolution: {integrity: sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==} + dev: false + + /lodash.isinteger@4.0.4: + resolution: {integrity: sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==} + dev: false + + /lodash.isnumber@3.0.3: + resolution: {integrity: sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==} + dev: false + + /lodash.isplainobject@4.0.6: + resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==} + dev: false + + /lodash.isstring@4.0.1: + resolution: {integrity: sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==} + dev: false + /lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + /lodash.once@4.1.1: + resolution: {integrity: sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==} + dev: false + /lodash@4.17.21: resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} dev: false diff --git a/src/node/handler/APIHandler.ts b/src/node/handler/APIHandler.ts index 616d20675..6726c83ce 100644 --- a/src/node/handler/APIHandler.ts +++ b/src/node/handler/APIHandler.ts @@ -21,30 +21,12 @@ import {MapArrayType} from "../types/MapType"; -const absolutePaths = require('../utils/AbsolutePaths'); -import fs from 'fs'; const api = require('../db/API'); -import log4js from 'log4js'; const padManager = require('../db/PadManager'); -const randomString = require('../utils/randomstring'); -const argv = require('../utils/Cli').argv; import createHTTPError from 'http-errors'; - -const apiHandlerLogger = log4js.getLogger('APIHandler'); - -// ensure we have an apikey -let apikey:string|null = null; -const apikeyFilename = absolutePaths.makeAbsolute(argv.apikey || './APIKEY.txt'); - -try { - apikey = fs.readFileSync(apikeyFilename, 'utf8'); - apiHandlerLogger.info(`Api key file read from: "${apikeyFilename}"`); -} catch (e) { - apiHandlerLogger.info( - `Api key file "${apikeyFilename}" not found. Creating with random contents.`); - apikey = randomString(32); - fs.writeFileSync(apikeyFilename, apikey!, 'utf8'); -} +import {Http2ServerRequest, Http2ServerResponse} from "node:http2"; +import {publicKeyExported} from "../security/OAuth2Provider"; +import {jwtVerify} from "jose"; // a list of all functions const version:MapArrayType = {}; @@ -174,14 +156,14 @@ type APIFields = { } /** - * Handles a HTTP API call + * Handles an HTTP API call * @param {String} apiVersion the version of the api * @param {String} functionName the name of the called function * @param fields the params of the called function - * @req express request object - * @res express response object + * @param req express request object + * @param res express response object */ -exports.handle = async function (apiVersion: string, functionName: string, fields: APIFields) { +exports.handle = async function (apiVersion: string, functionName: string, fields: APIFields, req: Http2ServerRequest, res: Http2ServerResponse) { // say goodbye if this is an unknown API version if (!(apiVersion in version)) { throw new createHTTPError.NotFound('no such api version'); @@ -192,13 +174,20 @@ exports.handle = async function (apiVersion: string, functionName: string, field throw new createHTTPError.NotFound('no such function'); } - // check the api key! - fields.apikey = fields.apikey || fields.api_key; - - if (fields.apikey !== apikey!.trim()) { + if(!req.headers.authorization) { throw new createHTTPError.Unauthorized('no or wrong API Key'); } + try { + await jwtVerify(req.headers.authorization!.replace("Bearer ", ""), publicKeyExported!, {algorithms: ['RS256'], + requiredClaims: ["admin"]}) + + } catch (e) { + throw new createHTTPError.Unauthorized('no or wrong API Key'); + } + + + // sanitize any padIDs before continuing if (fields.padID) { fields.padID = await padManager.sanitizePadId(fields.padID); @@ -217,7 +206,3 @@ exports.handle = async function (apiVersion: string, functionName: string, field // call the api function return api[functionName].apply(this, functionParams); }; - -exports.exportedForTestingOnly = { - apiKey: apikey, -}; diff --git a/src/node/hooks/express/openapi.ts b/src/node/hooks/express/openapi.ts index aa2f1e483..3cd5cfed6 100644 --- a/src/node/hooks/express/openapi.ts +++ b/src/node/hooks/express/openapi.ts @@ -657,7 +657,7 @@ exports.expressPreSession = async (hookName:string, {app}:any) => { } // start and bind to express - api.init(); + await api.init(); app.use(apiRoot, async (req:any, res:any) => { let response = null; try { diff --git a/src/node/security/OAuth2Provider.ts b/src/node/security/OAuth2Provider.ts index 520d64ae1..832589f76 100644 --- a/src/node/security/OAuth2Provider.ts +++ b/src/node/security/OAuth2Provider.ts @@ -1,6 +1,6 @@ import {ArgsExpressType} from "../types/ArgsExpressType"; -import Provider, {Account, Configuration, InteractionResults} from 'oidc-provider'; -import {generateKeyPair, exportJWK} from 'jose' +import Provider, {Account, Configuration} from 'oidc-provider'; +import {generateKeyPair, exportJWK, KeyLike} from 'jose' import MemoryAdapter from "./OIDCAdapter"; import path from "path"; const settings = require('../utils/Settings'); @@ -8,8 +8,8 @@ import {IncomingForm} from 'formidable' import express, {Request, Response} from 'express'; import {format} from 'url' import {ParsedUrlQuery} from "node:querystring"; -import cors from 'cors' import {Http2ServerRequest, Http2ServerResponse} from "node:http2"; + const configuration: Configuration = { scopes: ['openid', 'profile', 'email'], findAccount: async (ctx, id) => { @@ -27,14 +27,25 @@ const configuration: Configuration = { const account = usersArray1.find((user) => user.username === id); - return { - accountId: id, - claims: () => ({ - sub: id, - test: "test", - admin: account?.is_admin - }) - } as Account + if(account === undefined) { + return undefined + } + if (account.is_admin) { + return { + accountId: id, + claims: () => ({ + sub: id, + admin: true + }) + } as Account + } else { + return { + accountId: id, + claims: () => ({ + sub: id, + }) + } as Account + } }, ttl:{ AccessToken: 1 * 60 * 60, // 1 hour in seconds @@ -59,16 +70,17 @@ const configuration: Configuration = { }; +export let publicKeyExported: KeyLike|null +export let privateKeyExported: KeyLike|null + /* This function is used to initialize the OAuth2 provider */ export const expressCreateServer = async (hookName: string, args: ArgsExpressType, cb: Function) => { - const {privateKey} = await generateKeyPair('RS256'); + const {privateKey, publicKey} = await generateKeyPair('RS256'); const privateKeyJWK = await exportJWK(privateKey); - // Use cors middleware - args.app.use(cors({ - origin: ['http://localhost:3001', 'https://oauth.pstmn.io'], // replace with your allowed origins - })); + publicKeyExported = publicKey + privateKeyExported = privateKey const oidc = new Provider('http://localhost:9001', { ...configuration, jwks: { diff --git a/src/package.json b/src/package.json index 011f07895..44335fade 100644 --- a/src/package.json +++ b/src/package.json @@ -34,7 +34,6 @@ "axios": "^1.6.8", "clean-css": "^5.3.3", "cookie-parser": "^1.4.6", - "cors": "^2.8.5", "cross-spawn": "^7.0.3", "ejs": "^3.1.9", "etherpad-require-kernel": "^1.0.16", @@ -50,6 +49,7 @@ "js-cookie": "^3.0.5", "jsdom": "^24.0.0", "jsonminify": "0.4.2", + "jsonwebtoken": "^9.0.2", "languages4translatewiki": "0.1.3", "live-plugin-manager-pnpm": "^0.18.1", "lodash.clonedeep": "4.5.0", @@ -89,6 +89,7 @@ "@types/formidable": "^3.4.5", "@types/http-errors": "^2.0.4", "@types/jsdom": "^21.1.6", + "@types/jsonwebtoken": "^9.0.6", "@types/mocha": "^10.0.6", "@types/node": "^20.11.30", "@types/oidc-provider": "^8.4.4", diff --git a/src/tests/backend/common.ts b/src/tests/backend/common.ts index c0cfd1377..bf6de7919 100644 --- a/src/tests/backend/common.ts +++ b/src/tests/backend/common.ts @@ -13,18 +13,20 @@ const server = require('../../node/server'); const setCookieParser = require('set-cookie-parser'); const settings = require('../../node/utils/Settings'); import supertest from 'supertest'; +import TestAgent from "supertest/lib/agent"; +import {Http2Server} from "node:http2"; +import {SignJWT} from "jose"; +import {privateKeyExported} from "../../node/security/OAuth2Provider"; const webaccess = require('../../node/hooks/express/webaccess'); const backups:MapArrayType = {}; let agentPromise:Promise|null = null; -exports.apiKey = apiHandler.exportedForTestingOnly.apiKey; -exports.agent = null; -exports.baseUrl = null; -exports.httpServer = null; -exports.logger = log4js.getLogger('test'); +export let agent: TestAgent|null = null; +export let baseUrl:string|null = null; +export let httpServer: Http2Server|null = null; +export const logger = log4js.getLogger('test'); -const logger = exports.logger; const logLevel = logger.level; // Mocha doesn't monitor unhandled Promise rejections, so convert them to uncaught exceptions. @@ -33,10 +35,24 @@ process.on('unhandledRejection', (reason: string) => { throw reason; }); before(async function () { this.timeout(60000); - await exports.init(); + await init(); }); -exports.init = async function () { + +export const generateJWTToken = () => { + const jwt = new SignJWT({ + sub: 'admin', + jti: '123', + exp: Math.floor(Date.now() / 1000) + 60 * 60, + aud: 'account', + iss: 'http://localhost:9001', + admin: true + }) + jwt.setProtectedHeader({alg: 'RS256'}) + return jwt.sign(privateKeyExported!) +} + +export const init = async function () { if (agentPromise != null) return await agentPromise; let agentResolve; agentPromise = new Promise((resolve) => { agentResolve = resolve; }); @@ -53,11 +69,13 @@ exports.init = async function () { settings.ip = 'localhost'; settings.importExportRateLimiting = {max: 999999}; settings.commitRateLimiting = {duration: 0.001, points: 1e6}; - exports.httpServer = await server.start(); - exports.baseUrl = `http://localhost:${exports.httpServer.address().port}`; - logger.debug(`HTTP server at ${exports.baseUrl}`); + httpServer = await server.start(); + // @ts-ignore + baseUrl = `http://localhost:${httpServer!.address()!.port}`; + logger.debug(`HTTP server at ${baseUrl}`); // Create a supertest user agent for the HTTP server. - exports.agent = supertest(exports.baseUrl); + agent = supertest(baseUrl) + //.set('Authorization', `Bearer ${await generateJWTToken()}`); // Speed up authn tests. backups.authnFailureDelayMs = webaccess.authnFailureDelayMs; webaccess.authnFailureDelayMs = 0; @@ -69,8 +87,8 @@ exports.init = async function () { await server.exit(); }); - agentResolve!(exports.agent); - return exports.agent; + agentResolve!(agent); + return agent; }; /** @@ -81,7 +99,7 @@ exports.init = async function () { * @param {string} event - The socket.io Socket event to listen for. * @returns The argument(s) passed to the event handler. */ -exports.waitForSocketEvent = async (socket: any, event:string) => { +export const waitForSocketEvent = async (socket: any, event:string) => { const errorEvents = [ 'error', 'connect_error', @@ -136,7 +154,7 @@ exports.waitForSocketEvent = async (socket: any, event:string) => { * nullish, no cookies are passed to the server. * @returns {io.Socket} A socket.io client Socket object. */ -exports.connect = async (res:any = null) => { +export const connect = async (res:any = null) => { // Convert the `set-cookie` header(s) into a `cookie` header. const resCookies = (res == null) ? {} : setCookieParser.parse(res, {map: true}); const reqCookieHdr = Object.entries(resCookies).map( @@ -148,14 +166,14 @@ exports.connect = async (res:any = null) => { if (res) { padId = res.req.path.split('/p/')[1]; } - const socket = io(`${exports.baseUrl}/`, { + const socket = io(`${baseUrl}/`, { forceNew: true, // Different tests will have different query parameters. // socketio.js-client on node.js doesn't support cookies (see https://git.io/JU8u9), so the // express_sid cookie must be passed as a query parameter. query: {cookie: reqCookieHdr, padId}, }); try { - await exports.waitForSocketEvent(socket, 'connect'); + await waitForSocketEvent(socket, 'connect'); } catch (e) { socket.close(); throw e; @@ -173,7 +191,7 @@ exports.connect = async (res:any = null) => { * @param token * @returns The CLIENT_VARS message from the server. */ -exports.handshake = async (socket: any, padId:string, token = padutils.generateAuthorToken()) => { +export const handshake = async (socket: any, padId:string, token = padutils.generateAuthorToken()) => { logger.debug('sending CLIENT_READY...'); socket.emit('message', { component: 'pad', @@ -183,7 +201,7 @@ exports.handshake = async (socket: any, padId:string, token = padutils.generateA token, }); logger.debug('waiting for CLIENT_VARS response...'); - const msg = await exports.waitForSocketEvent(socket, 'message'); + const msg = await waitForSocketEvent(socket, 'message'); logger.debug('received CLIENT_VARS message'); return msg; }; @@ -191,7 +209,7 @@ exports.handshake = async (socket: any, padId:string, token = padutils.generateA /** * Convenience wrapper around `socket.send()` that waits for acknowledgement. */ -exports.sendMessage = async (socket: any, message:any) => await new Promise((resolve, reject) => { +export const sendMessage = async (socket: any, message:any) => await new Promise((resolve, reject) => { socket.emit('message', message, (errInfo:{ name: string, message: string, @@ -210,7 +228,7 @@ exports.sendMessage = async (socket: any, message:any) => await new Promise await exports.sendMessage(socket, { +export const sendUserChanges = async (socket:any, data:any) => await sendMessage(socket, { type: 'COLLABROOM', component: 'pad', data: { @@ -232,8 +250,8 @@ exports.sendUserChanges = async (socket:any, data:any) => await exports.sendMess * common.sendUserChanges(socket, {baseRev: rev, changeset}), * ]); */ -exports.waitForAcceptCommit = async (socket:any, wantRev: number) => { - const msg = await exports.waitForSocketEvent(socket, 'message'); +export const waitForAcceptCommit = async (socket:any, wantRev: number) => { + const msg = await waitForSocketEvent(socket, 'message'); assert.deepEqual(msg, { type: 'COLLABROOM', data: { @@ -252,7 +270,7 @@ const alphabet = 'abcdefghijklmnopqrstuvwxyz'; * @param {string} [charset] - Characters to pick from. * @returns {string} */ -exports.randomString = (len: number = 10, charset: string = `${alphabet}${alphabet.toUpperCase()}0123456789`): string => { +export const randomString = (len: number = 10, charset: string = `${alphabet}${alphabet.toUpperCase()}0123456789`): string => { let ret = ''; while (ret.length < len) ret += charset[Math.floor(Math.random() * charset.length)]; return ret; diff --git a/src/tests/backend/specs/api/api.ts b/src/tests/backend/specs/api/api.ts index 32681e5c9..294d38e90 100644 --- a/src/tests/backend/specs/api/api.ts +++ b/src/tests/backend/specs/api/api.ts @@ -27,7 +27,7 @@ const makeid = () => { const testPadId = makeid(); -const endPoint = (point:string) => `/api/${apiVersion}/${point}?apikey=${apiKey}`; +const endPoint = (point:string) => `/api/${apiVersion}/${point}`; describe(__filename, function () { before(async function () { agent = await common.init(); }); diff --git a/src/tests/backend/specs/api/characterEncoding.ts b/src/tests/backend/specs/api/characterEncoding.ts index 7c2202a09..df6e09555 100644 --- a/src/tests/backend/specs/api/characterEncoding.ts +++ b/src/tests/backend/specs/api/characterEncoding.ts @@ -6,17 +6,18 @@ * TODO: maybe unify those two files and merge in a single one. */ +import {generateJWTToken} from "../../common"; + const assert = require('assert').strict; const common = require('../../common'); const fs = require('fs'); const fsp = fs.promises; let agent:any; -const apiKey = common.apiKey; let apiVersion = 1; const testPadId = makeid(); -const endPoint = (point:string, version?:number) => `/api/${version || apiVersion}/${point}?apikey=${apiKey}`; +const endPoint = (point:string, version?:number) => `/api/${version || apiVersion}/${point}`; describe(__filename, function () { before(async function () { agent = await common.init(); }); @@ -24,12 +25,14 @@ describe(__filename, function () { describe('Sanity checks', function () { it('can connect', async function () { await agent.get('/api/') + .set("Authorization", await generateJWTToken()) .expect(200) .expect('Content-Type', /json/); }); it('finds the version tag', async function () { const res = await agent.get('/api/') + .set("Authorization", await generateJWTToken()) .expect(200); apiVersion = res.body.currentVersion; assert(apiVersion); @@ -38,14 +41,16 @@ describe(__filename, function () { it('errors with invalid APIKey', async function () { // This is broken because Etherpad doesn't handle HTTP codes properly see #2343 // If your APIKey is password you deserve to fail all tests anyway - await agent.get(`/api/${apiVersion}/createPad?apikey=password&padID=test`) + await agent.get(`/api/${apiVersion}/createPad?padID=test`) + .set("Authorization", (await generateJWTToken()).substring(0,10)) .expect(401); }); }); describe('Tests', function () { it('creates a new Pad', async function () { - const res = await agent.get(`${endPoint('createPad')}&padID=${testPadId}`) + const res = await agent.get(`${endPoint('createPad')}?padID=${testPadId}`) + .set("Authorization", await generateJWTToken()) .expect(200) .expect('Content-Type', /json/); assert.equal(res.body.code, 0); @@ -53,6 +58,7 @@ describe(__filename, function () { it('Sets the HTML of a Pad attempting to weird utf8 encoded content', async function () { const res = await agent.post(endPoint('setHTML')) + .set("Authorization", await generateJWTToken()) .send({ padID: testPadId, html: await fsp.readFile('tests/backend/specs/api/emojis.html', 'utf8'), @@ -63,7 +69,8 @@ describe(__filename, function () { }); it('get the HTML of Pad with emojis', async function () { - const res = await agent.get(`${endPoint('getHTML')}&padID=${testPadId}`) + const res = await agent.get(`${endPoint('getHTML')}?padID=${testPadId}`) + .set("Authorization", await generateJWTToken()) .expect(200) .expect('Content-Type', /json/); assert.match(res.body.data.html, /🇼/); diff --git a/src/tests/backend/specs/api/chat.ts b/src/tests/backend/specs/api/chat.ts index dc61402bf..d2c0ba8a8 100644 --- a/src/tests/backend/specs/api/chat.ts +++ b/src/tests/backend/specs/api/chat.ts @@ -1,17 +1,18 @@ 'use strict'; +import {generateJWTToken} from "../../common"; + const common = require('../../common'); import {strict as assert} from "assert"; let agent:any; -const apiKey = common.apiKey; let apiVersion = 1; let authorID = ''; const padID = makeid(); const timestamp = Date.now(); -const endPoint = (point:string) => `/api/${apiVersion}/${point}?apikey=${apiKey}`; +const endPoint = (point:string) => `/api/${apiVersion}/${point}`; describe(__filename, function () { before(async function () { agent = await common.init(); }); @@ -42,16 +43,18 @@ describe(__filename, function () { describe('Chat functionality', function () { it('creates a new Pad', async function () { - await agent.get(`${endPoint('createPad')}&padID=${padID}`) + await agent.get(`${endPoint('createPad')}?padID=${padID}`) + .set("authorization", await generateJWTToken()) + .expect(200) .expect((res:any) => { if (res.body.code !== 0) throw new Error('Unable to create new Pad'); }) - .expect('Content-Type', /json/) - .expect(200); + .expect('Content-Type', /json/); }); it('Creates an author with a name set', async function () { await agent.get(endPoint('createAuthor')) + .set("authorization", await generateJWTToken()) .expect((res:any) => { if (res.body.code !== 0 || !res.body.data.authorID) { throw new Error('Unable to create author'); @@ -63,7 +66,8 @@ describe(__filename, function () { }); it('Gets the head of chat before the first chat msg', async function () { - await agent.get(`${endPoint('getChatHead')}&padID=${padID}`) + await agent.get(`${endPoint('getChatHead')}?padID=${padID}`) + .set("authorization", await generateJWTToken()) .expect((res:any) => { if (res.body.data.chatHead !== -1) throw new Error('Chat Head Length is wrong'); if (res.body.code !== 0) throw new Error('Unable to get chat head'); @@ -73,8 +77,9 @@ describe(__filename, function () { }); it('Adds a chat message to the pad', async function () { - await agent.get(`${endPoint('appendChatMessage')}&padID=${padID}&text=blalblalbha` + + await agent.get(`${endPoint('appendChatMessage')}?padID=${padID}&text=blalblalbha` + `&authorID=${authorID}&time=${timestamp}`) + .set("authorization", await generateJWTToken()) .expect((res:any) => { if (res.body.code !== 0) throw new Error('Unable to create chat message'); }) @@ -83,7 +88,8 @@ describe(__filename, function () { }); it('Gets the head of chat', async function () { - await agent.get(`${endPoint('getChatHead')}&padID=${padID}`) + await agent.get(`${endPoint('getChatHead')}?padID=${padID}`) + .set("authorization", await generateJWTToken()) .expect((res:any) => { if (res.body.data.chatHead !== 0) throw new Error('Chat Head Length is wrong'); @@ -94,7 +100,8 @@ describe(__filename, function () { }); it('Gets Chat History of a Pad', async function () { - await agent.get(`${endPoint('getChatHistory')}&padID=${padID}`) + await agent.get(`${endPoint('getChatHistory')}?padID=${padID}`) + .set("authorization", await generateJWTToken()) .expect('Content-Type', /json/) .expect(200) .expect((res:any) => { diff --git a/src/tests/backend/specs/api/importexport.ts b/src/tests/backend/specs/api/importexport.ts index a1ef64d87..5b93ea346 100644 --- a/src/tests/backend/specs/api/importexport.ts +++ b/src/tests/backend/specs/api/importexport.ts @@ -11,10 +11,9 @@ import {MapArrayType} from "../../../../node/types/MapType"; const common = require('../../common'); let agent:any; -const apiKey = common.apiKey; const apiVersion = 1; -const endPoint = (point: string, version?:string) => `/api/${version || apiVersion}/${point}?apikey=${apiKey}`; +const endPoint = (point: string, version?:string) => `/api/${version || apiVersion}/${point}`; const testImports:MapArrayType = { 'malformed': { @@ -243,29 +242,33 @@ describe(__filename, function () { } it('createPad', async function () { - const res = await agent.get(`${endPoint('createPad')}&padID=${testPadId}`) + const res = await agent.get(`${endPoint('createPad')}?padID=${testPadId}`) + .set("authorization", await common.generateJWTToken()) .expect(200) .expect('Content-Type', /json/); assert.equal(res.body.code, 0); }); it('setHTML', async function () { - const res = await agent.get(`${endPoint('setHTML')}&padID=${testPadId}` + + const res = await agent.get(`${endPoint('setHTML')}?padID=${testPadId}` + `&html=${encodeURIComponent(test.input)}`) + .set("authorization", await common.generateJWTToken()) .expect(200) .expect('Content-Type', /json/); assert.equal(res.body.code, 0); }); it('getHTML', async function () { - const res = await agent.get(`${endPoint('getHTML')}&padID=${testPadId}`) + const res = await agent.get(`${endPoint('getHTML')}?padID=${testPadId}`) + .set("authorization", await common.generateJWTToken()) .expect(200) .expect('Content-Type', /json/); assert.equal(res.body.data.html, test.wantHTML); }); it('getText', async function () { - const res = await agent.get(`${endPoint('getText')}&padID=${testPadId}`) + const res = await agent.get(`${endPoint('getText')}?padID=${testPadId}`) + .set("authorization", await common.generateJWTToken()) .expect(200) .expect('Content-Type', /json/); assert.equal(res.body.data.text, test.wantText); diff --git a/src/tests/backend/specs/api/importexportGetPost.ts b/src/tests/backend/specs/api/importexportGetPost.ts index 40bfb5552..355699bc2 100644 --- a/src/tests/backend/specs/api/importexportGetPost.ts +++ b/src/tests/backend/specs/api/importexportGetPost.ts @@ -5,6 +5,8 @@ */ import {MapArrayType} from "../../../../node/types/MapType"; +import {SuperTestStatic} from "supertest"; +import TestAgent from "supertest/lib/agent"; const assert = require('assert').strict; const common = require('../../common'); @@ -21,8 +23,7 @@ const wordXDoc = fs.readFileSync(`${__dirname}/test.docx`); const odtDoc = fs.readFileSync(`${__dirname}/test.odt`); const pdfDoc = fs.readFileSync(`${__dirname}/test.pdf`); -let agent:any; -const apiKey = common.apiKey; +let agent: TestAgent; const apiVersion = 1; const testPadId = makeid(); const testPadIdEnc = encodeURIComponent(testPadId); @@ -41,6 +42,7 @@ describe(__filename, function () { describe('Connectivity', function () { it('can connect', async function () { await agent.get('/api/') + .set("authorization", await common.generateJWTToken()) .expect(200) .expect('Content-Type', /json/); }); @@ -49,6 +51,7 @@ describe(__filename, function () { describe('API Versioning', function () { it('finds the version tag', async function () { await agent.get('/api/') + .set("authorization", await common.generateJWTToken()) .expect(200) .expect((res:any) => assert(res.body.currentVersion)); }); @@ -103,14 +106,17 @@ describe(__filename, function () { }); it('creates a new Pad, imports content to it, checks that content', async function () { - await agent.get(`${endPoint('createPad')}&padID=${testPadId}`) + await agent.get(`${endPoint('createPad')}?padID=${testPadId}`) + .set("authorization", await common.generateJWTToken()) .expect(200) .expect('Content-Type', /json/) .expect((res:any) => assert.equal(res.body.code, 0)); await agent.post(`/p/${testPadId}/import`) + .set("authorization", await common.generateJWTToken()) .attach('file', padText, {filename: '/test.txt', contentType: 'text/plain'}) .expect(200); - await agent.get(`${endPoint('getText')}&padID=${testPadId}`) + await agent.get(`${endPoint('getText')}?padID=${testPadId}`) + .set("authorization", await common.generateJWTToken()) .expect(200) .expect((res:any) => assert.equal(res.body.data.text, padText.toString())); }); @@ -122,9 +128,11 @@ describe(__filename, function () { beforeEach(async function () { if (readOnlyId != null) return; await agent.post(`/p/${testPadId}/import`) + .set("authorization", await common.generateJWTToken()) .attach('file', padText, {filename: '/test.txt', contentType: 'text/plain'}) .expect(200); - const res = await agent.get(`${endPoint('getReadOnlyID')}&padID=${testPadId}`) + const res = await agent.get(`${endPoint('getReadOnlyID')}?padID=${testPadId}`) + .set("authorization", await common.generateJWTToken()) .expect(200) .expect('Content-Type', /json/) .expect((res:any) => assert.equal(res.body.code, 0)); @@ -145,7 +153,8 @@ describe(__filename, function () { // This ought to be before(), but it must run after the top-level beforeEach() above. beforeEach(async function () { if (text != null) return; - let req = agent.get(`/p/${readOnlyId}/export/${exportType}`); + let req = agent.get(`/p/${readOnlyId}/export/${exportType}`) + .set("authorization", await common.generateJWTToken()); if (authn) req = req.auth('user', 'user-password'); const res = await req .expect(200) @@ -163,6 +172,7 @@ describe(__filename, function () { it('re-import to read-only pad ID gives 403 forbidden', async function () { let req = agent.post(`/p/${readOnlyId}/import`) + .set("authorization", await common.generateJWTToken()) .attach('file', Buffer.from(text), { filename: `/test.${exportType}`, contentType: 'text/plain', @@ -175,6 +185,7 @@ describe(__filename, function () { // The new pad ID must differ from testPadId because Etherpad refuses to import // .etherpad files on top of a pad that already has edits. let req = agent.post(`/p/${testPadId}_import/import`) + .set("authorization", await common.generateJWTToken()) .attach('file', Buffer.from(text), { filename: `/test.${exportType}`, contentType: 'text/plain', @@ -200,6 +211,7 @@ describe(__filename, function () { // TODO: fix support for .doc files.. it('Tries to import .doc that uses soffice or abiword', async function () { await agent.post(`/p/${testPadId}/import`) + .set("authorization", await common.generateJWTToken()) .attach('file', wordDoc, {filename: '/test.doc', contentType: 'application/msword'}) .expect(200) .expect('Content-Type', /json/) @@ -212,6 +224,7 @@ describe(__filename, function () { it('exports DOC', async function () { await agent.get(`/p/${testPadId}/export/doc`) + .set("authorization", await common.generateJWTToken()) .buffer(true).parse(superagent.parse['application/octet-stream']) .expect(200) .expect((res:any) => assert(res.body.length >= 9000)); @@ -219,6 +232,7 @@ describe(__filename, function () { it('Tries to import .docx that uses soffice or abiword', async function () { await agent.post(`/p/${testPadId}/import`) + .set("authorization", await common.generateJWTToken()) .attach('file', wordXDoc, { filename: '/test.docx', contentType: @@ -235,6 +249,7 @@ describe(__filename, function () { it('exports DOC from imported DOCX', async function () { await agent.get(`/p/${testPadId}/export/doc`) + .set("authorization", await common.generateJWTToken()) .buffer(true).parse(superagent.parse['application/octet-stream']) .expect(200) .expect((res:any) => assert(res.body.length >= 9100)); @@ -242,6 +257,7 @@ describe(__filename, function () { it('Tries to import .pdf that uses soffice or abiword', async function () { await agent.post(`/p/${testPadId}/import`) + .set("authorization", await common.generateJWTToken()) .attach('file', pdfDoc, {filename: '/test.pdf', contentType: 'application/pdf'}) .expect(200) .expect('Content-Type', /json/) @@ -254,6 +270,7 @@ describe(__filename, function () { it('exports PDF', async function () { await agent.get(`/p/${testPadId}/export/pdf`) + .set("authorization", await common.generateJWTToken()) .buffer(true).parse(superagent.parse['application/octet-stream']) .expect(200) .expect((res:any) => assert(res.body.length >= 1000)); @@ -261,6 +278,7 @@ describe(__filename, function () { it('Tries to import .odt that uses soffice or abiword', async function () { await agent.post(`/p/${testPadId}/import`) + .set("authorization", await common.generateJWTToken()) .attach('file', odtDoc, {filename: '/test.odt', contentType: 'application/odt'}) .expect(200) .expect('Content-Type', /json/) @@ -273,6 +291,7 @@ describe(__filename, function () { it('exports ODT', async function () { await agent.get(`/p/${testPadId}/export/odt`) + .set("authorization", await common.generateJWTToken()) .buffer(true).parse(superagent.parse['application/octet-stream']) .expect(200) .expect((res:any) => assert(res.body.length >= 7000)); @@ -282,6 +301,7 @@ describe(__filename, function () { it('Tries to import .etherpad', async function () { this.timeout(3000); await agent.post(`/p/${testPadId}/import`) + .set("authorization", await common.generateJWTToken()) .attach('file', etherpadDoc, { filename: '/test.etherpad', contentType: 'application/etherpad', @@ -298,6 +318,7 @@ describe(__filename, function () { it('exports Etherpad', async function () { this.timeout(3000); await agent.get(`/p/${testPadId}/export/etherpad`) + .set("authorization", await common.generateJWTToken()) .buffer(true).parse(superagent.parse.text) .expect(200) .expect(/hello/); @@ -306,6 +327,7 @@ describe(__filename, function () { it('exports HTML for this Etherpad file', async function () { this.timeout(3000); await agent.get(`/p/${testPadId}/export/html`) + .set("authorization", await common.generateJWTToken()) .expect(200) .expect('content-type', 'text/html; charset=utf-8') .expect(/
    • hello<\/ul><\/li><\/ul>/); @@ -315,6 +337,7 @@ describe(__filename, function () { this.timeout(3000); settings.allowUnknownFileEnds = false; await agent.post(`/p/${testPadId}/import`) + .set("authorization", await common.generateJWTToken()) .attach('file', padText, {filename: '/test.xasdasdxx', contentType: 'weirdness/jobby'}) .expect(400) .expect('Content-Type', /json/) @@ -380,6 +403,8 @@ describe(__filename, function () { // that a buggy makeGoodExport() doesn't cause checks to accidentally pass. const records = makeGoodExport(); await deleteTestPad(); + const importedPads = await importEtherpad(records) + console.log(importedPads) await importEtherpad(records) .expect(200) .expect('Content-Type', /json/) @@ -389,6 +414,7 @@ describe(__filename, function () { data: {directDatabaseAccess: true}, })); await agent.get(`/p/${testPadId}/export/txt`) + .set("authorization", await common.generateJWTToken()) .expect(200) .buffer(true).parse(superagent.parse.text) .expect((res:any) => assert.match(res.text, /foo/)); @@ -397,19 +423,19 @@ describe(__filename, function () { it('missing rev', async function () { const records:MapArrayType = makeGoodExport(); delete records['pad:testing:revs:0']; - await importEtherpad(records).expect(500); + importEtherpad(records).expect(500); }); it('bad changeset', async function () { const records = makeGoodExport(); records['pad:testing:revs:0'].changeset = 'garbage'; - await importEtherpad(records).expect(500); + importEtherpad(records).expect(500); }); it('missing attrib in pool', async function () { const records = makeGoodExport(); records['pad:testing'].pool.nextNum++; - await importEtherpad(records).expect(500); + (importEtherpad(records)).expect(500); }); it('extra attrib in pool', async function () { @@ -417,7 +443,7 @@ describe(__filename, function () { const pool = records['pad:testing'].pool; // @ts-ignore pool.numToAttrib[pool.nextNum] = ['key', 'value']; - await importEtherpad(records).expect(500); + (importEtherpad(records)).expect(500); }); it('changeset refers to non-existent attrib', async function () { @@ -434,19 +460,19 @@ describe(__filename, function () { text: 'asdffoo\n', attribs: '*1+4|1+4', }; - await importEtherpad(records).expect(500); + (importEtherpad(records)).expect(500); }); it('pad atext does not match', async function () { const records = makeGoodExport(); records['pad:testing'].atext.attribs = `*0${records['pad:testing'].atext.attribs}`; - await importEtherpad(records).expect(500); + (importEtherpad(records)).expect(500); }); it('missing chat message', async function () { const records:MapArrayType = makeGoodExport(); delete records['pad:testing:chat:0']; - await importEtherpad(records).expect(500); + importEtherpad(records).expect(500); }); }); @@ -523,7 +549,7 @@ describe(__filename, function () { }, }); - const importEtherpad = (records:MapArrayType) => agent.post(`/p/${testPadId}/import`) + const importEtherpad = (records: MapArrayType) => agent.post(`/p/${testPadId}/import`) .attach('file', Buffer.from(JSON.stringify(records), 'utf8'), { filename: '/test.etherpad', contentType: 'application/etherpad', @@ -543,6 +569,7 @@ describe(__filename, function () { data: {directDatabaseAccess: true}, })); await agent.get(`/p/${testPadId}/export/txt`) + .set("authorization", await common.generateJWTToken()) .expect(200) .buffer(true).parse(superagent.parse.text) .expect((res:any) => assert.equal(res.text, 'oofoo\n')); @@ -550,6 +577,7 @@ describe(__filename, function () { it('txt request rev 1', async function () { await agent.get(`/p/${testPadId}/1/export/txt`) + .set("authorization", await common.generateJWTToken()) .expect(200) .buffer(true).parse(superagent.parse.text) .expect((res:any) => assert.equal(res.text, 'ofoo\n')); @@ -557,6 +585,7 @@ describe(__filename, function () { it('txt request rev 2', async function () { await agent.get(`/p/${testPadId}/2/export/txt`) + .set("authorization", await common.generateJWTToken()) .expect(200) .buffer(true).parse(superagent.parse.text) .expect((res:any) => assert.equal(res.text, 'oofoo\n')); @@ -564,6 +593,7 @@ describe(__filename, function () { it('txt request rev 1test returns rev 1', async function () { await agent.get(`/p/${testPadId}/1test/export/txt`) + .set("authorization", await common.generateJWTToken()) .expect(200) .buffer(true).parse(superagent.parse.text) .expect((res:any) => assert.equal(res.text, 'ofoo\n')); @@ -571,6 +601,7 @@ describe(__filename, function () { it('txt request rev test1 is 403', async function () { await agent.get(`/p/${testPadId}/test1/export/txt`) + .set("authorization", await common.generateJWTToken()) .expect(500) .buffer(true).parse(superagent.parse.text) .expect((res:any) => assert.match(res.text, /rev is not a number/)); @@ -578,6 +609,7 @@ describe(__filename, function () { it('txt request rev 5 returns head rev', async function () { await agent.get(`/p/${testPadId}/5/export/txt`) + .set("authorization", await common.generateJWTToken()) .expect(200) .buffer(true).parse(superagent.parse.text) .expect((res:any) => assert.equal(res.text, 'oofoo\n')); @@ -585,6 +617,7 @@ describe(__filename, function () { it('html request rev 1', async function () { await agent.get(`/p/${testPadId}/1/export/html`) + .set("authorization", await common.generateJWTToken()) .expect(200) .buffer(true).parse(superagent.parse.text) .expect((res:any) => assert.match(res.text, /ofoo
      /)); @@ -592,6 +625,7 @@ describe(__filename, function () { it('html request rev 2', async function () { await agent.get(`/p/${testPadId}/2/export/html`) + .set("authorization", await common.generateJWTToken()) .expect(200) .buffer(true).parse(superagent.parse.text) .expect((res:any) => assert.match(res.text, /oofoo
      /)); @@ -599,6 +633,7 @@ describe(__filename, function () { it('html request rev 1test returns rev 1', async function () { await agent.get(`/p/${testPadId}/1test/export/html`) + .set("authorization", await common.generateJWTToken()) .expect(200) .buffer(true).parse(superagent.parse.text) .expect((res:any) => assert.match(res.text, /ofoo
      /)); @@ -606,6 +641,7 @@ describe(__filename, function () { it('html request rev test1 results in 500 response', async function () { await agent.get(`/p/${testPadId}/test1/export/html`) + .set("authorization", await common.generateJWTToken()) .expect(500) .buffer(true).parse(superagent.parse.text) .expect((res:any) => assert.match(res.text, /rev is not a number/)); @@ -613,6 +649,7 @@ describe(__filename, function () { it('html request rev 5 returns head rev', async function () { await agent.get(`/p/${testPadId}/5/export/html`) + .set("authorization", await common.generateJWTToken()) .expect(200) .buffer(true).parse(superagent.parse.text) .expect((res:any) => assert.match(res.text, /oofoo
      /)); @@ -643,6 +680,7 @@ describe(__filename, function () { it('!authn !exist -> create', async function () { await agent.post(`/p/${testPadIdEnc}/import`) + .set("authorization", await common.generateJWTToken()) .attach('file', padText, {filename: '/test.txt', contentType: 'text/plain'}) .expect(200); assert(await padManager.doesPadExist(testPadId)); @@ -653,6 +691,7 @@ describe(__filename, function () { it('!authn exist -> replace', async function () { const pad = await createTestPad('before import'); await agent.post(`/p/${testPadIdEnc}/import`) + .set("authorization", await common.generateJWTToken()) .attach('file', padText, {filename: '/test.txt', contentType: 'text/plain'}) .expect(200); assert(await padManager.doesPadExist(testPadId)); @@ -662,6 +701,7 @@ describe(__filename, function () { it('authn anonymous !exist -> fail', async function () { settings.requireAuthentication = true; await agent.post(`/p/${testPadIdEnc}/import`) + .set("authorization", await common.generateJWTToken()) .attach('file', padText, {filename: '/test.txt', contentType: 'text/plain'}) .expect(401); assert(!(await padManager.doesPadExist(testPadId))); @@ -671,6 +711,7 @@ describe(__filename, function () { settings.requireAuthentication = true; const pad = await createTestPad('before import\n'); await agent.post(`/p/${testPadIdEnc}/import`) + .set("authorization", await common.generateJWTToken()) .attach('file', padText, {filename: '/test.txt', contentType: 'text/plain'}) .expect(401); assert.equal(pad.text(), 'before import\n'); @@ -679,6 +720,7 @@ describe(__filename, function () { it('authn user create !exist -> create', async function () { settings.requireAuthentication = true; await agent.post(`/p/${testPadIdEnc}/import`) + .set("authorization", await common.generateJWTToken()) .auth('user', 'user-password') .attach('file', padText, {filename: '/test.txt', contentType: 'text/plain'}) .expect(200); @@ -691,6 +733,7 @@ describe(__filename, function () { settings.requireAuthentication = true; authorize = () => 'modify'; await agent.post(`/p/${testPadIdEnc}/import`) + .set("authorization", await common.generateJWTToken()) .auth('user', 'user-password') .attach('file', padText, {filename: '/test.txt', contentType: 'text/plain'}) .expect(403); @@ -701,6 +744,7 @@ describe(__filename, function () { settings.requireAuthentication = true; authorize = () => 'readOnly'; await agent.post(`/p/${testPadIdEnc}/import`) + .set("authorization", await common.generateJWTToken()) .auth('user', 'user-password') .attach('file', padText, {filename: '/test.txt', contentType: 'text/plain'}) .expect(403); @@ -711,6 +755,7 @@ describe(__filename, function () { settings.requireAuthentication = true; const pad = await createTestPad('before import\n'); await agent.post(`/p/${testPadIdEnc}/import`) + .set("authorization", await common.generateJWTToken()) .auth('user', 'user-password') .attach('file', padText, {filename: '/test.txt', contentType: 'text/plain'}) .expect(200); @@ -722,6 +767,7 @@ describe(__filename, function () { authorize = () => 'modify'; const pad = await createTestPad('before import\n'); await agent.post(`/p/${testPadIdEnc}/import`) + .set("authorization", await common.generateJWTToken()) .auth('user', 'user-password') .attach('file', padText, {filename: '/test.txt', contentType: 'text/plain'}) .expect(200); @@ -733,6 +779,7 @@ describe(__filename, function () { settings.requireAuthentication = true; authorize = () => 'readOnly'; await agent.post(`/p/${testPadIdEnc}/import`) + .set("authorization", await common.generateJWTToken()) .auth('user', 'user-password') .attach('file', padText, {filename: '/test.txt', contentType: 'text/plain'}) .expect(403); @@ -744,7 +791,7 @@ describe(__filename, function () { const endPoint = (point: string, version?:string) => { - return `/api/${version || apiVersion}/${point}?apikey=${apiKey}`; + return `/api/${version || apiVersion}/${point}`; }; function makeid() { diff --git a/src/tests/backend/specs/api/instance.ts b/src/tests/backend/specs/api/instance.ts index fc348e5af..2bf51bf86 100644 --- a/src/tests/backend/specs/api/instance.ts +++ b/src/tests/backend/specs/api/instance.ts @@ -8,10 +8,9 @@ const common = require('../../common'); let agent:any; -const apiKey = common.apiKey; const apiVersion = '1.2.14'; -const endPoint = (point: string, version?: number) => `/api/${version || apiVersion}/${point}?apikey=${apiKey}`; +const endPoint = (point: string, version?: number) => `/api/${version || apiVersion}/${point}`; describe(__filename, function () { before(async function () { agent = await common.init(); }); @@ -27,6 +26,7 @@ describe(__filename, function () { describe('getStats', function () { it('Gets the stats of a running instance', async function () { await agent.get(endPoint('getStats')) + .set("Authorization", await common.generateJWTToken()) .expect((res:any) => { if (res.body.code !== 0) throw new Error('getStats() failed'); diff --git a/src/tests/backend/specs/api/pad.ts b/src/tests/backend/specs/api/pad.ts index 180494bb2..2c2ae142e 100644 --- a/src/tests/backend/specs/api/pad.ts +++ b/src/tests/backend/specs/api/pad.ts @@ -21,7 +21,7 @@ const anotherPadId = makeid(); let lastEdited = ''; const text = generateLongText(); -const endPoint = (point: string, version?: string) => `/api/${version || apiVersion}/${point}?apikey=${apiKey}`; +const endPoint = (point: string, version?: string) => `/api/${version || apiVersion}/${point}`; /* * Html document with nested lists of different types, to test its import and @@ -63,7 +63,8 @@ describe(__filename, function () { it('errors with invalid APIKey', async function () { // This is broken because Etherpad doesn't handle HTTP codes properly see #2343 // If your APIKey is password you deserve to fail all tests anyway - await agent.get(`/api/${apiVersion}/createPad?apikey=password&padID=test`) + await agent.get(`/api/${apiVersion}/createPad?padID=test`) + .set("Authorization", (await common.generateJWTToken()).substring(0, 10)) .expect(401); }); }); @@ -113,20 +114,23 @@ describe(__filename, function () { describe('Tests', function () { it('deletes a Pad that does not exist', async function () { - await agent.get(`${endPoint('deletePad')}&padID=${testPadId}`) + await agent.get(`${endPoint('deletePad')}?padID=${testPadId}`) + .set("Authorization", (await common.generateJWTToken())) .expect(200) // @TODO: we shouldn't expect 200 here since the pad may not exist .expect('Content-Type', /json/); }); it('creates a new Pad', async function () { - const res = await agent.get(`${endPoint('createPad')}&padID=${testPadId}`) + const res = await agent.get(`${endPoint('createPad')}?padID=${testPadId}`) + .set("Authorization", (await common.generateJWTToken())) .expect(200) .expect('Content-Type', /json/); assert.equal(res.body.code, 0); }); it('gets revision count of Pad', async function () { - const res = await agent.get(`${endPoint('getRevisionsCount')}&padID=${testPadId}`) + const res = await agent.get(`${endPoint('getRevisionsCount')}?padID=${testPadId}`) + .set("Authorization", (await common.generateJWTToken())) .expect(200) .expect('Content-Type', /json/); assert.equal(res.body.code, 0); @@ -134,7 +138,8 @@ describe(__filename, function () { }); it('gets saved revisions count of Pad', async function () { - const res = await agent.get(`${endPoint('getSavedRevisionsCount')}&padID=${testPadId}`) + const res = await agent.get(`${endPoint('getSavedRevisionsCount')}?padID=${testPadId}`) + .set("Authorization", (await common.generateJWTToken())) .expect(200) .expect('Content-Type', /json/); assert.equal(res.body.code, 0); @@ -142,7 +147,8 @@ describe(__filename, function () { }); it('gets saved revision list of Pad', async function () { - const res = await agent.get(`${endPoint('listSavedRevisions')}&padID=${testPadId}`) + const res = await agent.get(`${endPoint('listSavedRevisions')}?padID=${testPadId}`) + .set("Authorization", (await common.generateJWTToken())) .expect(200) .expect('Content-Type', /json/); assert.equal(res.body.code, 0); @@ -150,7 +156,8 @@ describe(__filename, function () { }); it('get the HTML of Pad', async function () { - const res = await agent.get(`${endPoint('getHTML')}&padID=${testPadId}`) + const res = await agent.get(`${endPoint('getHTML')}?padID=${testPadId}`) + .set("Authorization", (await common.generateJWTToken())) .expect(200) .expect('Content-Type', /json/); assert(res.body.data.html.length > 1); @@ -158,13 +165,15 @@ describe(__filename, function () { it('list all pads', async function () { const res = await agent.get(endPoint('listAllPads')) + .set("Authorization", (await common.generateJWTToken())) .expect(200) .expect('Content-Type', /json/); assert(res.body.data.padIDs.includes(testPadId)); }); it('deletes the Pad', async function () { - const res = await agent.get(`${endPoint('deletePad')}&padID=${testPadId}`) + const res = await agent.get(`${endPoint('deletePad')}?padID=${testPadId}`) + .set("Authorization", (await common.generateJWTToken())) .expect(200) .expect('Content-Type', /json/); assert.equal(res.body.code, 0); @@ -172,27 +181,31 @@ describe(__filename, function () { it('list all pads again', async function () { const res = await agent.get(endPoint('listAllPads')) + .set("Authorization", (await common.generateJWTToken())) .expect(200) .expect('Content-Type', /json/); assert(!res.body.data.padIDs.includes(testPadId)); }); it('get the HTML of a Pad -- Should return a failure', async function () { - const res = await agent.get(`${endPoint('getHTML')}&padID=${testPadId}`) + const res = await agent.get(`${endPoint('getHTML')}?padID=${testPadId}`) + .set("Authorization", (await common.generateJWTToken())) .expect(200) .expect('Content-Type', /json/); assert.equal(res.body.code, 1); }); it('creates a new Pad with text', async function () { - const res = await agent.get(`${endPoint('createPad')}&padID=${testPadId}&text=testText`) + const res = await agent.get(`${endPoint('createPad')}?padID=${testPadId}&text=testText`) + .set("Authorization", (await common.generateJWTToken())) .expect(200) .expect('Content-Type', /json/); assert.equal(res.body.code, 0); }); it('gets the Pad text and expect it to be testText with trailing \\n', async function () { - const res = await agent.get(`${endPoint('getText')}&padID=${testPadId}`) + const res = await agent.get(`${endPoint('getText')}?padID=${testPadId}`) + .set("Authorization", (await common.generateJWTToken())) .expect(200) .expect('Content-Type', /json/); assert.equal(res.body.data.text, 'testText\n'); @@ -200,6 +213,7 @@ describe(__filename, function () { it('set text', async function () { const res = await agent.post(endPoint('setText')) + .set("Authorization", (await common.generateJWTToken())) .send({ padID: testPadId, text: 'testTextTwo', @@ -210,28 +224,32 @@ describe(__filename, function () { }); it('gets the Pad text', async function () { - const res = await agent.get(`${endPoint('getText')}&padID=${testPadId}`) + const res = await agent.get(`${endPoint('getText')}?padID=${testPadId}`) + .set("Authorization", (await common.generateJWTToken())) .expect(200) .expect('Content-Type', /json/); assert.equal(res.body.data.text, 'testTextTwo\n'); }); it('gets Revision Count of a Pad', async function () { - const res = await agent.get(`${endPoint('getRevisionsCount')}&padID=${testPadId}`) + const res = await agent.get(`${endPoint('getRevisionsCount')}?padID=${testPadId}`) + .set("Authorization", (await common.generateJWTToken())) .expect(200) .expect('Content-Type', /json/); assert.equal(res.body.data.revisions, 1); }); it('saves Revision', async function () { - const res = await agent.get(`${endPoint('saveRevision')}&padID=${testPadId}`) + const res = await agent.get(`${endPoint('saveRevision')}?padID=${testPadId}`) + .set("Authorization", (await common.generateJWTToken())) .expect(200) .expect('Content-Type', /json/); assert.equal(res.body.code, 0); }); it('gets saved revisions count of Pad again', async function () { - const res = await agent.get(`${endPoint('getSavedRevisionsCount')}&padID=${testPadId}`) + const res = await agent.get(`${endPoint('getSavedRevisionsCount')}?padID=${testPadId}`) + .set("Authorization", (await common.generateJWTToken())) .expect(200) .expect('Content-Type', /json/); assert.equal(res.body.code, 0); @@ -239,7 +257,8 @@ describe(__filename, function () { }); it('gets saved revision list of Pad again', async function () { - const res = await agent.get(`${endPoint('listSavedRevisions')}&padID=${testPadId}`) + const res = await agent.get(`${endPoint('listSavedRevisions')}?padID=${testPadId}`) + .set("Authorization", (await common.generateJWTToken())) .expect(200) .expect('Content-Type', /json/); assert.equal(res.body.code, 0); @@ -247,28 +266,32 @@ describe(__filename, function () { }); it('gets User Count of a Pad', async function () { - const res = await agent.get(`${endPoint('padUsersCount')}&padID=${testPadId}`) + const res = await agent.get(`${endPoint('padUsersCount')}?padID=${testPadId}`) + .set("Authorization", (await common.generateJWTToken())) .expect(200) .expect('Content-Type', /json/); assert.equal(res.body.data.padUsersCount, 0); }); it('Gets the Read Only ID of a Pad', async function () { - const res = await agent.get(`${endPoint('getReadOnlyID')}&padID=${testPadId}`) + const res = await agent.get(`${endPoint('getReadOnlyID')}?padID=${testPadId}`) + .set("Authorization", (await common.generateJWTToken())) .expect(200) .expect('Content-Type', /json/); assert(res.body.data.readOnlyID); }); it('Get Authors of the Pad', async function () { - const res = await agent.get(`${endPoint('listAuthorsOfPad')}&padID=${testPadId}`) + const res = await agent.get(`${endPoint('listAuthorsOfPad')}?padID=${testPadId}`) + .set("Authorization", (await common.generateJWTToken())) .expect(200) .expect('Content-Type', /json/); assert.equal(res.body.data.authorIDs.length, 0); }); it('Get When Pad was left Edited', async function () { - const res = await agent.get(`${endPoint('getLastEdited')}&padID=${testPadId}`) + const res = await agent.get(`${endPoint('getLastEdited')}?padID=${testPadId}`) + .set("Authorization", (await common.generateJWTToken())) .expect(200) .expect('Content-Type', /json/); assert(res.body.data.lastEdited); @@ -277,6 +300,7 @@ describe(__filename, function () { it('set text again', async function () { const res = await agent.post(endPoint('setText')) + .set("Authorization", (await common.generateJWTToken())) .send({ padID: testPadId, text: 'testTextThree', @@ -287,35 +311,40 @@ describe(__filename, function () { }); it('Get When Pad was left Edited again', async function () { - const res = await agent.get(`${endPoint('getLastEdited')}&padID=${testPadId}`) + const res = await agent.get(`${endPoint('getLastEdited')}?padID=${testPadId}`) + .set("Authorization", (await common.generateJWTToken())) .expect(200) .expect('Content-Type', /json/); assert(res.body.data.lastEdited > lastEdited); }); it('gets User Count of a Pad again', async function () { - const res = await agent.get(`${endPoint('padUsers')}&padID=${testPadId}`) + const res = await agent.get(`${endPoint('padUsers')}?padID=${testPadId}`) + .set("Authorization", (await common.generateJWTToken())) .expect(200) .expect('Content-Type', /json/); assert.equal(res.body.data.padUsers.length, 0); }); it('deletes a Pad', async function () { - const res = await agent.get(`${endPoint('deletePad')}&padID=${testPadId}`) + const res = await agent.get(`${endPoint('deletePad')}?padID=${testPadId}`) + .set("Authorization", (await common.generateJWTToken())) .expect(200) .expect('Content-Type', /json/); assert.equal(res.body.code, 0); }); it('creates the Pad again', async function () { - const res = await agent.get(`${endPoint('createPad')}&padID=${testPadId}`) + const res = await agent.get(`${endPoint('createPad')}?padID=${testPadId}`) + .set("Authorization", (await common.generateJWTToken())) .expect(200) .expect('Content-Type', /json/); assert.equal(res.body.code, 0); }); it('Sets text on a pad Id', async function () { - const res = await agent.post(`${endPoint('setText')}&padID=${testPadId}`) + const res = await agent.post(`${endPoint('setText')}?padID=${testPadId}`) + .set("Authorization", (await common.generateJWTToken())) .field({text}) .expect(200) .expect('Content-Type', /json/); @@ -323,7 +352,8 @@ describe(__filename, function () { }); it('Gets text on a pad Id', async function () { - const res = await agent.get(`${endPoint('getText')}&padID=${testPadId}`) + const res = await agent.get(`${endPoint('getText')}?padID=${testPadId}`) + .set("Authorization", (await common.generateJWTToken())) .expect(200) .expect('Content-Type', /json/); assert.equal(res.body.code, 0); @@ -331,7 +361,8 @@ describe(__filename, function () { }); it('Sets text on a pad Id including an explicit newline', async function () { - const res = await agent.post(`${endPoint('setText')}&padID=${testPadId}`) + const res = await agent.post(`${endPoint('setText')}?padID=${testPadId}`) + .set("Authorization", (await common.generateJWTToken())) .field({text: `${text}\n`}) .expect(200) .expect('Content-Type', /json/); @@ -339,7 +370,8 @@ describe(__filename, function () { }); it("Gets text on a pad Id and doesn't have an excess newline", async function () { - const res = await agent.get(`${endPoint('getText')}&padID=${testPadId}`) + const res = await agent.get(`${endPoint('getText')}?padID=${testPadId}`) + .set("Authorization", (await common.generateJWTToken())) .expect(200) .expect('Content-Type', /json/); assert.equal(res.body.code, 0); @@ -347,7 +379,8 @@ describe(__filename, function () { }); it('Gets when pad was last edited', async function () { - const res = await agent.get(`${endPoint('getLastEdited')}&padID=${testPadId}`) + const res = await agent.get(`${endPoint('getLastEdited')}?padID=${testPadId}`) + .set("Authorization", (await common.generateJWTToken())) .expect(200) .expect('Content-Type', /json/); assert.notEqual(res.body.lastEdited, 0); @@ -355,14 +388,16 @@ describe(__filename, function () { it('Move a Pad to a different Pad ID', async function () { const res = await agent.get( - `${endPoint('movePad')}&sourceID=${testPadId}&destinationID=${newPadId}&force=true`) + `${endPoint('movePad')}?sourceID=${testPadId}&destinationID=${newPadId}&force=true`) + .set("Authorization", (await common.generateJWTToken())) .expect(200) .expect('Content-Type', /json/); assert.equal(res.body.code, 0); }); it('Gets text from new pad', async function () { - const res = await agent.get(`${endPoint('getText')}&padID=${newPadId}`) + const res = await agent.get(`${endPoint('getText')}?padID=${newPadId}`) + .set("Authorization", (await common.generateJWTToken())) .expect(200) .expect('Content-Type', /json/); assert.equal(res.body.data.text, `${text}\n`); @@ -370,21 +405,24 @@ describe(__filename, function () { it('Move pad back to original ID', async function () { const res = await agent.get( - `${endPoint('movePad')}&sourceID=${newPadId}&destinationID=${testPadId}&force=false`) + `${endPoint('movePad')}?sourceID=${newPadId}&destinationID=${testPadId}&force=false`) + .set("Authorization", (await common.generateJWTToken())) .expect(200) .expect('Content-Type', /json/); assert.equal(res.body.code, 0); }); it('Get text using original ID', async function () { - const res = await agent.get(`${endPoint('getText')}&padID=${testPadId}`) + const res = await agent.get(`${endPoint('getText')}?padID=${testPadId}`) + .set("Authorization", (await common.generateJWTToken())) .expect(200) .expect('Content-Type', /json/); assert.equal(res.body.data.text, `${text}\n`); }); it('Get last edit of original ID', async function () { - const res = await agent.get(`${endPoint('getLastEdited')}&padID=${testPadId}`) + const res = await agent.get(`${endPoint('getLastEdited')}?padID=${testPadId}`) + .set("Authorization", (await common.generateJWTToken())) .expect(200) .expect('Content-Type', /json/); assert.notEqual(res.body.lastEdited, 0); @@ -392,11 +430,13 @@ describe(__filename, function () { it('Append text to a pad Id', async function () { let res = await agent.get( - `${endPoint('appendText', '1.2.13')}&padID=${testPadId}&text=hello`) + `${endPoint('appendText', '1.2.13')}?padID=${testPadId}&text=hello`) + .set("Authorization", (await common.generateJWTToken())) .expect(200) .expect('Content-Type', /json/); assert.equal(res.body.code, 0); - res = await agent.get(`${endPoint('getText')}&padID=${testPadId}`) + res = await agent.get(`${endPoint('getText')}?padID=${testPadId}`) + .set("Authorization", (await common.generateJWTToken())) .expect(200) .expect('Content-Type', /json/); assert.equal(res.body.code, 0); @@ -404,7 +444,8 @@ describe(__filename, function () { }); it('getText of old revision', async function () { - let res = await agent.get(`${endPoint('getRevisionsCount')}&padID=${testPadId}`) + let res = await agent.get(`${endPoint('getRevisionsCount')}?padID=${testPadId}`) + .set("Authorization", (await common.generateJWTToken())) .expect(200) .expect('Content-Type', /json/); assert.equal(res.body.code, 0); @@ -412,7 +453,8 @@ describe(__filename, function () { assert(rev != null); assert(Number.isInteger(rev)); assert(rev > 0); - res = await agent.get(`${endPoint('getText')}&padID=${testPadId}&rev=${rev - 1}`) + res = await agent.get(`${endPoint('getText')}?padID=${testPadId}&rev=${rev - 1}`) + .set("Authorization", (await common.generateJWTToken())) .expect(200) .expect('Content-Type', /json/); assert.equal(res.body.code, 0); @@ -422,6 +464,7 @@ describe(__filename, function () { it('Sets the HTML of a Pad attempting to pass ugly HTML', async function () { const html = '
      Hello HTML
      '; const res = await agent.post(endPoint('setHTML')) + .set("Authorization", (await common.generateJWTToken())) .send({ padID: testPadId, html, @@ -433,6 +476,7 @@ describe(__filename, function () { it('Pad with complex nested lists of different types', async function () { let res = await agent.post(endPoint('setHTML')) + .set("Authorization", (await common.generateJWTToken())) .send({ padID: testPadId, html: ulHtml, @@ -440,7 +484,8 @@ describe(__filename, function () { .expect(200) .expect('Content-Type', /json/); assert.equal(res.body.code, 0); - res = await agent.get(`${endPoint('getHTML')}&padID=${testPadId}`) + res = await agent.get(`${endPoint('getHTML')}?padID=${testPadId}`) + .set("Authorization", (await common.generateJWTToken())) .expect(200) .expect('Content-Type', /json/); const receivedHtml = res.body.data.html.replace('
      ', '').toLowerCase(); @@ -448,11 +493,13 @@ describe(__filename, function () { }); it('Pad with white space between list items', async function () { - let res = await agent.get(`${endPoint('setHTML')}&padID=${testPadId}&html=${ulSpaceHtml}`) + let res = await agent.get(`${endPoint('setHTML')}?padID=${testPadId}&html=${ulSpaceHtml}`) + .set("Authorization", (await common.generateJWTToken())) .expect(200) .expect('Content-Type', /json/); assert.equal(res.body.code, 0); - res = await agent.get(`${endPoint('getHTML')}&padID=${testPadId}`) + res = await agent.get(`${endPoint('getHTML')}?padID=${testPadId}`) + .set("Authorization", (await common.generateJWTToken())) .expect(200) .expect('Content-Type', /json/); const receivedHtml = res.body.data.html.replace('
      ', '').toLowerCase(); @@ -461,7 +508,8 @@ describe(__filename, function () { it('errors if pad can be created', async function () { await Promise.all(['/', '%23', '%3F', '%26'].map(async (badUrlChar) => { - const res = await agent.get(`${endPoint('createPad')}&padID=${badUrlChar}`) + const res = await agent.get(`${endPoint('createPad')}?padID=${badUrlChar}`) + .set("Authorization", (await common.generateJWTToken())) .expect('Content-Type', /json/); assert.equal(res.body.code, 1); })); @@ -469,49 +517,57 @@ describe(__filename, function () { it('copies the content of a existent pad', async function () { const res = await agent.get( - `${endPoint('copyPad')}&sourceID=${testPadId}&destinationID=${copiedPadId}&force=true`) + `${endPoint('copyPad')}?sourceID=${testPadId}&destinationID=${copiedPadId}&force=true`) + .set("Authorization", (await common.generateJWTToken())) .expect(200) .expect('Content-Type', /json/); assert.equal(res.body.code, 0); }); it('does not add an useless revision', async function () { - let res = await agent.post(`${endPoint('setText')}&padID=${testPadId}`) + let res = await agent.post(`${endPoint('setText')}?padID=${testPadId}`) + .set("Authorization", (await common.generateJWTToken())) .field({text: 'identical text\n'}) .expect(200) .expect('Content-Type', /json/); assert.equal(res.body.code, 0); - res = await agent.get(`${endPoint('getText')}&padID=${testPadId}`) + res = await agent.get(`${endPoint('getText')}?padID=${testPadId}`) + .set("Authorization", (await common.generateJWTToken())) .expect(200) .expect('Content-Type', /json/); assert.equal(res.body.data.text, 'identical text\n'); - res = await agent.get(`${endPoint('getRevisionsCount')}&padID=${testPadId}`) + res = await agent.get(`${endPoint('getRevisionsCount')}?padID=${testPadId}`) + .set("Authorization", (await common.generateJWTToken())) .expect(200) .expect('Content-Type', /json/); const revCount = res.body.data.revisions; - res = await agent.post(`${endPoint('setText')}&padID=${testPadId}`) + res = await agent.post(`${endPoint('setText')}?padID=${testPadId}`) + .set("Authorization", (await common.generateJWTToken())) .field({text: 'identical text\n'}) .expect(200) .expect('Content-Type', /json/); assert.equal(res.body.code, 0); - res = await agent.get(`${endPoint('getRevisionsCount')}&padID=${testPadId}`) + res = await agent.get(`${endPoint('getRevisionsCount')}?padID=${testPadId}`) + .set("Authorization", (await common.generateJWTToken())) .expect(200) .expect('Content-Type', /json/); assert.equal(res.body.data.revisions, revCount); }); it('creates a new Pad with empty text', async function () { - await agent.get(`${endPoint('createPad')}&padID=${anotherPadId}&text=`) + await agent.get(`${endPoint('createPad')}?padID=${anotherPadId}&text=`) + .set("Authorization", (await common.generateJWTToken())) .expect('Content-Type', /json/) .expect(200) .expect((res:any) => { assert.equal(res.body.code, 0, 'Unable to create new Pad'); }); - await agent.get(`${endPoint('getText')}&padID=${anotherPadId}`) + await agent.get(`${endPoint('getText')}?padID=${anotherPadId}`) + .set("Authorization", (await common.generateJWTToken())) .expect('Content-Type', /json/) .expect(200) .expect((res:any) => { @@ -521,7 +577,8 @@ describe(__filename, function () { }); it('deletes with empty text', async function () { - await agent.get(`${endPoint('deletePad')}&padID=${anotherPadId}`) + await agent.get(`${endPoint('deletePad')}?padID=${anotherPadId}`) + .set("Authorization", (await common.generateJWTToken())) .expect('Content-Type', /json/) .expect(200) .expect((res: any) => { @@ -543,8 +600,9 @@ describe(__filename, function () { }); it('returns a successful response', async function () { - const res = await agent.get(`${endPoint('copyPadWithoutHistory')}&sourceID=${sourcePadId}` + + const res = await agent.get(`${endPoint('copyPadWithoutHistory')}?sourceID=${sourcePadId}` + `&destinationID=${newPad}&force=false`) + .set("Authorization", (await common.generateJWTToken())) .expect(200) .expect('Content-Type', /json/); assert.equal(res.body.code, 0); @@ -552,10 +610,12 @@ describe(__filename, function () { // this test validates if the source pad's text and attributes are kept it('creates a new pad with the same content as the source pad', async function () { - let res = await agent.get(`${endPoint('copyPadWithoutHistory')}&sourceID=${sourcePadId}` + - `&destinationID=${newPad}&force=false`); + let res = await agent.get(`${endPoint('copyPadWithoutHistory')}?sourceID=${sourcePadId}` + + `&destinationID=${newPad}&force=false`) + .set("Authorization", (await common.generateJWTToken())); assert.equal(res.body.code, 0); - res = await agent.get(`${endPoint('getHTML')}&padID=${newPad}`) + res = await agent.get(`${endPoint('getHTML')}?padID=${newPad}`) + .set("Authorization", (await common.generateJWTToken())) .expect(200); const receivedHtml = res.body.data.html.replace('

      ', '').toLowerCase(); assert.equal(receivedHtml, expectedHtml); @@ -564,8 +624,9 @@ describe(__filename, function () { it('copying to a non-existent group throws an error', async function () { const padWithNonExistentGroup = `notExistentGroup$${newPad}`; const res = await agent.get(`${endPoint('copyPadWithoutHistory')}` + - `&sourceID=${sourcePadId}` + + `?sourceID=${sourcePadId}` + `&destinationID=${padWithNonExistentGroup}&force=true`) + .set("Authorization", (await common.generateJWTToken())) .expect(200); assert.equal(res.body.code, 1); }); @@ -577,16 +638,18 @@ describe(__filename, function () { it('force=false fails', async function () { const res = await agent.get(`${endPoint('copyPadWithoutHistory')}` + - `&sourceID=${sourcePadId}` + + `?sourceID=${sourcePadId}` + `&destinationID=${newPad}&force=false`) + .set("Authorization", (await common.generateJWTToken())) .expect(200); assert.equal(res.body.code, 1); }); it('force=true succeeds', async function () { const res = await agent.get(`${endPoint('copyPadWithoutHistory')}` + - `&sourceID=${sourcePadId}` + + `?sourceID=${sourcePadId}` + `&destinationID=${newPad}&force=true`) + .set("Authorization", (await common.generateJWTToken())) .expect(200); assert.equal(res.body.code, 0); }); @@ -613,7 +676,8 @@ describe(__filename, function () { // state between the two attribute pools caused corruption. const getHtml = async (padId:string) => { - const res = await agent.get(`${endPoint('getHTML')}&padID=${padId}`) + const res = await agent.get(`${endPoint('getHTML')}?padID=${padId}`) + .set("Authorization", (await common.generateJWTToken())) .expect(200) .expect('Content-Type', /json/); assert.equal(res.body.code, 0); @@ -622,6 +686,7 @@ describe(__filename, function () { const setBody = async (padId: string, bodyHtml: string) => { await agent.post(endPoint('setHTML')) + .set("Authorization", (await common.generateJWTToken())) .send({padID: padId, html: `${bodyHtml}`}) .expect(200) .expect('Content-Type', /json/) @@ -631,8 +696,9 @@ describe(__filename, function () { const origHtml = await getHtml(sourcePadId); assert.doesNotMatch(origHtml, //); assert.doesNotMatch(origHtml, //); - await agent.get(`${endPoint('copyPadWithoutHistory')}&sourceID=${sourcePadId}` + + await agent.get(`${endPoint('copyPadWithoutHistory')}?sourceID=${sourcePadId}` + `&destinationID=${newPad}&force=false`) + .set("Authorization", (await common.generateJWTToken())) .expect(200) .expect('Content-Type', /json/) .expect((res:any) => assert.equal(res.body.code, 0)); @@ -672,8 +738,10 @@ describe(__filename, function () { */ const createNewPadWithHtml = async (padId: string, html: string) => { - await agent.get(`${endPoint('createPad')}&padID=${padId}`); + await agent.get(`${endPoint('createPad')}?padID=${padId}`) + .set("Authorization", (await common.generateJWTToken())); await agent.post(endPoint('setHTML')) + .set("Authorization", (await common.generateJWTToken())) .send({ padID: padId, html, diff --git a/src/tests/backend/specs/api/restoreRevision.ts b/src/tests/backend/specs/api/restoreRevision.ts index 28a509012..a779766da 100644 --- a/src/tests/backend/specs/api/restoreRevision.ts +++ b/src/tests/backend/specs/api/restoreRevision.ts @@ -21,6 +21,7 @@ describe(__filename, function () { ...(authorId == null ? {} : {authorId}), })); const res = await agent.get(`/api/${v}/restoreRevision?${p}`) + .set("Authorization", (await common.generateJWTToken())) .expect(200) .expect('Content-Type', /json/); assert.equal(res.body.code, 0); diff --git a/src/tests/backend/specs/api/sessionsAndGroups.ts b/src/tests/backend/specs/api/sessionsAndGroups.ts index 1c3196214..a7e85fbe9 100644 --- a/src/tests/backend/specs/api/sessionsAndGroups.ts +++ b/src/tests/backend/specs/api/sessionsAndGroups.ts @@ -1,25 +1,33 @@ 'use strict'; +import {agent, generateJWTToken, init, logger} from "../../common"; + +import TestAgent from "supertest/lib/agent"; +import supertest from "supertest"; const assert = require('assert').strict; -const common = require('../../common'); const db = require('../../../../node/db/DB'); -let agent:any; -const apiKey = common.apiKey; let apiVersion = 1; let groupID = ''; let authorID = ''; let sessionID = ''; let padID = makeid(); -const endPoint = (point:string) => `/api/${apiVersion}/${point}?apikey=${apiKey}`; +const endPoint = (point:string) => { + return `/api/${apiVersion}/${point}`; +} + +let preparedAgent: TestAgent describe(__filename, function () { - before(async function () { agent = await common.init(); }); + before(async function () { + preparedAgent = await init(); + }); describe('API Versioning', function () { it('errors if can not connect', async function () { - await agent.get('/api/') + await agent!.get('/api/') + .set('Accept', 'application/json') .expect(200) .expect((res:any) => { assert(res.body.currentVersion); @@ -60,7 +68,8 @@ describe(__filename, function () { describe('API: Group creation and deletion', function () { it('createGroup', async function () { - await agent.get(endPoint('createGroup')) + await agent!.get(endPoint('createGroup')) + .set("Authorization", await generateJWTToken()) .expect(200) .expect('Content-Type', /json/) .expect((res:any) => { @@ -71,7 +80,8 @@ describe(__filename, function () { }); it('listSessionsOfGroup for empty group', async function () { - await agent.get(`${endPoint('listSessionsOfGroup')}&groupID=${groupID}`) + await agent!.get(`${endPoint('listSessionsOfGroup')}?groupID=${groupID}`) + .set("Authorization", await generateJWTToken()) .expect(200) .expect('Content-Type', /json/) .expect((res:any) => { @@ -81,7 +91,9 @@ describe(__filename, function () { }); it('deleteGroup', async function () { - await agent.get(`${endPoint('deleteGroup')}&groupID=${groupID}`) + await agent! + .get(`${endPoint('deleteGroup')}?groupID=${groupID}`) + .set("Authorization", await generateJWTToken()) .expect(200) .expect('Content-Type', /json/) .expect((res:any) => { @@ -92,7 +104,8 @@ describe(__filename, function () { it('createGroupIfNotExistsFor', async function () { const mapper = makeid(); let groupId: string; - await agent.get(`${endPoint('createGroupIfNotExistsFor')}&groupMapper=${mapper}`) + await preparedAgent.get(`${endPoint('createGroupIfNotExistsFor')}?groupMapper=${mapper}`) + .set("Authorization", await generateJWTToken()) .expect(200) .expect('Content-Type', /json/) .expect((res:any) => { @@ -101,7 +114,8 @@ describe(__filename, function () { assert(groupId); }); // Passing the same mapper should return the same group ID. - await agent.get(`${endPoint('createGroupIfNotExistsFor')}&groupMapper=${mapper}`) + await preparedAgent.get(`${endPoint('createGroupIfNotExistsFor')}?groupMapper=${mapper}`) + .set("Authorization", await generateJWTToken()) .expect(200) .expect('Content-Type', /json/) .expect((res:any) => { @@ -110,7 +124,8 @@ describe(__filename, function () { }); // Deleting the group should clean up the mapping. assert.equal(await db.get(`mapper2group:${mapper}`), groupId!); - await agent.get(`${endPoint('deleteGroup')}&groupID=${groupId!}`) + await preparedAgent.get(`${endPoint('deleteGroup')}?groupID=${groupId!}`) + .set("Authorization", await generateJWTToken()) .expect(200) .expect('Content-Type', /json/) .expect((res:any) => { @@ -122,7 +137,8 @@ describe(__filename, function () { // Test coverage for https://github.com/ether/etherpad-lite/issues/4227 // Creates a group, creates 2 sessions, 2 pads and then deletes the group. it('createGroup', async function () { - await agent.get(endPoint('createGroup')) + await preparedAgent.get(endPoint('createGroup')) + .set("Authorization", await generateJWTToken()) .expect(200) .expect('Content-Type', /json/) .expect((res:any) => { @@ -133,7 +149,8 @@ describe(__filename, function () { }); it('createAuthor', async function () { - await agent.get(endPoint('createAuthor')) + await preparedAgent.get(endPoint('createAuthor')) + .set("Authorization", await generateJWTToken()) .expect(200) .expect('Content-Type', /json/) .expect((res:any) => { @@ -144,8 +161,9 @@ describe(__filename, function () { }); it('createSession', async function () { - await agent.get(`${endPoint('createSession')}&authorID=${authorID}&groupID=${groupID}` + + await preparedAgent.get(`${endPoint('createSession')}?authorID=${authorID}&groupID=${groupID}` + '&validUntil=999999999999') + .set("Authorization", await generateJWTToken()) .expect(200) .expect('Content-Type', /json/) .expect((res:any) => { @@ -156,8 +174,9 @@ describe(__filename, function () { }); it('createSession', async function () { - await agent.get(`${endPoint('createSession')}&authorID=${authorID}&groupID=${groupID}` + + await preparedAgent.get(`${endPoint('createSession')}?authorID=${authorID}&groupID=${groupID}` + '&validUntil=999999999999') + .set("Authorization", await generateJWTToken()) .expect(200) .expect('Content-Type', /json/) .expect((res:any) => { @@ -168,7 +187,8 @@ describe(__filename, function () { }); it('createGroupPad', async function () { - await agent.get(`${endPoint('createGroupPad')}&groupID=${groupID}&padName=x1234567`) + await preparedAgent.get(`${endPoint('createGroupPad')}?groupID=${groupID}&padName=x1234567`) + .set("Authorization", await generateJWTToken()) .expect(200) .expect('Content-Type', /json/) .expect((res:any) => { @@ -177,7 +197,8 @@ describe(__filename, function () { }); it('createGroupPad', async function () { - await agent.get(`${endPoint('createGroupPad')}&groupID=${groupID}&padName=x12345678`) + await preparedAgent.get(`${endPoint('createGroupPad')}?groupID=${groupID}&padName=x12345678`) + .set("Authorization", await generateJWTToken()) .expect(200) .expect('Content-Type', /json/) .expect((res:any) => { @@ -186,7 +207,8 @@ describe(__filename, function () { }); it('deleteGroup', async function () { - await agent.get(`${endPoint('deleteGroup')}&groupID=${groupID}`) + await preparedAgent.get(`${endPoint('deleteGroup')}?groupID=${groupID}`) + .set("Authorization", await generateJWTToken()) .expect(200) .expect('Content-Type', /json/) .expect((res:any) => { @@ -198,7 +220,8 @@ describe(__filename, function () { describe('API: Author creation', function () { it('createGroup', async function () { - await agent.get(endPoint('createGroup')) + await preparedAgent.get(endPoint('createGroup')) + .set("Authorization", await generateJWTToken()) .expect(200) .expect('Content-Type', /json/) .expect((res:any) => { @@ -209,7 +232,8 @@ describe(__filename, function () { }); it('createAuthor', async function () { - await agent.get(endPoint('createAuthor')) + await preparedAgent.get(endPoint('createAuthor')) + .set("Authorization", await generateJWTToken()) .expect(200) .expect('Content-Type', /json/) .expect((res:any) => { @@ -219,7 +243,8 @@ describe(__filename, function () { }); it('createAuthor with name', async function () { - await agent.get(`${endPoint('createAuthor')}&name=john`) + await preparedAgent.get(`${endPoint('createAuthor')}?name=john`) + .set("Authorization", await generateJWTToken()) .expect(200) .expect('Content-Type', /json/) .expect((res:any) => { @@ -230,7 +255,8 @@ describe(__filename, function () { }); it('createAuthorIfNotExistsFor', async function () { - await agent.get(`${endPoint('createAuthorIfNotExistsFor')}&authorMapper=chris`) + await preparedAgent.get(`${endPoint('createAuthorIfNotExistsFor')}?authorMapper=chris`) + .set("Authorization", await generateJWTToken()) .expect(200) .expect('Content-Type', /json/) .expect((res:any) => { @@ -240,7 +266,8 @@ describe(__filename, function () { }); it('getAuthorName', async function () { - await agent.get(`${endPoint('getAuthorName')}&authorID=${authorID}`) + await preparedAgent.get(`${endPoint('getAuthorName')}?authorID=${authorID}`) + .set("Authorization", await generateJWTToken()) .expect(200) .expect('Content-Type', /json/) .expect((res:any) => { @@ -252,8 +279,9 @@ describe(__filename, function () { describe('API: Sessions', function () { it('createSession', async function () { - await agent.get(`${endPoint('createSession')}&authorID=${authorID}&groupID=${groupID}` + + await preparedAgent.get(`${endPoint('createSession')}?authorID=${authorID}&groupID=${groupID}` + '&validUntil=999999999999') + .set("Authorization", await generateJWTToken()) .expect(200) .expect('Content-Type', /json/) .expect((res:any) => { @@ -264,7 +292,8 @@ describe(__filename, function () { }); it('getSessionInfo', async function () { - await agent.get(`${endPoint('getSessionInfo')}&sessionID=${sessionID}`) + await preparedAgent.get(`${endPoint('getSessionInfo')}?sessionID=${sessionID}`) + .set("Authorization", await generateJWTToken()) .expect(200) .expect('Content-Type', /json/) .expect((res:any) => { @@ -276,7 +305,8 @@ describe(__filename, function () { }); it('listSessionsOfGroup', async function () { - await agent.get(`${endPoint('listSessionsOfGroup')}&groupID=${groupID}`) + await preparedAgent.get(`${endPoint('listSessionsOfGroup')}?groupID=${groupID}`) + .set("Authorization", await generateJWTToken()) .expect(200) .expect('Content-Type', /json/) .expect((res:any) => { @@ -286,7 +316,8 @@ describe(__filename, function () { }); it('deleteSession', async function () { - await agent.get(`${endPoint('deleteSession')}&sessionID=${sessionID}`) + await preparedAgent.get(`${endPoint('deleteSession')}?sessionID=${sessionID}`) + .set("Authorization", await generateJWTToken()) .expect(200) .expect('Content-Type', /json/) .expect((res:any) => { @@ -295,7 +326,8 @@ describe(__filename, function () { }); it('getSessionInfo of deleted session', async function () { - await agent.get(`${endPoint('getSessionInfo')}&sessionID=${sessionID}`) + await preparedAgent.get(`${endPoint('getSessionInfo')}?sessionID=${sessionID}`) + .set("Authorization", await generateJWTToken()) .expect(200) .expect('Content-Type', /json/) .expect((res:any) => { @@ -306,7 +338,8 @@ describe(__filename, function () { describe('API: Group pad management', function () { it('listPads', async function () { - await agent.get(`${endPoint('listPads')}&groupID=${groupID}`) + await preparedAgent.get(`${endPoint('listPads')}?groupID=${groupID}`) + .set("Authorization", await generateJWTToken()) .expect(200) .expect('Content-Type', /json/) .expect((res:any) => { @@ -316,7 +349,8 @@ describe(__filename, function () { }); it('createGroupPad', async function () { - await agent.get(`${endPoint('createGroupPad')}&groupID=${groupID}&padName=${padID}`) + await preparedAgent.get(`${endPoint('createGroupPad')}?groupID=${groupID}&padName=${padID}`) + .set("Authorization", await generateJWTToken()) .expect(200) .expect('Content-Type', /json/) .expect((res:any) => { @@ -326,10 +360,11 @@ describe(__filename, function () { }); it('listPads after creating a group pad', async function () { - await agent.get(`${endPoint('listPads')}&groupID=${groupID}`) + await preparedAgent.get(`${endPoint('listPads')}?groupID=${groupID}`) + .set("Authorization", await generateJWTToken()) .expect(200) .expect('Content-Type', /json/) - .expect((res:any) => { + .expect((res) => { assert.equal(res.body.code, 0); assert.equal(res.body.data.padIDs.length, 1); }); @@ -338,7 +373,8 @@ describe(__filename, function () { describe('API: Pad security', function () { it('getPublicStatus', async function () { - await agent.get(`${endPoint('getPublicStatus')}&padID=${padID}`) + await preparedAgent.get(`${endPoint('getPublicStatus')}?padID=${padID}`) + .set("Authorization", await generateJWTToken()) .expect(200) .expect('Content-Type', /json/) .expect((res:any) => { @@ -348,7 +384,8 @@ describe(__filename, function () { }); it('setPublicStatus', async function () { - await agent.get(`${endPoint('setPublicStatus')}&padID=${padID}&publicStatus=true`) + await preparedAgent.get(`${endPoint('setPublicStatus')}?padID=${padID}&publicStatus=true`) + .set("Authorization", await generateJWTToken()) .expect(200) .expect('Content-Type', /json/) .expect((res:any) => { @@ -357,7 +394,8 @@ describe(__filename, function () { }); it('getPublicStatus after changing public status', async function () { - await agent.get(`${endPoint('getPublicStatus')}&padID=${padID}`) + await preparedAgent.get(`${endPoint('getPublicStatus')}?padID=${padID}`) + .set("Authorization", await generateJWTToken()) .expect(200) .expect('Content-Type', /json/) .expect((res:any) => { @@ -373,7 +411,8 @@ describe(__filename, function () { describe('API: Misc', function () { it('listPadsOfAuthor', async function () { - await agent.get(`${endPoint('listPadsOfAuthor')}&authorID=${authorID}`) + await preparedAgent.get(`${endPoint('listPadsOfAuthor')}?authorID=${authorID}`) + .set("Authorization", await generateJWTToken()) .expect(200) .expect('Content-Type', /json/) .expect((res:any) => { diff --git a/src/tests/settings.json b/src/tests/settings.json index c8064176b..e83c0bf80 100644 --- a/src/tests/settings.json +++ b/src/tests/settings.json @@ -650,5 +650,23 @@ /* * Enable/Disable case-insensitive pad names. */ - "lowerCasePadIds": false + "lowerCasePadIds": false, + "sso": { + "clients": [ + { + "client_id": "admin_client", + "client_secret": "admin", + "grant_types": ["authorization_code"], + "response_types": ["code"], + "redirect_uris": ["http://localhost:9001/admin/"] + }, + { + "client_id": "user_client", + "client_secret": "user", + "grant_types": ["authorization_code"], + "response_types": ["code"], + "redirect_uris": ["http://localhost:9001/"] + } + ] + } }