From 1b1c83e022e9880aede59720cb2ed833f943a6c4 Mon Sep 17 00:00:00 2001 From: Arno Kaimbacher Date: Tue, 30 Jun 2026 09:22:50 +0200 Subject: [PATCH] fix: refactor OaiController to improve error handling and enforce request validation --- app/Controllers/Http/Oai/OaiController.ts | 36 ++++++++++++++++------- app/models/activity.ts | 4 +-- 2 files changed, 27 insertions(+), 13 deletions(-) diff --git a/app/Controllers/Http/Oai/OaiController.ts b/app/Controllers/Http/Oai/OaiController.ts index 47d1708..e00836f 100644 --- a/app/Controllers/Http/Oai/OaiController.ts +++ b/app/Controllers/Http/Oai/OaiController.ts @@ -1,5 +1,4 @@ import type { HttpContext } from '@adonisjs/core/http'; -// import { RequestContract } from '@ioc:Adonis/Core/Request'; import { Request } from '@adonisjs/core/http'; import { XMLBuilder } from 'xmlbuilder2/lib/interfaces.js'; import { create } from 'xmlbuilder2'; @@ -18,11 +17,8 @@ import { getDomain, preg_match } from '#app/utils/utility-functions'; import DatasetXmlSerializer from '#app/Library/DatasetXmlSerializer'; import logger from '@adonisjs/core/services/logger'; import ResumptionToken from '#app/Library/Oai/ResumptionToken'; -// import Config from '@ioc:Adonis/Core/Config'; import config from '@adonisjs/core/services/config'; -// import { inject } from '@adonisjs/fold'; import { inject } from '@adonisjs/core'; -// import { TokenWorkerContract } from "MyApp/Models/TokenWorker"; import TokenWorkerContract from '#library/Oai/TokenWorkerContract'; import { ModelQueryBuilderContract } from '@adonisjs/lucid/types/model'; @@ -83,13 +79,13 @@ export default class OaiController { xsltParameter['oai_error_message'] = 'Only POST and GET methods are allowed for OAI-PMH.'; } - let earliestDateFromDb; // const oaiRequest: OaiParameter = request.body; try { this.firstPublishedDataset = await Dataset.earliestPublicationDate(); - this.firstPublishedDataset != null && - (earliestDateFromDb = this.firstPublishedDataset.server_date_published.toFormat("yyyy-MM-dd'T'HH:mm:ss'Z'")); - this.xsltParameter['earliestDatestamp'] = earliestDateFromDb; + // Pflichtfeld laut OAI-PMH: auch bei leerem Repository einen validen + // UTCdatetime liefern, sonst entsteht ein ungültiges leeres Element. + this.xsltParameter['earliestDatestamp'] = + this.firstPublishedDataset?.server_date_published.toFormat("yyyy-MM-dd'T'HH:mm:ss'Z'") ?? '1970-01-01T00:00:00Z'; // start the request await this.handleRequest(oaiRequest, request); } catch (error) { @@ -122,7 +118,7 @@ export default class OaiController { // logLevel: 10, }); xmlOutput = result.principalResult; - } catch (error) { + } catch (error: any) { return response.status(500).json({ message: 'An error occurred while creating the user', error: error.message, @@ -157,7 +153,7 @@ export default class OaiController { const verb = oaiRequest['verb']; this.xsltParameter['oai_verb'] = verb; if (verb === 'Identify') { - this.handleIdentify(); + this.handleIdentify(oaiRequest); } else if (verb === 'ListMetadataFormats') { this.handleListMetadataFormats(); } else if (verb == 'GetRecord') { @@ -184,7 +180,10 @@ export default class OaiController { } } - protected handleIdentify() { + protected handleIdentify(oaiRequest: Dictionary) { + // OAI-PMH: Identify akzeptiert außer `verb` keine Argumente. + this.assertOnlyVerb(oaiRequest); + // Get configuration values from environment or a dedicated configuration service const email = process.env.OAI_EMAIL ?? 'repository@geosphere.at'; const repositoryName = process.env.OAI_REPOSITORY_NAME ?? 'Tethys RDR'; @@ -203,6 +202,21 @@ export default class OaiController { this.xml.root().ele('Datasets'); } + /** + * Wirft badArgument, wenn der Request andere Parameter als `verb` enthält. + * Für Verben ohne zusätzliche Argumente (Identify, ListSets, ListMetadataFormats). + */ + private assertOnlyVerb(oaiRequest: Dictionary) { + const illegalKeys = Object.keys(oaiRequest).filter((key) => key !== 'verb'); + if (illegalKeys.length > 0) { + throw new OaiModelException( + StatusCodes.BAD_REQUEST, + `The request includes illegal arguments: ${illegalKeys.join(', ')}.`, + OaiErrorCodes.BADARGUMENT, + ); + } + } + protected handleListMetadataFormats() { this.xml.root().ele('Datasets'); } diff --git a/app/models/activity.ts b/app/models/activity.ts index 9d3403a..a2b81c3 100644 --- a/app/models/activity.ts +++ b/app/models/activity.ts @@ -7,7 +7,7 @@ import User from '#models/user'; import { SnakeCaseNamingStrategy } from '@adonisjs/lucid/orm'; export default class Activity extends BaseModel { - public static namingStrategy = new SnakeCaseNamingStrategy(); + public static namingStrategy = new SnakeCaseNamingStrategy(); public static primaryKey = 'id'; public static table = 'activities'; @@ -39,7 +39,7 @@ export default class Activity extends BaseModel { // declare properties: Record | null; @column() -declare properties: Record | null; + declare properties: Record | null; @column.dateTime({ autoCreate: true }) declare createdAt: DateTime;