mirror of
https://github.com/ether/etherpad-lite.git
synced 2025-04-23 17:06:16 -04:00
Feat/oauth2 (#6281): Added oauth to API paths
* Added oauth provider. * Fixed provider. * Added auth flow. * Fixed auth flow and added scaffolding vite config. * Added working oauth2. * Fixed dockerfile. * Adapted run.sh script * Moved api tests to oauth2. * Updated security schemes. * Removed api key from existance. * Fixed installation * Added missing issuer in config. * Fixed dev dependencies. * Updated lock file.
This commit is contained in:
parent
562177022f
commit
fb56809e55
44 changed files with 1782 additions and 237 deletions
|
@ -45,6 +45,12 @@
|
|||
"expressPreSession": "ep_etherpad-lite/node/hooks/express/specialpages"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "oauth2",
|
||||
"hooks": {
|
||||
"expressCreateServer": "ep_etherpad-lite/node/security/OAuth2Provider"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "padurlsanitize",
|
||||
"hooks": {
|
||||
|
|
|
@ -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<any> = {};
|
||||
|
@ -167,21 +149,20 @@ exports.version = version;
|
|||
|
||||
|
||||
type APIFields = {
|
||||
apikey: string;
|
||||
api_key: string;
|
||||
padID: string;
|
||||
padName: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 +173,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 +205,3 @@ exports.handle = async function (apiVersion: string, functionName: string, field
|
|||
// call the api function
|
||||
return api[functionName].apply(this, functionParams);
|
||||
};
|
||||
|
||||
exports.exportedForTestingOnly = {
|
||||
apiKey: apikey,
|
||||
};
|
||||
|
|
|
@ -483,14 +483,24 @@ const generateDefinitionForVersion = (version:string, style = APIPathStyle.FLAT)
|
|||
...defaultResponses,
|
||||
},
|
||||
securitySchemes: {
|
||||
ApiKey: {
|
||||
type: 'apiKey',
|
||||
in: 'query',
|
||||
name: 'apikey',
|
||||
openid: {
|
||||
type: "oauth2",
|
||||
flows: {
|
||||
authorizationCode: {
|
||||
authorizationUrl: settings.sso.issuer+"/oidc/auth",
|
||||
tokenUrl: settings.sso.issuer+"/oidc/token",
|
||||
scopes: {
|
||||
openid: "openid",
|
||||
profile: "profile",
|
||||
email: "email",
|
||||
admin: "admin"
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
security: [{ApiKey: []}],
|
||||
security: [{openid: []}],
|
||||
};
|
||||
|
||||
// build operations
|
||||
|
@ -657,7 +667,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 {
|
||||
|
|
274
src/node/security/OAuth2Provider.ts
Normal file
274
src/node/security/OAuth2Provider.ts
Normal file
|
@ -0,0 +1,274 @@
|
|||
import {ArgsExpressType} from "../types/ArgsExpressType";
|
||||
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');
|
||||
import {IncomingForm} from 'formidable'
|
||||
import express, {Request, Response} from 'express';
|
||||
import {format} from 'url'
|
||||
import {ParsedUrlQuery} from "node:querystring";
|
||||
import {Http2ServerRequest, Http2ServerResponse} from "node:http2";
|
||||
|
||||
const configuration: Configuration = {
|
||||
scopes: ['openid', 'profile', 'email'],
|
||||
findAccount: async (ctx, id) => {
|
||||
const users = settings.users as {
|
||||
[username: string]: {
|
||||
password: string;
|
||||
is_admin: boolean;
|
||||
}
|
||||
}
|
||||
|
||||
const usersArray1 = Object.keys(users).map((username) => ({
|
||||
username,
|
||||
...users[username]
|
||||
}));
|
||||
|
||||
const account = usersArray1.find((user) => user.username === id);
|
||||
|
||||
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
|
||||
AuthorizationCode: 10 * 60, // 10 minutes in seconds
|
||||
ClientCredentials: 1 * 60 * 60, // 1 hour in seconds
|
||||
IdToken: 1 * 60 * 60, // 1 hour in seconds
|
||||
RefreshToken: 1 * 24 * 60 * 60, // 1 day in seconds
|
||||
},
|
||||
claims: {
|
||||
openid: ['sub'],
|
||||
email: ['email'],
|
||||
profile: ['name'],
|
||||
admin: ['admin']
|
||||
},
|
||||
cookies: {
|
||||
keys: ['oidc'],
|
||||
},
|
||||
features:{
|
||||
devInteractions: {enabled: false},
|
||||
},
|
||||
adapter: MemoryAdapter
|
||||
};
|
||||
|
||||
|
||||
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, publicKey} = await generateKeyPair('RS256');
|
||||
const privateKeyJWK = await exportJWK(privateKey);
|
||||
publicKeyExported = publicKey
|
||||
privateKeyExported = privateKey
|
||||
|
||||
const oidc = new Provider(settings.sso.issuer, {
|
||||
...configuration, jwks: {
|
||||
keys: [
|
||||
privateKeyJWK
|
||||
],
|
||||
},
|
||||
conformIdTokenClaims: false,
|
||||
claims: {
|
||||
address: ['address'],
|
||||
email: ['email', 'email_verified'],
|
||||
phone: ['phone_number', 'phone_number_verified'],
|
||||
profile: ['birthdate', 'family_name', 'gender', 'given_name', 'locale', 'middle_name', 'name',
|
||||
'nickname', 'picture', 'preferred_username', 'profile', 'updated_at', 'website', 'zoneinfo'],
|
||||
},
|
||||
features:{
|
||||
userinfo: {enabled: true},
|
||||
claimsParameter: {enabled: true},
|
||||
devInteractions: {enabled: false},
|
||||
resourceIndicators: {enabled: true, defaultResource(ctx) {
|
||||
return ctx.origin;
|
||||
},
|
||||
getResourceServerInfo(ctx, resourceIndicator, client) {
|
||||
return {
|
||||
scope: client.scope as string,
|
||||
audience: 'account',
|
||||
accessTokenFormat: 'jwt',
|
||||
};
|
||||
},
|
||||
useGrantedResource(ctx, model) {
|
||||
return true;
|
||||
},},
|
||||
jwtResponseModes: {enabled: true},
|
||||
},
|
||||
clientBasedCORS: (ctx, origin, client) => {
|
||||
return true
|
||||
},
|
||||
extraTokenClaims: async (ctx, token) => {
|
||||
|
||||
|
||||
if(token.kind === 'AccessToken') {
|
||||
// Add your custom claims here. For example:
|
||||
const users = settings.users as {
|
||||
[username: string]: {
|
||||
password: string;
|
||||
is_admin: boolean;
|
||||
}
|
||||
}
|
||||
|
||||
const usersArray1 = Object.keys(users).map((username) => ({
|
||||
username,
|
||||
...users[username]
|
||||
}));
|
||||
|
||||
const account = usersArray1.find((user) => user.username === token.accountId);
|
||||
return {
|
||||
admin: account?.is_admin
|
||||
};
|
||||
}
|
||||
},
|
||||
clients: settings.sso.clients
|
||||
});
|
||||
|
||||
|
||||
args.app.post('/interaction/:uid', async (req: Http2ServerRequest, res: Http2ServerResponse, next:Function) => {
|
||||
const formid = new IncomingForm();
|
||||
try {
|
||||
// @ts-ignore
|
||||
const {login, password} = (await formid.parse(req))[0]
|
||||
const {prompt, jti, session,cid, params, grantId} = await oidc.interactionDetails(req, res);
|
||||
|
||||
const client = await oidc.Client.find(params.client_id as string);
|
||||
|
||||
switch (prompt.name) {
|
||||
case 'login': {
|
||||
const users = settings.users as {
|
||||
[username: string]: {
|
||||
password: string;
|
||||
admin: boolean;
|
||||
}
|
||||
}
|
||||
const usersArray1 = Object.keys(users).map((username) => ({
|
||||
username,
|
||||
...users[username]
|
||||
}));
|
||||
const account = usersArray1.find((user) => user.username === login as unknown as string && user.password === password as unknown as string);
|
||||
if (!account) {
|
||||
res.setHeader('Content-Type', 'application/json');
|
||||
res.end(JSON.stringify({error: "Invalid login"}));
|
||||
}
|
||||
|
||||
if (account) {
|
||||
await oidc.interactionFinished(req, res, {
|
||||
login: {accountId: account.username}
|
||||
}, {mergeWithLastSubmission: false});
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'consent': {
|
||||
let grant;
|
||||
if (grantId) {
|
||||
// we'll be modifying existing grant in existing session
|
||||
grant = await oidc.Grant.find(grantId);
|
||||
} else {
|
||||
// we're establishing a new grant
|
||||
grant = new oidc.Grant({
|
||||
accountId: session!.accountId,
|
||||
clientId: params.client_id as string,
|
||||
});
|
||||
}
|
||||
|
||||
if (prompt.details.missingOIDCScope) {
|
||||
// @ts-ignore
|
||||
grant!.addOIDCScope(prompt.details.missingOIDCScope.join(' '));
|
||||
}
|
||||
if (prompt.details.missingOIDCClaims) {
|
||||
grant!.addOIDCClaims(prompt.details.missingOIDCClaims as string[]);
|
||||
}
|
||||
if (prompt.details.missingResourceScopes) {
|
||||
for (const [indicator, scope] of Object.entries(prompt.details.missingResourceScopes)) {
|
||||
grant!.addResourceScope(indicator, scope.join(' '));
|
||||
}
|
||||
}
|
||||
const result = {consent: {grantId: await grant!.save()}};
|
||||
await oidc.interactionFinished(req, res, result, {
|
||||
mergeWithLastSubmission: true,
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
await next();
|
||||
} catch (err:any) {
|
||||
return res.writeHead(500).end(err.message);
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
args.app.get('/interaction/:uid', async (req: Request, res: Response, next: Function) => {
|
||||
try {
|
||||
const {
|
||||
uid, prompt, params, session,
|
||||
} = await oidc.interactionDetails(req, res);
|
||||
|
||||
params["state"] = uid
|
||||
|
||||
switch (prompt.name) {
|
||||
case 'login': {
|
||||
res.redirect(format({
|
||||
pathname: '/views/login.html',
|
||||
query: params as ParsedUrlQuery
|
||||
}))
|
||||
break
|
||||
}
|
||||
case 'consent': {
|
||||
res.redirect(format({
|
||||
pathname: '/views/consent.html',
|
||||
query: params as ParsedUrlQuery
|
||||
}))
|
||||
break
|
||||
}
|
||||
default:
|
||||
return res.sendFile(path.join(settings.root,'src','static', 'oidc','login.html'));
|
||||
}
|
||||
} catch (err) {
|
||||
return next(err);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
args.app.use('/views/', express.static(path.join(settings.root,'src','static', 'oidc'), {maxAge: 1000 * 60 * 60 * 24}));
|
||||
|
||||
/*
|
||||
oidc.on('authorization.error', (ctx, error) => {
|
||||
console.log('authorization.error', error);
|
||||
})
|
||||
|
||||
oidc.on('server_error', (ctx, error) => {
|
||||
console.log('server_error', error);
|
||||
})
|
||||
oidc.on('grant.error', (ctx, error) => {
|
||||
console.log('grant.error', error);
|
||||
})
|
||||
oidc.on('introspection.error', (ctx, error) => {
|
||||
console.log('introspection.error', error);
|
||||
})
|
||||
oidc.on('revocation.error', (ctx, error) => {
|
||||
console.log('revocation.error', error);
|
||||
})*/
|
||||
args.app.use("/oidc", oidc.callback());
|
||||
//cb();
|
||||
}
|
5
src/node/security/OAuth2User.ts
Normal file
5
src/node/security/OAuth2User.ts
Normal file
|
@ -0,0 +1,5 @@
|
|||
export type OAuth2User = {
|
||||
username: string;
|
||||
password: string;
|
||||
admin: boolean;
|
||||
}
|
115
src/node/security/OIDCAdapter.ts
Normal file
115
src/node/security/OIDCAdapter.ts
Normal file
|
@ -0,0 +1,115 @@
|
|||
import {LRUCache} from 'lru-cache';
|
||||
import type {Adapter, AdapterPayload} from "oidc-provider";
|
||||
|
||||
|
||||
const options = {
|
||||
max: 500,
|
||||
sizeCalculation: (item:any, key:any) => {
|
||||
return 1
|
||||
},
|
||||
// for use with tracking overall storage size
|
||||
maxSize: 5000,
|
||||
|
||||
// how long to live in ms
|
||||
ttl: 1000 * 60 * 5,
|
||||
|
||||
// return stale items before removing from cache?
|
||||
allowStale: false,
|
||||
|
||||
updateAgeOnGet: false,
|
||||
updateAgeOnHas: false,
|
||||
}
|
||||
|
||||
const epochTime = (date = Date.now()) => Math.floor(date / 1000);
|
||||
|
||||
const storage = new LRUCache<string,AdapterPayload|string[]|string>(options);
|
||||
|
||||
function grantKeyFor(id: string) {
|
||||
return `grant:${id}`;
|
||||
}
|
||||
|
||||
function userCodeKeyFor(userCode:string) {
|
||||
return `userCode:${userCode}`;
|
||||
}
|
||||
|
||||
class MemoryAdapter implements Adapter{
|
||||
private readonly name: string;
|
||||
constructor(name:string) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
key(id:string) {
|
||||
return `${this.name}:${id}`;
|
||||
}
|
||||
|
||||
destroy(id:string) {
|
||||
const key = this.key(id);
|
||||
|
||||
const found = storage.get(key) as AdapterPayload;
|
||||
const grantId = found && found.grantId;
|
||||
|
||||
storage.delete(key);
|
||||
|
||||
if (grantId) {
|
||||
const grantKey = grantKeyFor(grantId);
|
||||
(storage.get(grantKey) as string[])!.forEach(token => storage.delete(token));
|
||||
storage.delete(grantKey);
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
consume(id: string) {
|
||||
(storage.get(this.key(id)) as AdapterPayload)!.consumed = epochTime();
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
find(id: string): Promise<AdapterPayload | void | undefined> {
|
||||
if (storage.has(this.key(id))){
|
||||
return Promise.resolve<AdapterPayload>(storage.get(this.key(id)) as AdapterPayload);
|
||||
}
|
||||
return Promise.resolve<undefined>(undefined)
|
||||
}
|
||||
|
||||
findByUserCode(userCode: string) {
|
||||
const id = storage.get(userCodeKeyFor(userCode)) as string;
|
||||
return this.find(id);
|
||||
}
|
||||
|
||||
upsert(id: string, payload: {
|
||||
iat: number;
|
||||
exp: number;
|
||||
uid: string;
|
||||
kind: string;
|
||||
jti: string;
|
||||
accountId: string;
|
||||
loginTs: number;
|
||||
}, expiresIn: number) {
|
||||
const key = this.key(id);
|
||||
|
||||
storage.set(key, payload, {ttl: expiresIn * 1000});
|
||||
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
findByUid(uid: string): Promise<AdapterPayload | void | undefined> {
|
||||
for(const [_, value] of storage.entries()){
|
||||
if(typeof value ==="object" && "uid" in value && value.uid === uid){
|
||||
return Promise.resolve(value);
|
||||
}
|
||||
}
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
revokeByGrantId(grantId: string): Promise<void | undefined> {
|
||||
const grantKey = grantKeyFor(grantId);
|
||||
const grant = storage.get(grantKey) as string[];
|
||||
if (grant) {
|
||||
grant.forEach((token) => storage.delete(token));
|
||||
storage.delete(grantKey);
|
||||
}
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
export default MemoryAdapter
|
|
@ -45,10 +45,5 @@ for (let i = 0; i < argv.length; i++) {
|
|||
exports.argv.sessionkey = arg;
|
||||
}
|
||||
|
||||
// Override location of APIKEY.txt file
|
||||
if (prevArg === '--apikey') {
|
||||
exports.argv.apikey = arg;
|
||||
}
|
||||
|
||||
prevArg = arg;
|
||||
}
|
||||
|
|
|
@ -342,6 +342,11 @@ exports.requireAuthentication = false;
|
|||
exports.requireAuthorization = false;
|
||||
exports.users = {};
|
||||
|
||||
/*
|
||||
* This setting is used for configuring sso
|
||||
*/
|
||||
exports.sso = {}
|
||||
|
||||
/*
|
||||
* Show settings in admin page, by default it is true
|
||||
*/
|
||||
|
|
|
@ -45,15 +45,18 @@
|
|||
"find-root": "1.1.0",
|
||||
"formidable": "^3.5.1",
|
||||
"http-errors": "^2.0.0",
|
||||
"jose": "^5.2.3",
|
||||
"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",
|
||||
"log4js": "^6.9.1",
|
||||
"measured-core": "^2.0.0",
|
||||
"mime-types": "^2.1.35",
|
||||
"oidc-provider": "^8.4.5",
|
||||
"openapi-backend": "^5.10.6",
|
||||
"proxy-addr": "^2.0.7",
|
||||
"rate-limiter-flexible": "^5.0.0",
|
||||
|
@ -72,22 +75,27 @@
|
|||
"ueberdb2": "^4.2.63",
|
||||
"underscore": "1.13.6",
|
||||
"unorm": "1.6.0",
|
||||
"wtfnode": "^0.9.1"
|
||||
"wtfnode": "^0.9.1",
|
||||
"lru-cache": "^10.2.0"
|
||||
},
|
||||
"bin": {
|
||||
"etherpad-healthcheck": "../bin/etherpad-healthcheck",
|
||||
"etherpad-lite": "node/server.ts"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@playwright/test": "^1.42.1",
|
||||
"@types/async": "^3.2.24",
|
||||
"@types/express": "^4.17.21",
|
||||
"@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",
|
||||
"@types/semver": "^7.5.8",
|
||||
"@types/sinon": "^17.0.3",
|
||||
"@types/supertest": "^6.0.2",
|
||||
"@types/semver": "^7.5.8",
|
||||
"@types/underscore": "^1.11.15",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-config-etherpad": "^4.0.4",
|
||||
|
@ -96,7 +104,6 @@
|
|||
"mocha-froth": "^0.2.10",
|
||||
"nodeify": "^1.0.1",
|
||||
"openapi-schema-validation": "^0.4.2",
|
||||
"@playwright/test": "^1.42.1",
|
||||
"set-cookie-parser": "^2.6.0",
|
||||
"sinon": "^17.0.1",
|
||||
"split-grid": "^1.0.11",
|
||||
|
|
|
@ -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<any> = {};
|
||||
let agentPromise:Promise<any>|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,37 @@ 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 generateJWTTokenUser = () => {
|
||||
const jwt = new SignJWT({
|
||||
sub: 'admin',
|
||||
jti: '123',
|
||||
exp: Math.floor(Date.now() / 1000) + 60 * 60,
|
||||
aud: 'account',
|
||||
iss: 'http://localhost:9001',
|
||||
})
|
||||
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 +82,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 +100,8 @@ exports.init = async function () {
|
|||
await server.exit();
|
||||
});
|
||||
|
||||
agentResolve!(exports.agent);
|
||||
return exports.agent;
|
||||
agentResolve!(agent);
|
||||
return agent;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -81,7 +112,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 +167,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 +179,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 +204,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 +214,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 +222,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<void>((resolve, reject) => {
|
||||
export const sendMessage = async (socket: any, message:any) => await new Promise<void>((resolve, reject) => {
|
||||
socket.emit('message', message, (errInfo:{
|
||||
name: string,
|
||||
message: string,
|
||||
|
@ -210,7 +241,7 @@ exports.sendMessage = async (socket: any, message:any) => await new Promise<void
|
|||
/**
|
||||
* Convenience function to send a USER_CHANGES message. Waits for acknowledgement.
|
||||
*/
|
||||
exports.sendUserChanges = async (socket:any, data:any) => await exports.sendMessage(socket, {
|
||||
export const sendUserChanges = async (socket:any, data:any) => await sendMessage(socket, {
|
||||
type: 'COLLABROOM',
|
||||
component: 'pad',
|
||||
data: {
|
||||
|
@ -232,8 +263,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 +283,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;
|
||||
|
|
|
@ -7,13 +7,12 @@ const common = require('./common');
|
|||
const host = `http://${settings.ip}:${settings.port}`;
|
||||
const froth = require('mocha-froth');
|
||||
const axios = require('axios');
|
||||
const apiKey = common.apiKey;
|
||||
const apiVersion = 1;
|
||||
const testPadId = `TEST_fuzz${makeid()}`;
|
||||
|
||||
const endPoint = function (point: string, version?:number) {
|
||||
version = version || apiVersion;
|
||||
return `/api/${version}/${point}?apikey=${apiKey}`;
|
||||
return `/api/${version}/${point}}`;
|
||||
};
|
||||
|
||||
console.log('Testing against padID', testPadId);
|
||||
|
@ -29,7 +28,12 @@ setTimeout(() => {
|
|||
}, 5000); // wait 5 seconds
|
||||
|
||||
async function runTest(number: number) {
|
||||
await axios.get(`${host + endPoint('createPad')}&padID=${testPadId}`)
|
||||
await axios
|
||||
.get(`${host + endPoint('createPad')}?padID=${testPadId}`, {
|
||||
headers: {
|
||||
Authorization: await common.generateJWTToken(),
|
||||
}
|
||||
})
|
||||
.then(() => {
|
||||
const req = axios.post(`${host}/p/${testPadId}/import`)
|
||||
.then(() => {
|
||||
|
|
|
@ -12,7 +12,6 @@ const common = require('../../common');
|
|||
const validateOpenAPI = require('openapi-schema-validation').validate;
|
||||
|
||||
let agent: any;
|
||||
const apiKey = common.apiKey;
|
||||
let apiVersion = 1;
|
||||
|
||||
const makeid = () => {
|
||||
|
@ -27,7 +26,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(); });
|
||||
|
|
|
@ -6,17 +6,18 @@
|
|||
* TODO: maybe unify those two files and merge in a single one.
|
||||
*/
|
||||
|
||||
import {generateJWTToken, generateJWTTokenUser} 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,28 +25,38 @@ 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);
|
||||
});
|
||||
|
||||
it('errors with invalid APIKey', async function () {
|
||||
it('errors with invalid OAuth token', 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);
|
||||
});
|
||||
|
||||
it('errors with unprivileged OAuth token', async function () {
|
||||
// This is broken because Etherpad doesn't handle HTTP codes properly see #2343
|
||||
await agent.get(`/api/${apiVersion}/createPad?padID=test`)
|
||||
.set("Authorization", (await generateJWTTokenUser()).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 +64,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 +75,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, /🇼/);
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -9,7 +9,6 @@ const settings = require('../../../container/loadSettings.js').loadSettings();
|
|||
|
||||
const host = "http://" + settings.ip + ":" + settings.port;
|
||||
|
||||
const apiKey = common.apiKey;
|
||||
var apiVersion = 1;
|
||||
var testPadId = "TEST_fuzz" + makeid();
|
||||
|
||||
|
|
|
@ -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<any> = {
|
||||
'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);
|
||||
|
|
|
@ -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(/<ul class="bullet"><li><ul class="bullet"><li>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<any> = 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<any> = 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<any>) => agent.post(`/p/${testPadId}/import`)
|
||||
const importEtherpad = (records: MapArrayType<any>) => 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<br>/));
|
||||
|
@ -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<br>/));
|
||||
|
@ -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<br>/));
|
||||
|
@ -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<br>/));
|
||||
|
@ -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() {
|
||||
|
|
|
@ -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');
|
||||
|
||||
|
|
|
@ -12,7 +12,6 @@ const common = require('../../common');
|
|||
const padManager = require('../../../../node/db/PadManager');
|
||||
|
||||
let agent:any;
|
||||
const apiKey = common.apiKey;
|
||||
let apiVersion = 1;
|
||||
const testPadId = makeid();
|
||||
const newPadId = makeid();
|
||||
|
@ -21,7 +20,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
|
||||
|
@ -60,10 +59,10 @@ describe(__filename, function () {
|
|||
});
|
||||
|
||||
describe('Sanity checks', function () {
|
||||
it('errors with invalid APIKey', async function () {
|
||||
it('errors with invalid oauth token', 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 +112,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 +136,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 +145,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 +154,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 +163,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 +179,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 +211,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 +222,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 +255,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 +264,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 +298,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 +309,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 +350,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 +359,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 +368,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 +377,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 +386,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 +403,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 +428,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 +442,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 +451,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 +462,7 @@ describe(__filename, function () {
|
|||
it('Sets the HTML of a Pad attempting to pass ugly HTML', async function () {
|
||||
const html = '<div><b>Hello HTML</title></head></div>';
|
||||
const res = await agent.post(endPoint('setHTML'))
|
||||
.set("Authorization", (await common.generateJWTToken()))
|
||||
.send({
|
||||
padID: testPadId,
|
||||
html,
|
||||
|
@ -433,6 +474,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 +482,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('<br></body>', '</body>').toLowerCase();
|
||||
|
@ -448,11 +491,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('<br></body>', '</body>').toLowerCase();
|
||||
|
@ -461,7 +506,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 +515,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 +575,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 +598,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 +608,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('<br><br></body>', '</body>').toLowerCase();
|
||||
assert.equal(receivedHtml, expectedHtml);
|
||||
|
@ -564,8 +622,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 +636,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 +674,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 +684,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: `<!DOCTYPE HTML><html><body>${bodyHtml}</body></html>`})
|
||||
.expect(200)
|
||||
.expect('Content-Type', /json/)
|
||||
|
@ -631,8 +694,9 @@ describe(__filename, function () {
|
|||
const origHtml = await getHtml(sourcePadId);
|
||||
assert.doesNotMatch(origHtml, /<strong>/);
|
||||
assert.doesNotMatch(origHtml, /<em>/);
|
||||
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 +736,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,
|
||||
|
|
|
@ -14,13 +14,14 @@ describe(__filename, function () {
|
|||
let pad: PadType;
|
||||
|
||||
const restoreRevision = async (v:string, padId: string, rev: number, authorId:string|null = null) => {
|
||||
// @ts-ignore
|
||||
const p = new URLSearchParams(Object.entries({
|
||||
apikey: common.apiKey,
|
||||
padID: padId,
|
||||
rev,
|
||||
...(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);
|
||||
|
|
|
@ -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<supertest.Test>
|
||||
|
||||
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) => {
|
||||
|
|
|
@ -31,8 +31,8 @@ describe('API Versioning', function () {
|
|||
});
|
||||
|
||||
describe('Permission', function () {
|
||||
it('errors with invalid APIKey', function (done) {
|
||||
api.get(`/api/${apiVersion}/createPad?apikey=wrong_password&padID=test`)
|
||||
it('errors with invalid OAuth token', function (done) {
|
||||
api.get(`/api/${apiVersion}/createPad?padID=test`)
|
||||
.expect(401, done);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -650,5 +650,24 @@
|
|||
/*
|
||||
* Enable/Disable case-insensitive pad names.
|
||||
*/
|
||||
"lowerCasePadIds": false
|
||||
"lowerCasePadIds": false,
|
||||
"sso": {
|
||||
"issuer": "${SSO_ISSUER:http://localhost:9001}",
|
||||
"clients": [
|
||||
{
|
||||
"client_id": "${ADMIN_CLIENT:admin_client}",
|
||||
"client_secret": "${ADMIN_SECRET:admin}",
|
||||
"grant_types": ["authorization_code"],
|
||||
"response_types": ["code"],
|
||||
"redirect_uris": ["${ADMIN_REDIRECT:http://localhost:9001/admin/}", "https://oauth.pstmn.io/v1/callback"]
|
||||
},
|
||||
{
|
||||
"client_id": "${USER_CLIENT:user_client}",
|
||||
"client_secret": "${USER_SECRET:user}",
|
||||
"grant_types": ["authorization_code"],
|
||||
"response_types": ["code"],
|
||||
"redirect_uris": ["${USER_REDIRECT:http://localhost:9001/}"]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue