2021-01-21 21:06:52 +00:00
|
|
|
'use strict';
|
2011-08-03 19:31:25 +01:00
|
|
|
/**
|
|
|
|
* The API Handler handles all API http requests
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
2011-08-11 15:26:41 +01:00
|
|
|
* 2011 Peter 'Pita' Martischka (Primary Technology Ltd)
|
2011-08-03 19:31:25 +01:00
|
|
|
*
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS-IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
|
|
|
*/
|
|
|
|
|
2024-02-23 19:48:55 +01:00
|
|
|
import {MapArrayType} from "../types/MapType";
|
|
|
|
|
2020-11-23 13:24:19 -05:00
|
|
|
const api = require('../db/API');
|
|
|
|
const padManager = require('../db/PadManager');
|
2024-02-23 19:48:55 +01:00
|
|
|
import createHTTPError from 'http-errors';
|
2024-03-26 17:11:24 +01:00
|
|
|
import {Http2ServerRequest, Http2ServerResponse} from "node:http2";
|
|
|
|
import {publicKeyExported} from "../security/OAuth2Provider";
|
|
|
|
import {jwtVerify} from "jose";
|
2024-05-14 22:36:16 +02:00
|
|
|
import {apikey} from './APIKeyHandler'
|
2019-02-08 23:20:57 +01:00
|
|
|
// a list of all functions
|
2024-02-23 19:48:55 +01:00
|
|
|
const version:MapArrayType<any> = {};
|
2020-11-23 13:24:19 -05:00
|
|
|
|
2022-02-21 00:45:26 -05:00
|
|
|
version['1'] = {
|
|
|
|
createGroup: [],
|
|
|
|
createGroupIfNotExistsFor: ['groupMapper'],
|
|
|
|
deleteGroup: ['groupID'],
|
|
|
|
listPads: ['groupID'],
|
|
|
|
createPad: ['padID', 'text'],
|
|
|
|
createGroupPad: ['groupID', 'padName', 'text'],
|
|
|
|
createAuthor: ['name'],
|
|
|
|
createAuthorIfNotExistsFor: ['authorMapper', 'name'],
|
|
|
|
listPadsOfAuthor: ['authorID'],
|
|
|
|
createSession: ['groupID', 'authorID', 'validUntil'],
|
|
|
|
deleteSession: ['sessionID'],
|
|
|
|
getSessionInfo: ['sessionID'],
|
|
|
|
listSessionsOfGroup: ['groupID'],
|
|
|
|
listSessionsOfAuthor: ['authorID'],
|
|
|
|
getText: ['padID', 'rev'],
|
|
|
|
setText: ['padID', 'text'],
|
|
|
|
getHTML: ['padID', 'rev'],
|
|
|
|
setHTML: ['padID', 'html'],
|
|
|
|
getRevisionsCount: ['padID'],
|
|
|
|
getLastEdited: ['padID'],
|
|
|
|
deletePad: ['padID'],
|
|
|
|
getReadOnlyID: ['padID'],
|
|
|
|
setPublicStatus: ['padID', 'publicStatus'],
|
|
|
|
getPublicStatus: ['padID'],
|
|
|
|
listAuthorsOfPad: ['padID'],
|
|
|
|
padUsersCount: ['padID'],
|
|
|
|
};
|
|
|
|
|
|
|
|
version['1.1'] = {
|
|
|
|
...version['1'],
|
|
|
|
getAuthorName: ['authorID'],
|
|
|
|
padUsers: ['padID'],
|
|
|
|
sendClientsMessage: ['padID', 'msg'],
|
|
|
|
listAllGroups: [],
|
|
|
|
};
|
|
|
|
|
|
|
|
version['1.2'] = {
|
|
|
|
...version['1.1'],
|
|
|
|
checkToken: [],
|
|
|
|
};
|
|
|
|
|
|
|
|
version['1.2.1'] = {
|
|
|
|
...version['1.2'],
|
|
|
|
listAllPads: [],
|
|
|
|
};
|
|
|
|
|
|
|
|
version['1.2.7'] = {
|
|
|
|
...version['1.2.1'],
|
|
|
|
createDiffHTML: ['padID', 'startRev', 'endRev'],
|
|
|
|
getChatHistory: ['padID', 'start', 'end'],
|
|
|
|
getChatHead: ['padID'],
|
|
|
|
};
|
|
|
|
|
|
|
|
version['1.2.8'] = {
|
|
|
|
...version['1.2.7'],
|
|
|
|
getAttributePool: ['padID'],
|
|
|
|
getRevisionChangeset: ['padID', 'rev'],
|
|
|
|
};
|
|
|
|
|
|
|
|
version['1.2.9'] = {
|
|
|
|
...version['1.2.8'],
|
|
|
|
copyPad: ['sourceID', 'destinationID', 'force'],
|
|
|
|
movePad: ['sourceID', 'destinationID', 'force'],
|
|
|
|
};
|
|
|
|
|
|
|
|
version['1.2.10'] = {
|
|
|
|
...version['1.2.9'],
|
|
|
|
getPadID: ['roID'],
|
|
|
|
};
|
|
|
|
|
|
|
|
version['1.2.11'] = {
|
|
|
|
...version['1.2.10'],
|
|
|
|
getSavedRevisionsCount: ['padID'],
|
|
|
|
listSavedRevisions: ['padID'],
|
|
|
|
saveRevision: ['padID', 'rev'],
|
|
|
|
restoreRevision: ['padID', 'rev'],
|
|
|
|
};
|
|
|
|
|
|
|
|
version['1.2.12'] = {
|
|
|
|
...version['1.2.11'],
|
|
|
|
appendChatMessage: ['padID', 'text', 'authorID', 'time'],
|
|
|
|
};
|
|
|
|
|
|
|
|
version['1.2.13'] = {
|
|
|
|
...version['1.2.12'],
|
|
|
|
appendText: ['padID', 'text'],
|
|
|
|
};
|
|
|
|
|
|
|
|
version['1.2.14'] = {
|
|
|
|
...version['1.2.13'],
|
|
|
|
getStats: [],
|
|
|
|
};
|
|
|
|
|
|
|
|
version['1.2.15'] = {
|
|
|
|
...version['1.2.14'],
|
|
|
|
copyPadWithoutHistory: ['sourceID', 'destinationID', 'force'],
|
|
|
|
};
|
2020-09-16 15:24:09 -03:00
|
|
|
|
2022-02-16 23:25:19 -05:00
|
|
|
version['1.3.0'] = {
|
|
|
|
...version['1.2.15'],
|
|
|
|
appendText: ['padID', 'text', 'authorId'],
|
|
|
|
copyPadWithoutHistory: ['sourceID', 'destinationID', 'force', 'authorId'],
|
|
|
|
createGroupPad: ['groupID', 'padName', 'text', 'authorId'],
|
|
|
|
createPad: ['padID', 'text', 'authorId'],
|
|
|
|
restoreRevision: ['padID', 'rev', 'authorId'],
|
|
|
|
setHTML: ['padID', 'html', 'authorId'],
|
|
|
|
setText: ['padID', 'text', 'authorId'],
|
|
|
|
};
|
|
|
|
|
2013-02-12 21:50:14 +01:00
|
|
|
// set the latest available API version here
|
2022-02-16 23:25:19 -05:00
|
|
|
exports.latestApiVersion = '1.3.0';
|
2013-02-12 21:50:14 +01:00
|
|
|
|
2013-02-13 16:29:01 +00:00
|
|
|
// exports the versions so it can be used by the new Swagger endpoint
|
|
|
|
exports.version = version;
|
|
|
|
|
2024-02-23 19:48:55 +01:00
|
|
|
|
|
|
|
type APIFields = {
|
2024-05-14 22:36:16 +02:00
|
|
|
apikey: string;
|
2024-02-23 19:48:55 +01:00
|
|
|
api_key: string;
|
|
|
|
padID: string;
|
|
|
|
padName: string;
|
2024-05-22 21:16:19 +02:00
|
|
|
authorization: string;
|
2024-02-23 19:48:55 +01:00
|
|
|
}
|
|
|
|
|
2011-08-03 19:31:25 +01:00
|
|
|
/**
|
2024-03-26 17:11:24 +01:00
|
|
|
* Handles an HTTP API call
|
2023-10-17 12:49:56 +02:00
|
|
|
* @param {String} apiVersion the version of the api
|
|
|
|
* @param {String} functionName the name of the called function
|
2011-08-03 19:31:25 +01:00
|
|
|
* @param fields the params of the called function
|
2024-03-26 17:11:24 +01:00
|
|
|
* @param req express request object
|
2011-08-03 19:31:25 +01:00
|
|
|
*/
|
2024-05-14 22:36:16 +02:00
|
|
|
exports.handle = async function (apiVersion: string, functionName: string, fields: APIFields,
|
|
|
|
req: Http2ServerRequest) {
|
2019-02-08 23:20:57 +01:00
|
|
|
// say goodbye if this is an unknown API version
|
2019-01-30 10:41:10 +00:00
|
|
|
if (!(apiVersion in version)) {
|
2020-04-01 18:18:02 +02:00
|
|
|
throw new createHTTPError.NotFound('no such api version');
|
2011-08-03 19:31:25 +01:00
|
|
|
}
|
2013-11-24 22:34:00 +02:00
|
|
|
|
2019-02-08 23:20:57 +01:00
|
|
|
// say goodbye if this is an unknown function
|
2019-01-30 10:41:10 +00:00
|
|
|
if (!(functionName in version[apiVersion])) {
|
2020-04-01 18:18:02 +02:00
|
|
|
throw new createHTTPError.NotFound('no such function');
|
2011-08-03 19:31:25 +01:00
|
|
|
}
|
2013-11-24 22:34:00 +02:00
|
|
|
|
2024-05-24 21:31:58 +02:00
|
|
|
|
2013-11-24 22:34:00 +02:00
|
|
|
|
2024-05-14 22:36:16 +02:00
|
|
|
if (apikey !== null && apikey.trim().length > 0) {
|
2024-05-22 21:16:19 +02:00
|
|
|
fields.apikey = fields.apikey || fields.api_key || fields.authorization;
|
2024-05-14 22:36:16 +02:00
|
|
|
// API key is configured, check if it is valid
|
|
|
|
if (fields.apikey !== apikey!.trim()) {
|
|
|
|
throw new createHTTPError.Unauthorized('no or wrong API Key');
|
|
|
|
}
|
|
|
|
} else {
|
2024-05-24 21:31:58 +02:00
|
|
|
if(!req.headers.authorization) {
|
|
|
|
throw new createHTTPError.Unauthorized('no or wrong API Key');
|
|
|
|
}
|
2024-05-14 22:36:16 +02:00
|
|
|
try {
|
|
|
|
await jwtVerify(req.headers.authorization!.replace("Bearer ", ""), publicKeyExported!, {algorithms: ['RS256'],
|
|
|
|
requiredClaims: ["admin"]})
|
|
|
|
} catch (e) {
|
|
|
|
throw new createHTTPError.Unauthorized('no or wrong OAuth token');
|
|
|
|
}
|
2012-09-09 18:20:16 +02:00
|
|
|
}
|
2011-12-16 15:41:11 -05:00
|
|
|
|
2019-01-30 10:41:10 +00:00
|
|
|
// sanitize any padIDs before continuing
|
2020-11-23 13:24:19 -05:00
|
|
|
if (fields.padID) {
|
|
|
|
fields.padID = await padManager.sanitizePadId(fields.padID);
|
2019-01-30 10:41:10 +00:00
|
|
|
}
|
|
|
|
// there was an 'else' here before - removed it to ensure
|
|
|
|
// that this sanitize step can't be circumvented by forcing
|
|
|
|
// the first branch to be taken
|
2020-11-23 13:24:19 -05:00
|
|
|
if (fields.padName) {
|
|
|
|
fields.padName = await padManager.sanitizePadId(fields.padName);
|
2011-12-16 15:41:11 -05:00
|
|
|
}
|
2019-01-30 10:41:10 +00:00
|
|
|
|
2019-02-08 23:20:57 +01:00
|
|
|
// put the function parameters in an array
|
2024-02-23 19:48:55 +01:00
|
|
|
// @ts-ignore
|
2020-11-23 13:24:19 -05:00
|
|
|
const functionParams = version[apiVersion][functionName].map((field) => fields[field]);
|
2019-02-08 23:20:57 +01:00
|
|
|
|
2020-03-29 19:35:25 +02:00
|
|
|
// call the api function
|
|
|
|
return api[functionName].apply(this, functionParams);
|
2020-11-23 13:24:19 -05:00
|
|
|
};
|