Merge pull request 'API Key based auth feature/26-api_auth' (#27) from feature/26-api_auth into dev
Some checks failed
continuous-integration/drone/push Build is failing
Some checks failed
continuous-integration/drone/push Build is failing
Reviewed-on: #27
This commit is contained in:
commit
2d031dae03
@ -35,6 +35,7 @@ The basic generation mechanism makes the templates and routes interchangeable (i
|
|||||||
| CURRENCY_SYMBOL | String | "€" | The your currency's symbol - used to generate pdf text.
|
| CURRENCY_SYMBOL | String | "€" | The your currency's symbol - used to generate pdf text.
|
||||||
| SPONSORING_RECEIPT_MINIMUM_AMOUNT | String | "10" | The mimimum total donation amount a sponsor has to donate to be able to receive a donation receipt - used to generate pdf text.
|
| SPONSORING_RECEIPT_MINIMUM_AMOUNT | String | "10" | The mimimum total donation amount a sponsor has to donate to be able to receive a donation receipt - used to generate pdf text.
|
||||||
| SPONOR_LOGOS | Array<String> | Empty png | The sponsor images you want to loop through. You can provide them via http url, local file or base64-encoded image.
|
| SPONOR_LOGOS | Array<String> | Empty png | The sponsor images you want to loop through. You can provide them via http url, local file or base64-encoded image.
|
||||||
|
| API_KEY | String(min length: 64) | Random generated string | The api key you want to use for auth (query-param `key`), has to be at least 64 chars long.
|
||||||
|
|
||||||
## Templates
|
## Templates
|
||||||
> The document server uses html templates to generate various pdf documents.
|
> The document server uses html templates to generate various pdf documents.
|
||||||
|
@ -14,6 +14,14 @@ export function generateSpec(storage: MetadataArgsStorage, schemas) {
|
|||||||
{
|
{
|
||||||
components: {
|
components: {
|
||||||
schemas,
|
schemas,
|
||||||
|
"securitySchemes": {
|
||||||
|
"AuthToken": {
|
||||||
|
"type": "apiKey",
|
||||||
|
"in": "query",
|
||||||
|
"name": "key",
|
||||||
|
description: "A simple api key. See the README's env section for more details."
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
info: {
|
info: {
|
||||||
description: "The the API for the LfK! document server.",
|
description: "The the API for the LfK! document server.",
|
||||||
|
@ -3,11 +3,13 @@ import "reflect-metadata";
|
|||||||
import { createExpressServer } from "routing-controllers";
|
import { createExpressServer } from "routing-controllers";
|
||||||
import { config, e as errors } from './config';
|
import { config, e as errors } from './config';
|
||||||
import loaders from "./loaders/index";
|
import loaders from "./loaders/index";
|
||||||
|
import AuthChecker from './middlewares/AuthChecker';
|
||||||
import { ErrorHandler } from './middlewares/ErrorHandler';
|
import { ErrorHandler } from './middlewares/ErrorHandler';
|
||||||
|
|
||||||
const CONTROLLERS_FILE_EXTENSION = process.env.NODE_ENV === 'production' ? 'js' : 'ts';
|
const CONTROLLERS_FILE_EXTENSION = process.env.NODE_ENV === 'production' ? 'js' : 'ts';
|
||||||
const app = createExpressServer({
|
const app = createExpressServer({
|
||||||
middlewares: [ErrorHandler],
|
middlewares: [ErrorHandler],
|
||||||
|
authorizationChecker: AuthChecker,
|
||||||
development: config.development,
|
development: config.development,
|
||||||
cors: true,
|
cors: true,
|
||||||
controllers: [`${__dirname}/controllers/*.${CONTROLLERS_FILE_EXTENSION}`],
|
controllers: [`${__dirname}/controllers/*.${CONTROLLERS_FILE_EXTENSION}`],
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import consola from "consola";
|
||||||
import { config as configDotenv } from 'dotenv';
|
import { config as configDotenv } from 'dotenv';
|
||||||
|
|
||||||
configDotenv();
|
configDotenv();
|
||||||
@ -9,7 +10,8 @@ export const config = {
|
|||||||
currency_symbol: process.env.CURRENCY_SYMBOL || "€",
|
currency_symbol: process.env.CURRENCY_SYMBOL || "€",
|
||||||
sponsoring_receipt_minimum_amount: process.env.SPONSORING_RECEIPT_MINIMUM_AMOUNT || "10",
|
sponsoring_receipt_minimum_amount: process.env.SPONSORING_RECEIPT_MINIMUM_AMOUNT || "10",
|
||||||
codeformat: process.env.CODEFORMAT || "qrcode",
|
codeformat: process.env.CODEFORMAT || "qrcode",
|
||||||
sponor_logos: getSponsorLogos()
|
sponor_logos: getSponsorLogos(),
|
||||||
|
api_key: getApiKey(),
|
||||||
}
|
}
|
||||||
let errors = 0
|
let errors = 0
|
||||||
if (typeof config.internal_port !== "number") {
|
if (typeof config.internal_port !== "number") {
|
||||||
@ -27,4 +29,24 @@ function getSponsorLogos(): string[] {
|
|||||||
return ["data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+P+/HgAFhAJ/wlseKgAAAABJRU5ErkJggg=="];
|
return ["data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+P+/HgAFhAJ/wlseKgAAAABJRU5ErkJggg=="];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getApiKey(): string {
|
||||||
|
const key = process.env.API_KEY;
|
||||||
|
if (!key) {
|
||||||
|
consola.info("No API key set - generating a random one...");
|
||||||
|
let result = '';
|
||||||
|
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
||||||
|
const charactersLength = characters.length;
|
||||||
|
for (var i = 0; i < 64; i++) {
|
||||||
|
result += characters.charAt(Math.floor(Math.random() * charactersLength));
|
||||||
|
}
|
||||||
|
consola.info(`API KEY: ${result}`)
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
if (key.length < 64) {
|
||||||
|
consola.error(`API key is too short - minimum: 64, current: ${key.length}`)
|
||||||
|
throw new Error("API_KEY too short.")
|
||||||
|
}
|
||||||
|
return key
|
||||||
|
}
|
||||||
export let e = errors
|
export let e = errors
|
@ -1,4 +1,4 @@
|
|||||||
import { Body, JsonController, Post, QueryParam, Res } from 'routing-controllers';
|
import { Authorized, Body, JsonController, Post, QueryParam, Res } from 'routing-controllers';
|
||||||
import { OpenAPI } from 'routing-controllers-openapi';
|
import { OpenAPI } from 'routing-controllers-openapi';
|
||||||
import { Runner } from '../models/Runner';
|
import { Runner } from '../models/Runner';
|
||||||
import { RunnerCard } from '../models/RunnerCard';
|
import { RunnerCard } from '../models/RunnerCard';
|
||||||
@ -10,6 +10,8 @@ import { PdfCreator } from '../PdfCreator';
|
|||||||
* All endpoints have to accept a locale query-param to support i18n.
|
* All endpoints have to accept a locale query-param to support i18n.
|
||||||
*/
|
*/
|
||||||
@JsonController()
|
@JsonController()
|
||||||
|
@Authorized()
|
||||||
|
@OpenAPI({ security: [{ "AuthToken": [] }] })
|
||||||
export class PdfController {
|
export class PdfController {
|
||||||
private pdf: PdfCreator = new PdfCreator();
|
private pdf: PdfCreator = new PdfCreator();
|
||||||
private initialized: boolean = false;
|
private initialized: boolean = false;
|
||||||
|
14
src/middlewares/AuthChecker.ts
Normal file
14
src/middlewares/AuthChecker.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import { Action } from "routing-controllers";
|
||||||
|
import { config } from '../config';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles authentication via jwt's (Bearer authorization header) for all api endpoints using the @Authorized decorator.
|
||||||
|
* @param action Routing-Controllers action object that provides request and response objects among other stuff.
|
||||||
|
* @param permissions The permissions that the endpoint using @Authorized requires.
|
||||||
|
*/
|
||||||
|
const AuthChecker = async (action: Action) => {
|
||||||
|
const provided_token = action.request.query.key;
|
||||||
|
return provided_token == config.api_key;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AuthChecker
|
Loading…
x
Reference in New Issue
Block a user