Readded support for apikey (#6382)
This commit is contained in:
parent
2c1916ac09
commit
556c3c8e5b
7 changed files with 74 additions and 14 deletions
|
@ -171,6 +171,13 @@
|
||||||
*/
|
*/
|
||||||
"showSettingsInAdminPage": "${SHOW_SETTINGS_IN_ADMIN_PAGE:true}",
|
"showSettingsInAdminPage": "${SHOW_SETTINGS_IN_ADMIN_PAGE:true}",
|
||||||
|
|
||||||
|
/*
|
||||||
|
The authentication method used by the server.
|
||||||
|
The default value is sso
|
||||||
|
If you want to use the old authentication system, change this to apikey
|
||||||
|
*/
|
||||||
|
"authenticationMethod": "${AUTHENTICATION_METHOD:sso}",
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Node native SSL support
|
* Node native SSL support
|
||||||
*
|
*
|
||||||
|
|
|
@ -586,6 +586,13 @@
|
||||||
*/
|
*/
|
||||||
"importMaxFileSize": 52428800, // 50 * 1024 * 1024
|
"importMaxFileSize": 52428800, // 50 * 1024 * 1024
|
||||||
|
|
||||||
|
/*
|
||||||
|
The authentication method used by the server.
|
||||||
|
The default value is sso
|
||||||
|
If you want to use the old authentication system, change this to apikey
|
||||||
|
*/
|
||||||
|
"authenticationMethod": "${AUTHENTICATION_METHOD:sso}",
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* From Etherpad 1.8.5 onwards, when Etherpad is in production mode commits from individual users are rate limited
|
* From Etherpad 1.8.5 onwards, when Etherpad is in production mode commits from individual users are rate limited
|
||||||
*
|
*
|
||||||
|
|
|
@ -27,7 +27,7 @@ import createHTTPError from 'http-errors';
|
||||||
import {Http2ServerRequest, Http2ServerResponse} from "node:http2";
|
import {Http2ServerRequest, Http2ServerResponse} from "node:http2";
|
||||||
import {publicKeyExported} from "../security/OAuth2Provider";
|
import {publicKeyExported} from "../security/OAuth2Provider";
|
||||||
import {jwtVerify} from "jose";
|
import {jwtVerify} from "jose";
|
||||||
|
import {apikey} from './APIKeyHandler'
|
||||||
// a list of all functions
|
// a list of all functions
|
||||||
const version:MapArrayType<any> = {};
|
const version:MapArrayType<any> = {};
|
||||||
|
|
||||||
|
@ -149,6 +149,7 @@ exports.version = version;
|
||||||
|
|
||||||
|
|
||||||
type APIFields = {
|
type APIFields = {
|
||||||
|
apikey: string;
|
||||||
api_key: string;
|
api_key: string;
|
||||||
padID: string;
|
padID: string;
|
||||||
padName: string;
|
padName: string;
|
||||||
|
@ -160,9 +161,9 @@ type APIFields = {
|
||||||
* @param {String} functionName the name of the called function
|
* @param {String} functionName the name of the called function
|
||||||
* @param fields the params of the called function
|
* @param fields the params of the called function
|
||||||
* @param req express request object
|
* @param req express request object
|
||||||
* @param res express response object
|
|
||||||
*/
|
*/
|
||||||
exports.handle = async function (apiVersion: string, functionName: string, fields: APIFields, req: Http2ServerRequest, res: Http2ServerResponse) {
|
exports.handle = async function (apiVersion: string, functionName: string, fields: APIFields,
|
||||||
|
req: Http2ServerRequest) {
|
||||||
// say goodbye if this is an unknown API version
|
// say goodbye if this is an unknown API version
|
||||||
if (!(apiVersion in version)) {
|
if (!(apiVersion in version)) {
|
||||||
throw new createHTTPError.NotFound('no such api version');
|
throw new createHTTPError.NotFound('no such api version');
|
||||||
|
@ -177,16 +178,21 @@ exports.handle = async function (apiVersion: string, functionName: string, field
|
||||||
throw new createHTTPError.Unauthorized('no or wrong API Key');
|
throw new createHTTPError.Unauthorized('no or wrong API Key');
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
if (apikey !== null && apikey.trim().length > 0) {
|
||||||
await jwtVerify(req.headers.authorization!.replace("Bearer ", ""), publicKeyExported!, {algorithms: ['RS256'],
|
fields.apikey = fields.apikey || fields.api_key;
|
||||||
requiredClaims: ["admin"]})
|
// API key is configured, check if it is valid
|
||||||
|
if (fields.apikey !== apikey!.trim()) {
|
||||||
} catch (e) {
|
throw new createHTTPError.Unauthorized('no or wrong API Key');
|
||||||
throw new createHTTPError.Unauthorized('no or wrong API Key');
|
}
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
await jwtVerify(req.headers.authorization!.replace("Bearer ", ""), publicKeyExported!, {algorithms: ['RS256'],
|
||||||
|
requiredClaims: ["admin"]})
|
||||||
|
} catch (e) {
|
||||||
|
throw new createHTTPError.Unauthorized('no or wrong OAuth token');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// sanitize any padIDs before continuing
|
// sanitize any padIDs before continuing
|
||||||
if (fields.padID) {
|
if (fields.padID) {
|
||||||
fields.padID = await padManager.sanitizePadId(fields.padID);
|
fields.padID = await padManager.sanitizePadId(fields.padID);
|
||||||
|
|
25
src/node/handler/APIKeyHandler.ts
Normal file
25
src/node/handler/APIKeyHandler.ts
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
const absolutePaths = require('../utils/AbsolutePaths');
|
||||||
|
import fs from 'fs';
|
||||||
|
import log4js from 'log4js';
|
||||||
|
const randomString = require('../utils/randomstring');
|
||||||
|
const argv = require('../utils/Cli').argv;
|
||||||
|
const settings = require('../utils/Settings');
|
||||||
|
|
||||||
|
const apiHandlerLogger = log4js.getLogger('APIHandler');
|
||||||
|
|
||||||
|
// ensure we have an apikey
|
||||||
|
export let apikey:string|null = null;
|
||||||
|
const apikeyFilename = absolutePaths.makeAbsolute(argv.apikey || './APIKEY.txt');
|
||||||
|
|
||||||
|
|
||||||
|
if(settings.authenticationMethod === 'apikey') {
|
||||||
|
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');
|
||||||
|
}
|
||||||
|
}
|
|
@ -608,7 +608,7 @@ exports.expressPreSession = async (hookName:string, {app}:any) => {
|
||||||
for (const funcName of Object.keys(apiHandler.version[version])) {
|
for (const funcName of Object.keys(apiHandler.version[version])) {
|
||||||
const handler = async (c: any, req:any, res:any) => {
|
const handler = async (c: any, req:any, res:any) => {
|
||||||
// parse fields from request
|
// parse fields from request
|
||||||
const {header, params, query} = c.request;
|
const {headers, params, query} = c.request;
|
||||||
|
|
||||||
// read form data if method was POST
|
// read form data if method was POST
|
||||||
let formData:MapArrayType<any> = {};
|
let formData:MapArrayType<any> = {};
|
||||||
|
@ -622,8 +622,7 @@ exports.expressPreSession = async (hookName:string, {app}:any) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const fields = Object.assign({}, header, params, query, formData);
|
const fields = Object.assign({}, headers, params, query, formData);
|
||||||
|
|
||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
logger.debug(`REQUEST, v${version}:${funcName}, ${JSON.stringify(fields)}`);
|
logger.debug(`REQUEST, v${version}:${funcName}, ${JSON.stringify(fields)}`);
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,5 +45,10 @@ for (let i = 0; i < argv.length; i++) {
|
||||||
exports.argv.sessionkey = arg;
|
exports.argv.sessionkey = arg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Override location of APIKEY.txt file
|
||||||
|
if (prevArg === '--apikey') {
|
||||||
|
exports.argv.apikey = arg;
|
||||||
|
}
|
||||||
|
|
||||||
prevArg = arg;
|
prevArg = arg;
|
||||||
}
|
}
|
||||||
|
|
|
@ -156,6 +156,15 @@ exports.socketIo = {
|
||||||
maxHttpBufferSize: 10000,
|
maxHttpBufferSize: 10000,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
The authentication method used by the server.
|
||||||
|
The default value is sso
|
||||||
|
If you want to use the old authentication system, change this to apikey
|
||||||
|
*/
|
||||||
|
exports.authenticationMethod = 'sso'
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The Type of the database
|
* The Type of the database
|
||||||
*/
|
*/
|
||||||
|
@ -519,6 +528,8 @@ exports.getGitCommit = () => {
|
||||||
// Return etherpad version from package.json
|
// Return etherpad version from package.json
|
||||||
exports.getEpVersion = () => require('../../package.json').version;
|
exports.getEpVersion = () => require('../../package.json').version;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Receives a settingsObj and, if the property name is a valid configuration
|
* Receives a settingsObj and, if the property name is a valid configuration
|
||||||
* item, stores it in the module's exported properties via a side effect.
|
* item, stores it in the module's exported properties via a side effect.
|
||||||
|
|
Loading…
Reference in a new issue