From c9a39a81972d5e104b38b845609e27fb27543cfa Mon Sep 17 00:00:00 2001 From: AsterITA Date: Thu, 19 Dec 2024 18:19:50 +0100 Subject: [PATCH 01/23] feat: create purpose by delegate, add check on delegate --- packages/api-clients/open-api/bffApi.yml | 3 + packages/api-clients/open-api/purposeApi.yml | 3 + .../src/services/purposeService.ts | 8 +- packages/models/src/purpose/purpose.ts | 2 + .../src/model/domain/errors.ts | 9 + .../src/routers/PurposeRouter.ts | 71 ++---- .../src/services/purposeService.ts | 211 ++++++++-------- .../src/services/readModelService.ts | 61 +++-- .../src/services/validators.ts | 60 ++++- .../test/archivePurposeVersion.test.ts | 115 ++++++--- .../test/createPurpose.test.ts | 107 ++++---- .../test/createPurposeVersion.test.ts | 239 +++++++++++------- .../test/createReversePurpose.test.ts | 109 ++++---- .../test/deletePurpose.test.ts | 37 +-- .../test/updatePurpose.test.ts | 175 +++++++------ 15 files changed, 695 insertions(+), 515 deletions(-) diff --git a/packages/api-clients/open-api/bffApi.yml b/packages/api-clients/open-api/bffApi.yml index 425d0afd98..484cff67f2 100644 --- a/packages/api-clients/open-api/bffApi.yml +++ b/packages/api-clients/open-api/bffApi.yml @@ -15053,6 +15053,9 @@ components: consumerId: type: string format: uuid + delegationId: + type: string + format: uuid riskAnalysisForm: $ref: "#/components/schemas/RiskAnalysisFormSeed" title: diff --git a/packages/api-clients/open-api/purposeApi.yml b/packages/api-clients/open-api/purposeApi.yml index eef5a3b4e8..24e9fba9a5 100644 --- a/packages/api-clients/open-api/purposeApi.yml +++ b/packages/api-clients/open-api/purposeApi.yml @@ -1114,6 +1114,9 @@ components: consumerId: type: string format: uuid + delegationId: + type: string + format: uuid riskAnalysisForm: $ref: "#/components/schemas/RiskAnalysisFormSeed" title: diff --git a/packages/backend-for-frontend/src/services/purposeService.ts b/packages/backend-for-frontend/src/services/purposeService.ts index 9bc499771e..7b5ce329b3 100644 --- a/packages/backend-for-frontend/src/services/purposeService.ts +++ b/packages/backend-for-frontend/src/services/purposeService.ts @@ -260,7 +260,13 @@ export function purposeServiceBuilder( { logger, headers }: WithLogger ): Promise { logger.info( - `Creating purpose with eService ${createSeed.eserviceId} and consumer ${createSeed.consumerId}` + `Creating purpose with eService ${createSeed.eserviceId} and consumer ${ + createSeed.consumerId + }${ + createSeed.delegationId + ? ` with delegation ${createSeed.delegationId}` + : "" + }` ); const result = await purposeProcessClient.createPurpose(createSeed, { headers, diff --git a/packages/models/src/purpose/purpose.ts b/packages/models/src/purpose/purpose.ts index 1a36620c92..952e6088e1 100644 --- a/packages/models/src/purpose/purpose.ts +++ b/packages/models/src/purpose/purpose.ts @@ -1,5 +1,6 @@ import { z } from "zod"; import { + DelegationId, EServiceId, PurposeId, PurposeVersionDocumentId, @@ -48,6 +49,7 @@ export const Purpose = z.object({ id: PurposeId, eserviceId: EServiceId, consumerId: TenantId, + delegationId: DelegationId.optional(), versions: z.array(PurposeVersion), suspendedByConsumer: z.boolean().optional(), suspendedByProducer: z.boolean().optional(), diff --git a/packages/purpose-process/src/model/domain/errors.ts b/packages/purpose-process/src/model/domain/errors.ts index fe5f43c493..17aa09b845 100644 --- a/packages/purpose-process/src/model/domain/errors.ts +++ b/packages/purpose-process/src/model/domain/errors.ts @@ -41,6 +41,7 @@ export const errorCodes = { missingRiskAnalysis: "0024", purposeVersionStateConflict: "0025", riskAnalysisConfigLatestVersionNotFound: "0026", + operationNotAllowed: "0027", }; export type ErrorCodes = keyof typeof errorCodes; @@ -306,3 +307,11 @@ export function purposeVersionStateConflict( title: "Purpose version state conflict", }); } + +export function operationNotAllowed(requesterId: string): ApiError { + return new ApiError({ + detail: `Operation not allowed by ${requesterId}`, + code: "operationNotAllowed", + title: "Operation not allowed", + }); +} diff --git a/packages/purpose-process/src/routers/PurposeRouter.ts b/packages/purpose-process/src/routers/PurposeRouter.ts index 9caf3ab83d..6800ce50b8 100644 --- a/packages/purpose-process/src/routers/PurposeRouter.ts +++ b/packages/purpose-process/src/routers/PurposeRouter.ts @@ -141,12 +141,7 @@ const purposeRouter = ( const ctx = fromAppContext(req.ctx); try { const { purpose, isRiskAnalysisValid } = - await purposeService.createPurpose( - req.body, - req.ctx.authData.organizationId, - req.ctx.correlationId, - ctx.logger - ); + await purposeService.createPurpose(req.body, ctx); return res .status(200) .send( @@ -172,12 +167,7 @@ const purposeRouter = ( const ctx = fromAppContext(req.ctx); try { const { purpose, isRiskAnalysisValid } = - await purposeService.createReversePurpose( - req.ctx.authData.organizationId, - req.body, - ctx.correlationId, - ctx.logger - ); + await purposeService.createReversePurpose(req.body, ctx); return res .status(200) .send( @@ -203,13 +193,11 @@ const purposeRouter = ( const ctx = fromAppContext(req.ctx); try { const { purpose, isRiskAnalysisValid } = - await purposeService.updateReversePurpose({ - purposeId: unsafeBrandId(req.params.id), - reversePurposeUpdateContent: req.body, - organizationId: req.ctx.authData.organizationId, - correlationId: req.ctx.correlationId, - logger: ctx.logger, - }); + await purposeService.updateReversePurpose( + unsafeBrandId(req.params.id), + req.body, + ctx + ); return res .status(200) .send( @@ -271,13 +259,11 @@ const purposeRouter = ( const ctx = fromAppContext(req.ctx); try { const { purpose, isRiskAnalysisValid } = - await purposeService.updatePurpose({ - purposeId: unsafeBrandId(req.params.id), - purposeUpdateContent: req.body, - organizationId: req.ctx.authData.organizationId, - correlationId: req.ctx.correlationId, - logger: ctx.logger, - }); + await purposeService.updatePurpose( + unsafeBrandId(req.params.id), + req.body, + ctx + ); return res .status(200) .send( @@ -302,12 +288,7 @@ const purposeRouter = ( async (req, res) => { const ctx = fromAppContext(req.ctx); try { - await purposeService.deletePurpose({ - purposeId: unsafeBrandId(req.params.id), - organizationId: req.ctx.authData.organizationId, - correlationId: req.ctx.correlationId, - logger: ctx.logger, - }); + await purposeService.deletePurpose(unsafeBrandId(req.params.id), ctx); return res.status(204).send(); } catch (error) { const errorRes = makeApiProblem( @@ -326,13 +307,11 @@ const purposeRouter = ( async (req, res) => { const ctx = fromAppContext(req.ctx); try { - const purposeVersion = await purposeService.createPurposeVersion({ - purposeId: unsafeBrandId(req.params.purposeId), - seed: req.body, - organizationId: req.ctx.authData.organizationId, - correlationId: req.ctx.correlationId, - logger: ctx.logger, - }); + const purposeVersion = await purposeService.createPurposeVersion( + unsafeBrandId(req.params.purposeId), + req.body, + ctx + ); return res .status(200) .send( @@ -534,13 +513,13 @@ const purposeRouter = ( async (req, res) => { const ctx = fromAppContext(req.ctx); try { - const archivedVersion = await purposeService.archivePurposeVersion({ - purposeId: unsafeBrandId(req.params.purposeId), - versionId: unsafeBrandId(req.params.versionId), - organizationId: req.ctx.authData.organizationId, - correlationId: req.ctx.correlationId, - logger: ctx.logger, - }); + const archivedVersion = await purposeService.archivePurposeVersion( + { + purposeId: unsafeBrandId(req.params.purposeId), + versionId: unsafeBrandId(req.params.versionId), + }, + ctx + ); return res .status(200) .send( diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index a1f65b3e81..c2cb6e3d52 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -1,11 +1,14 @@ /* eslint-disable sonarjs/no-identical-functions */ import { + AppContext, + AuthData, CreateEvent, DB, FileManager, Logger, PDFGenerator, RiskAnalysisFormRules, + WithLogger, eventRepository, formatDateddMMyyyyHHmmss, getFormRulesByVersion, @@ -41,9 +44,8 @@ import { RiskAnalysisId, RiskAnalysis, CorrelationId, - delegationKind, Delegation, - DelegationKind, + DelegationId, } from "pagopa-interop-models"; import { purposeApi } from "pagopa-interop-api-clients"; import { P, match } from "ts-pattern"; @@ -92,7 +94,6 @@ import { import { config } from "../config/config.js"; import { GetPurposesFilters, ReadModelService } from "./readModelService.js"; import { - assertOrganizationIsAConsumer, assertEserviceMode, assertConsistentFreeOfCharge, isRiskAnalysisFormValid, @@ -110,6 +111,7 @@ import { isOverQuota, assertRequesterIsAllowedToRetrieveRiskAnalysisDocument, assertRequesterIsProducer, + assertRequesterCanActAsConsumer, } from "./validators.js"; import { riskAnalysisDocumentBuilder } from "./riskAnalysisDocumentBuilder.js"; @@ -220,14 +222,6 @@ async function retrieveTenantKind( return tenant.kind; } -async function retrieveActiveDelegation( - eserviceId: EServiceId, - delegationKind: DelegationKind, - readModelService: ReadModelService -): Promise { - return readModelService.getActiveDelegation(eserviceId, delegationKind); -} - // eslint-disable-next-line @typescript-eslint/explicit-function-return-type export function purposeServiceBuilder( dbInstance: DB, @@ -252,9 +246,8 @@ export function purposeServiceBuilder( ); const activeProducerDelegation = - await readModelService.getActiveDelegation( - purpose.data.eserviceId, - delegationKind.delegatedProducer + await readModelService.getActiveProducerDelegationByEserviceId( + purpose.data.eserviceId ); return authorizeRiskAnalysisForm({ @@ -399,19 +392,11 @@ export function purposeServiceBuilder( }); await repository.createEvent(event); }, - async updatePurpose({ - purposeId, - purposeUpdateContent, - organizationId, - correlationId, - logger, - }: { - purposeId: PurposeId; - purposeUpdateContent: purposeApi.PurposeUpdateContent; - organizationId: TenantId; - correlationId: CorrelationId; - logger: Logger; - }): Promise<{ purpose: Purpose; isRiskAnalysisValid: boolean }> { + async updatePurpose( + purposeId: PurposeId, + purposeUpdateContent: purposeApi.PurposeUpdateContent, + { authData, correlationId, logger }: WithLogger + ): Promise<{ purpose: Purpose; isRiskAnalysisValid: boolean }> { logger.info(`Updating Purpose ${purposeId}`); return await performUpdatePurpose( purposeId, @@ -419,25 +404,17 @@ export function purposeServiceBuilder( updateContent: purposeUpdateContent, mode: eserviceMode.deliver, }, - organizationId, + authData, readModelService, correlationId, repository ); }, - async updateReversePurpose({ - purposeId, - reversePurposeUpdateContent, - organizationId, - correlationId, - logger, - }: { - purposeId: PurposeId; - reversePurposeUpdateContent: purposeApi.ReversePurposeUpdateContent; - organizationId: TenantId; - correlationId: CorrelationId; - logger: Logger; - }): Promise<{ purpose: Purpose; isRiskAnalysisValid: boolean }> { + async updateReversePurpose( + purposeId: PurposeId, + reversePurposeUpdateContent: purposeApi.ReversePurposeUpdateContent, + { authData, correlationId, logger }: WithLogger + ): Promise<{ purpose: Purpose; isRiskAnalysisValid: boolean }> { logger.info(`Updating Reverse Purpose ${purposeId}`); return await performUpdatePurpose( purposeId, @@ -445,33 +422,32 @@ export function purposeServiceBuilder( updateContent: reversePurposeUpdateContent, mode: eserviceMode.receive, }, - organizationId, + authData, readModelService, correlationId, repository ); }, - async deletePurpose({ - purposeId, - organizationId, - correlationId, - logger, - }: { - purposeId: PurposeId; - organizationId: TenantId; - correlationId: CorrelationId; - logger: Logger; - }): Promise { + async deletePurpose( + purposeId: PurposeId, + { authData, correlationId, logger }: WithLogger + ): Promise { logger.info(`Deleting Purpose ${purposeId}`); const purpose = await retrievePurpose(purposeId, readModelService); - assertOrganizationIsAConsumer(organizationId, purpose.data.consumerId); - if (!isDeletable(purpose.data)) { throw purposeCannotBeDeleted(purpose.data.id); } + assertRequesterCanActAsConsumer( + purpose.data, + authData, + await readModelService.getActiveConsumerDelegationByEserviceId( + purpose.data.eserviceId + ) + ); + const event = purposeIsDraft(purpose.data) ? toCreateEventDraftPurposeDeleted({ purpose: purpose.data, @@ -486,24 +462,28 @@ export function purposeServiceBuilder( await repository.createEvent(event); }, - async archivePurposeVersion({ - purposeId, - versionId, - organizationId, - correlationId, - logger, - }: { - purposeId: PurposeId; - versionId: PurposeVersionId; - organizationId: TenantId; - correlationId: CorrelationId; - logger: Logger; - }): Promise { + async archivePurposeVersion( + { + purposeId, + versionId, + }: { + purposeId: PurposeId; + versionId: PurposeVersionId; + }, + { authData, correlationId, logger }: WithLogger + ): Promise { logger.info(`Archiving Version ${versionId} in Purpose ${purposeId}`); const purpose = await retrievePurpose(purposeId, readModelService); - assertOrganizationIsAConsumer(organizationId, purpose.data.consumerId); + assertRequesterCanActAsConsumer( + purpose.data, + authData, + await readModelService.getActiveConsumerDelegationByEserviceId( + purpose.data.eserviceId + ) + ); + const purposeVersion = retrievePurposeVersion(versionId, purpose); if (!isArchivable(purposeVersion)) { @@ -633,11 +613,10 @@ export function purposeServiceBuilder( throw eserviceNotFound(purpose.eserviceId); } - const activeProducerDelegation = await retrieveActiveDelegation( - eservice.id, - delegationKind.delegatedProducer, - readModelService - ); + const activeProducerDelegation = + await readModelService.getActiveProducerDelegationByEserviceId( + eservice.id + ); return { purpose, @@ -668,24 +647,22 @@ export function purposeServiceBuilder( totalCount: purposesList.totalCount, }; }, - async createPurposeVersion({ - purposeId, - seed, - organizationId, - correlationId, - logger, - }: { - purposeId: PurposeId; - seed: purposeApi.PurposeVersionSeed; - organizationId: TenantId; - correlationId: CorrelationId; - logger: Logger; - }): Promise { + async createPurposeVersion( + purposeId: PurposeId, + seed: purposeApi.PurposeVersionSeed, + { authData, correlationId, logger }: WithLogger + ): Promise { logger.info(`Creating Version for Purpose ${purposeId}`); const purpose = await retrievePurpose(purposeId, readModelService); - assertOrganizationIsAConsumer(organizationId, purpose.data.consumerId); + assertRequesterCanActAsConsumer( + purpose.data, + authData, + await readModelService.getActiveConsumerDelegationByEserviceId( + purpose.data.eserviceId + ) + ); const previousVersion = [ ...purpose.data.versions.filter( @@ -1032,16 +1009,30 @@ export function purposeServiceBuilder( }, async createPurpose( purposeSeed: purposeApi.PurposeSeed, - organizationId: TenantId, - correlationId: CorrelationId, - logger: Logger + { authData, correlationId, logger }: WithLogger ): Promise<{ purpose: Purpose; isRiskAnalysisValid: boolean }> { logger.info( - `Creating Purpose for EService ${purposeSeed.eserviceId} and Consumer ${purposeSeed.consumerId}` + `Creating Purpose for EService ${purposeSeed.eserviceId} and Consumer ${ + purposeSeed.consumerId + }${ + purposeSeed.delegationId + ? ` with delegation ${purposeSeed.delegationId}` + : "" + }` ); const eserviceId = unsafeBrandId(purposeSeed.eserviceId); const consumerId = unsafeBrandId(purposeSeed.consumerId); - assertOrganizationIsAConsumer(organizationId, consumerId); + const delegationId = purposeSeed.delegationId + ? unsafeBrandId(purposeSeed.delegationId) + : undefined; + + assertRequesterCanActAsConsumer( + { eserviceId, consumerId }, + authData, + delegationId + ? await readModelService.getActiveConsumerDelegationById(delegationId) + : undefined + ); assertConsistentFreeOfCharge( purposeSeed.isFreeOfCharge, @@ -1051,7 +1042,7 @@ export function purposeServiceBuilder( const validatedFormSeed = validateAndTransformRiskAnalysis( purposeSeed.riskAnalysisForm, false, - await retrieveTenantKind(organizationId, readModelService) + await retrieveTenantKind(authData.organizationId, readModelService) ); await retrieveActiveAgreement(eserviceId, consumerId, readModelService); @@ -1070,6 +1061,7 @@ export function purposeServiceBuilder( createdAt: new Date(), eserviceId, consumerId, + delegationId, versions: [ { id: generateId(), @@ -1089,10 +1081,8 @@ export function purposeServiceBuilder( return { purpose, isRiskAnalysisValid: validatedFormSeed !== undefined }; }, async createReversePurpose( - organizationId: TenantId, seed: purposeApi.EServicePurposeSeed, - correlationId: CorrelationId, - logger: Logger + { authData, correlationId, logger }: WithLogger ): Promise<{ purpose: Purpose; isRiskAnalysisValid: boolean }> { logger.info( `Creating Purpose for EService ${seed.eServiceId}, Consumer ${seed.consumerId}` @@ -1101,7 +1091,14 @@ export function purposeServiceBuilder( const eserviceId: EServiceId = unsafeBrandId(seed.eServiceId); const consumerId: TenantId = unsafeBrandId(seed.consumerId); - assertOrganizationIsAConsumer(organizationId, consumerId); + assertRequesterCanActAsConsumer( + { eserviceId, consumerId }, + authData, + await readModelService.getActiveConsumerDelegationByEserviceId( + eserviceId + ) + ); + const eservice = await retrieveEService(eserviceId, readModelService); assertEserviceMode(eservice, eserviceMode.receive); @@ -1395,10 +1392,8 @@ const getOrganizationRole = async ({ return ownership.CONSUMER; } - const activeProducerDelegation = await readModelService.getActiveDelegation( - eserviceId, - delegationKind.delegatedProducer - ); + const activeProducerDelegation = + await readModelService.getActiveProducerDelegationByEserviceId(eserviceId); if ( (activeProducerDelegation && @@ -1462,7 +1457,7 @@ const performUpdatePurpose = async ( mode: "Receive"; updateContent: purposeApi.ReversePurposeUpdateContent; }, - organizationId: TenantId, + authData: AuthData, readModelService: ReadModelService, correlationId: CorrelationId, repository: { @@ -1471,7 +1466,6 @@ const performUpdatePurpose = async ( // eslint-disable-next-line max-params ): Promise<{ purpose: Purpose; isRiskAnalysisValid: boolean }> => { const purpose = await retrievePurpose(purposeId, readModelService); - assertOrganizationIsAConsumer(organizationId, purpose.data.consumerId); assertPurposeIsDraft(purpose.data); if (updateContent.title !== purpose.data.title) { @@ -1482,6 +1476,15 @@ const performUpdatePurpose = async ( title: updateContent.title, }); } + + assertRequesterCanActAsConsumer( + purpose.data, + authData, + await readModelService.getActiveConsumerDelegationByEserviceId( + purpose.data.eserviceId + ) + ); + const eservice = await retrieveEService( purpose.data.eserviceId, readModelService diff --git a/packages/purpose-process/src/services/readModelService.ts b/packages/purpose-process/src/services/readModelService.ts index 3651e52a83..d10c34ccfe 100644 --- a/packages/purpose-process/src/services/readModelService.ts +++ b/packages/purpose-process/src/services/readModelService.ts @@ -4,6 +4,7 @@ import { TenantCollection, PurposeCollection, ReadModelFilter, + DelegationCollection, } from "pagopa-interop-commons"; import { EService, @@ -25,8 +26,9 @@ import { TenantReadModel, delegationState, Delegation, - DelegationKind, delegationKind, + DelegationReadModel, + DelegationId, } from "pagopa-interop-models"; import { Document, Filter, WithId } from "mongodb"; import { z } from "zod"; @@ -112,6 +114,27 @@ async function getTenant( } } +async function getDelegation( + delegations: DelegationCollection, + filter: Filter<{ data: DelegationReadModel }> +): Promise { + const data = await delegations.findOne(filter, { + projection: { data: true }, + }); + if (data) { + const result = Delegation.safeParse(data.data); + if (!result.success) { + throw genericInternalError( + `Unable to parse delegation item: result ${JSON.stringify( + result + )} - data ${JSON.stringify(data)} ` + ); + } + return result.data; + } + return undefined; +} + async function buildGetPurposesAggregation( filters: GetPurposesFilters, eservices: EServiceCollection @@ -375,24 +398,32 @@ export function readModelServiceBuilder( return result.data; }, - async getActiveDelegation( - eserviceId: EServiceId, - kind: DelegationKind + async getActiveProducerDelegationByEserviceId( + eserviceId: EServiceId ): Promise { - const data = await delegations.findOne({ + return getDelegation(delegations, { "data.eserviceId": eserviceId, - "data.kind": kind, "data.state": delegationState.active, + "data.kind": delegationKind.delegatedProducer, + }); + }, + async getActiveConsumerDelegationByEserviceId( + eserviceId: EServiceId + ): Promise { + return getDelegation(delegations, { + "data.eserviceId": eserviceId, + "data.state": delegationState.active, + "data.kind": delegationKind.delegatedConsumer, + }); + }, + async getActiveConsumerDelegationById( + delegationId: DelegationId + ): Promise { + return getDelegation(delegations, { + "data.id": delegationId, + "data.state": delegationState.active, + "data.kind": delegationKind.delegatedConsumer, }); - if (!data) { - return undefined; - } else { - const result = Delegation.safeParse(data.data); - if (!result.success) { - throw genericError("Unable to parse delegation item"); - } - return result.data; - } }, }; } diff --git a/packages/purpose-process/src/services/validators.ts b/packages/purpose-process/src/services/validators.ts index 22a682c858..0721a9e21d 100644 --- a/packages/purpose-process/src/services/validators.ts +++ b/packages/purpose-process/src/services/validators.ts @@ -10,12 +10,15 @@ import { purposeVersionState, EServiceId, delegationKind, + Delegation, + delegationState, } from "pagopa-interop-models"; import { validateRiskAnalysis, riskAnalysisFormToRiskAnalysisFormToValidate, RiskAnalysisValidatedForm, riskAnalysisValidatedFormToNewRiskAnalysisForm, + AuthData, } from "pagopa-interop-commons"; import { purposeApi } from "pagopa-interop-api-clients"; import { @@ -23,6 +26,7 @@ import { duplicatedPurposeTitle, eServiceModeNotAllowed, missingFreeOfChargeReason, + operationNotAllowed, organizationIsNotTheConsumer, organizationIsNotTheProducer, organizationNotAllowed, @@ -81,12 +85,12 @@ export const assertConsistentFreeOfCharge = ( } }; -export const assertOrganizationIsAConsumer = ( - organizationId: TenantId, - consumerId: TenantId +const assertOrganizationIsAConsumer = ( + purpose: Pick, + authData: AuthData ): void => { - if (organizationId !== consumerId) { - throw organizationIsNotTheConsumer(organizationId); + if (authData.organizationId !== purpose.consumerId) { + throw organizationIsNotTheConsumer(authData.organizationId); } }; @@ -269,10 +273,8 @@ export const assertRequesterIsAllowedToRetrieveRiskAnalysisDocument = async ({ return; } - const activeProducerDelegation = await readModelService.getActiveDelegation( - eserviceId, - delegationKind.delegatedProducer - ); + const activeProducerDelegation = + await readModelService.getActiveProducerDelegationByEserviceId(eserviceId); if ( activeProducerDelegation && @@ -295,10 +297,8 @@ export const assertRequesterIsProducer = async ({ producerId: TenantId; readModelService: ReadModelService; }): Promise => { - const activeProducerDelegation = await readModelService.getActiveDelegation( - eserviceId, - delegationKind.delegatedProducer - ); + const activeProducerDelegation = + await readModelService.getActiveProducerDelegationByEserviceId(eserviceId); if ( (activeProducerDelegation && @@ -310,3 +310,37 @@ export const assertRequesterIsProducer = async ({ throw organizationIsNotTheProducer(organizationId); }; + +export const assertRequesterCanActAsConsumer = ( + purpose: Pick, + authData: AuthData, + activeConsumerDelegation: Delegation | undefined +): void => { + if (!activeConsumerDelegation) { + // No active consumer delegation, the requester is authorized only if they are the consumer + assertOrganizationIsAConsumer(purpose, authData); + } else { + // Active consumer delegation, the requester is authorized only if they are the delegate + assertRequesterIsDelegateConsumer( + purpose, + authData, + activeConsumerDelegation + ); + } +}; + +const assertRequesterIsDelegateConsumer = ( + purpose: Pick, + authData: Pick, + activeConsumerDelegation: Delegation | undefined +): void => { + if ( + activeConsumerDelegation?.delegateId !== authData.organizationId || + activeConsumerDelegation?.delegatorId !== purpose.consumerId || + activeConsumerDelegation?.eserviceId !== purpose.eserviceId || + activeConsumerDelegation?.kind !== delegationKind.delegatedConsumer || + activeConsumerDelegation?.state !== delegationState.active + ) { + throw operationNotAllowed(authData.organizationId); + } +}; diff --git a/packages/purpose-process/test/archivePurposeVersion.test.ts b/packages/purpose-process/test/archivePurposeVersion.test.ts index c34386fb83..61aa92644e 100644 --- a/packages/purpose-process/test/archivePurposeVersion.test.ts +++ b/packages/purpose-process/test/archivePurposeVersion.test.ts @@ -3,6 +3,7 @@ import { getMockPurposeVersion, getMockPurpose, decodeProtobufPayload, + getRandomAuthData, } from "pagopa-interop-commons-test"; import { PurposeVersion, @@ -45,13 +46,18 @@ describe("archivePurposeVersion", () => { }; await addOnePurpose(mockPurpose); - const returnedPurposeVersion = await purposeService.archivePurposeVersion({ - purposeId: mockPurpose.id, - versionId: mockPurposeVersion.id, - organizationId: mockPurpose.consumerId, - correlationId: generateId(), - logger: genericLogger, - }); + const returnedPurposeVersion = await purposeService.archivePurposeVersion( + { + purposeId: mockPurpose.id, + versionId: mockPurposeVersion.id, + }, + { + authData: getRandomAuthData(mockPurpose.consumerId), + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + } + ); const writtenEvent = await readLastPurposeEvent(mockPurpose.id); @@ -106,13 +112,18 @@ describe("archivePurposeVersion", () => { }; await addOnePurpose(mockPurpose); - const returnedPurposeVersion = await purposeService.archivePurposeVersion({ - purposeId: mockPurpose.id, - versionId: mockPurposeVersion1.id, - organizationId: mockPurpose.consumerId, - correlationId: generateId(), - logger: genericLogger, - }); + const returnedPurposeVersion = await purposeService.archivePurposeVersion( + { + purposeId: mockPurpose.id, + versionId: mockPurposeVersion1.id, + }, + { + authData: getRandomAuthData(mockPurpose.consumerId), + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + } + ); const writtenEvent = await readLastPurposeEvent(mockPurpose.id); @@ -156,13 +167,18 @@ describe("archivePurposeVersion", () => { await addOnePurpose(mockPurpose); expect( - purposeService.archivePurposeVersion({ - purposeId: randomPurposeId, - versionId: randomVersionId, - organizationId: mockPurpose.consumerId, - correlationId: generateId(), - logger: genericLogger, - }) + purposeService.archivePurposeVersion( + { + purposeId: randomPurposeId, + versionId: randomVersionId, + }, + { + authData: getRandomAuthData(mockPurpose.consumerId), + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + } + ) ).rejects.toThrowError(purposeNotFound(randomPurposeId)); }); it("should throw organizationIsNotTheConsumer if the requester is not the consumer", async () => { @@ -180,13 +196,18 @@ describe("archivePurposeVersion", () => { await addOnePurpose(mockPurpose); expect( - purposeService.archivePurposeVersion({ - purposeId: mockPurpose.id, - versionId: mockPurposeVersion.id, - organizationId: randomOrganizationId, - correlationId: generateId(), - logger: genericLogger, - }) + purposeService.archivePurposeVersion( + { + purposeId: mockPurpose.id, + versionId: mockPurposeVersion.id, + }, + { + authData: getRandomAuthData(randomOrganizationId), + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + } + ) ).rejects.toThrowError(organizationIsNotTheConsumer(randomOrganizationId)); }); it("should throw purposeVersionNotFound if the purpose version doesn't exist", async () => { @@ -199,13 +220,18 @@ describe("archivePurposeVersion", () => { await addOnePurpose(mockPurpose); expect( - purposeService.archivePurposeVersion({ - purposeId: mockPurpose.id, - versionId: randomVersionId, - organizationId: mockPurpose.consumerId, - correlationId: generateId(), - logger: genericLogger, - }) + purposeService.archivePurposeVersion( + { + purposeId: mockPurpose.id, + versionId: randomVersionId, + }, + { + authData: getRandomAuthData(mockPurpose.consumerId), + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + } + ) ).rejects.toThrowError( purposeVersionNotFound(mockPurpose.id, randomVersionId) ); @@ -229,13 +255,18 @@ describe("archivePurposeVersion", () => { await addOnePurpose(mockPurpose); expect( - purposeService.archivePurposeVersion({ - purposeId: mockPurpose.id, - versionId: mockPurposeVersion.id, - organizationId: mockPurpose.consumerId, - correlationId: generateId(), - logger: genericLogger, - }) + purposeService.archivePurposeVersion( + { + purposeId: mockPurpose.id, + versionId: mockPurposeVersion.id, + }, + { + authData: getRandomAuthData(mockPurpose.consumerId), + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + } + ) ).rejects.toThrowError( notValidVersionState(mockPurposeVersion.id, mockPurposeVersion.state) ); diff --git a/packages/purpose-process/test/createPurpose.test.ts b/packages/purpose-process/test/createPurpose.test.ts index a367beae58..99783089be 100644 --- a/packages/purpose-process/test/createPurpose.test.ts +++ b/packages/purpose-process/test/createPurpose.test.ts @@ -19,6 +19,7 @@ import { toReadModelAgreement, unsafeBrandId, toReadModelTenant, + TenantId, } from "pagopa-interop-models"; import { purposeApi } from "pagopa-interop-api-clients"; import { describe, expect, it, vi } from "vitest"; @@ -30,6 +31,7 @@ import { getMockTenant, getMockPurpose, getMockDescriptor, + getRandomAuthData, } from "pagopa-interop-commons-test"; import { genericLogger, @@ -103,9 +105,14 @@ describe("createPurpose", () => { const { purpose, isRiskAnalysisValid } = await purposeService.createPurpose( purposeSeed, - unsafeBrandId(purposeSeed.consumerId), - generateId(), - genericLogger + { + authData: getRandomAuthData( + unsafeBrandId(purposeSeed.consumerId) + ), + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + } ); const writtenEvent = await readLastPurposeEvent(purpose.id); @@ -174,12 +181,14 @@ describe("createPurpose", () => { }; expect( - purposeService.createPurpose( - seed, - unsafeBrandId(purposeSeed.consumerId), - generateId(), - genericLogger - ) + purposeService.createPurpose(seed, { + authData: getRandomAuthData( + unsafeBrandId(purposeSeed.consumerId) + ), + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + }) ).rejects.toThrowError(missingFreeOfChargeReason()); }); it("should throw tenantKindNotFound if the kind doesn't exists", async () => { @@ -209,22 +218,26 @@ describe("createPurpose", () => { await writeInReadmodel(toReadModelEService(eService), eservices); expect( - purposeService.createPurpose( - seed, - unsafeBrandId(purposeSeed.consumerId), - generateId(), - genericLogger - ) + purposeService.createPurpose(seed, { + authData: getRandomAuthData( + unsafeBrandId(purposeSeed.consumerId) + ), + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + }) ).rejects.toThrowError(tenantKindNotFound(tenantWithoutKind.id)); }); it("should throw tenantNotFound if the tenant doesn't exists", async () => { expect( - purposeService.createPurpose( - purposeSeed, - unsafeBrandId(purposeSeed.consumerId), - generateId(), - genericLogger - ) + purposeService.createPurpose(purposeSeed, { + authData: getRandomAuthData( + unsafeBrandId(purposeSeed.consumerId) + ), + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + }) ).rejects.toThrowError(tenantNotFound(tenant.id)); }); it("should throw agreementNotFound if the agreement doesn't exists ", async () => { @@ -261,12 +274,12 @@ describe("createPurpose", () => { await writeInReadmodel(toReadModelEService(eService), eservices); expect( - purposeService.createPurpose( - seed, - unsafeBrandId(seed.consumerId), - generateId(), - genericLogger - ) + purposeService.createPurpose(seed, { + authData: getRandomAuthData(unsafeBrandId(seed.consumerId)), + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + }) ).rejects.toThrowError(agreementNotFound(eService.id, tenant.id)); }); it("should throw organizationIsNotTheConsumer if the requester is not the consumer", async () => { @@ -283,12 +296,14 @@ describe("createPurpose", () => { }; expect( - purposeService.createPurpose( - seed, - unsafeBrandId(purposeSeed.consumerId), - generateId(), - genericLogger - ) + purposeService.createPurpose(seed, { + authData: getRandomAuthData( + unsafeBrandId(purposeSeed.consumerId) + ), + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + }) ).rejects.toThrowError( organizationIsNotTheConsumer(unsafeBrandId(purposeSeed.consumerId)) ); @@ -312,12 +327,12 @@ describe("createPurpose", () => { }; expect( - purposeService.createPurpose( - seed, - unsafeBrandId(purposeSeed.consumerId), - generateId(), - genericLogger - ) + purposeService.createPurpose(seed, { + authData: getRandomAuthData(unsafeBrandId(seed.consumerId)), + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + }) ).rejects.toThrowError( riskAnalysisValidationFailed([ unexpectedRulesVersionError(mockInvalidRiskAnalysisForm.version), @@ -341,12 +356,14 @@ describe("createPurpose", () => { await writeInReadmodel(toReadModelEService(eService1), eservices); expect( - purposeService.createPurpose( - purposeSeed, - unsafeBrandId(purposeSeed.consumerId), - generateId(), - genericLogger - ) + purposeService.createPurpose(purposeSeed, { + authData: getRandomAuthData( + unsafeBrandId(purposeSeed.consumerId) + ), + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + }) ).rejects.toThrowError(duplicatedPurposeTitle(purposeSeed.title)); }); }); diff --git a/packages/purpose-process/test/createPurposeVersion.test.ts b/packages/purpose-process/test/createPurposeVersion.test.ts index 4594aabc46..9896d51093 100644 --- a/packages/purpose-process/test/createPurposeVersion.test.ts +++ b/packages/purpose-process/test/createPurposeVersion.test.ts @@ -13,6 +13,7 @@ import { getMockPurpose, getMockValidRiskAnalysisForm, writeInReadmodel, + getRandomAuthData, } from "pagopa-interop-commons-test"; import { purposeVersionState, @@ -121,15 +122,18 @@ describe("createPurposeVersion", () => { await writeInReadmodel(toReadModelTenant(mockConsumer), tenants); await writeInReadmodel(toReadModelTenant(mockProducer), tenants); - const returnedPurposeVersion = await purposeService.createPurposeVersion({ - purposeId: mockPurpose.id, - seed: { + const returnedPurposeVersion = await purposeService.createPurposeVersion( + mockPurpose.id, + { dailyCalls: 24, }, - organizationId: mockPurpose.consumerId, - correlationId: generateId(), - logger: genericLogger, - }); + { + authData: getRandomAuthData(mockPurpose.consumerId), + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + } + ); const writtenEvent = await readLastEventByStreamId( mockPurpose.id, @@ -182,15 +186,18 @@ describe("createPurposeVersion", () => { await writeInReadmodel(toReadModelTenant(mockConsumer), tenants); await writeInReadmodel(toReadModelTenant(mockProducer), tenants); - const returnedPurposeVersion = await purposeService.createPurposeVersion({ - purposeId: mockPurpose.id, - seed: { + const returnedPurposeVersion = await purposeService.createPurposeVersion( + mockPurpose.id, + { dailyCalls: 4, }, - organizationId: mockPurpose.consumerId, - correlationId: generateId(), - logger: genericLogger, - }); + { + authData: getRandomAuthData(mockPurpose.consumerId), + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + } + ); const writtenEvent = await readLastEventByStreamId( mockPurpose.id, @@ -249,15 +256,18 @@ describe("createPurposeVersion", () => { await writeInReadmodel(toReadModelTenant(mockConsumer), tenants); await writeInReadmodel(toReadModelTenant(mockProducer), tenants); - const returnedPurposeVersion = await purposeService.createPurposeVersion({ - purposeId: mockPurpose.id, - seed: { + const returnedPurposeVersion = await purposeService.createPurposeVersion( + mockPurpose.id, + { dailyCalls: 30, }, - organizationId: mockPurpose.consumerId, - correlationId: generateId(), - logger: genericLogger, - }); + { + authData: getRandomAuthData(mockPurpose.consumerId), + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + } + ); const writtenEvent = await readLastEventByStreamId( mockPurpose.id, @@ -306,15 +316,18 @@ describe("createPurposeVersion", () => { expect( async () => - await purposeService.createPurposeVersion({ - purposeId: mockPurpose.id, - seed: { + await purposeService.createPurposeVersion( + mockPurpose.id, + { dailyCalls: mockPurposeVersion.dailyCalls, }, - organizationId: mockPurpose.consumerId, - correlationId: generateId(), - logger: genericLogger, - }) + { + authData: getRandomAuthData(mockPurpose.consumerId), + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + } + ) ).rejects.toThrowError(unchangedDailyCalls(mockPurpose.id)); }); @@ -326,15 +339,18 @@ describe("createPurposeVersion", () => { await writeInReadmodel(toReadModelTenant(mockProducer), tenants); expect(async () => { - await purposeService.createPurposeVersion({ - purposeId: mockPurpose.id, - seed: { + await purposeService.createPurposeVersion( + mockPurpose.id, + { dailyCalls: 1000, }, - organizationId: mockEService.producerId, - correlationId: generateId(), - logger: genericLogger, - }); + { + authData: getRandomAuthData(mockEService.producerId), + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + } + ); }).rejects.toThrowError( organizationIsNotTheConsumer(mockEService.producerId) ); @@ -348,15 +364,18 @@ describe("createPurposeVersion", () => { await writeInReadmodel(toReadModelTenant(mockProducer), tenants); expect(async () => { - await purposeService.createPurposeVersion({ - purposeId: mockPurpose.id, - seed: { + await purposeService.createPurposeVersion( + mockPurpose.id, + { dailyCalls: 20, }, - organizationId: mockPurpose.consumerId, - correlationId: generateId(), - logger: genericLogger, - }); + { + authData: getRandomAuthData(mockPurpose.consumerId), + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + } + ); }).rejects.toThrowError(eserviceNotFound(mockEService.id)); }); @@ -371,15 +390,18 @@ describe("createPurposeVersion", () => { await writeInReadmodel(toReadModelTenant(anotherTenant), tenants); expect(async () => { - await purposeService.createPurposeVersion({ - purposeId: mockPurpose.id, - seed: { + await purposeService.createPurposeVersion( + mockPurpose.id, + { dailyCalls: 20, }, - organizationId: anotherTenant.id, - correlationId: generateId(), - logger: genericLogger, - }); + { + authData: getRandomAuthData(anotherTenant.id), + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + } + ); }).rejects.toThrowError(organizationNotAllowed(anotherTenant.id)); }); @@ -391,15 +413,18 @@ describe("createPurposeVersion", () => { await writeInReadmodel(toReadModelTenant(mockProducer), tenants); expect(async () => { - await purposeService.createPurposeVersion({ - purposeId: mockPurpose.id, - seed: { + await purposeService.createPurposeVersion( + mockPurpose.id, + { dailyCalls: 20, }, - organizationId: mockPurpose.consumerId, - correlationId: generateId(), - logger: genericLogger, - }); + { + authData: getRandomAuthData(mockPurpose.consumerId), + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + } + ); }).rejects.toThrowError( agreementNotFound(mockEService.id, mockConsumer.id) ); @@ -424,15 +449,18 @@ describe("createPurposeVersion", () => { await writeInReadmodel(toReadModelTenant(mockProducer), tenants); expect(async () => { - await purposeService.createPurposeVersion({ - purposeId: mockPurpose.id, - seed: { + await purposeService.createPurposeVersion( + mockPurpose.id, + { dailyCalls: 20, }, - organizationId: mockPurpose.consumerId, - correlationId: generateId(), - logger: genericLogger, - }); + { + authData: getRandomAuthData(mockPurpose.consumerId), + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + } + ); }).rejects.toThrowError( agreementNotFound(mockEService.id, mockConsumer.id) ); @@ -447,15 +475,18 @@ describe("createPurposeVersion", () => { await writeInReadmodel(toReadModelTenant(mockProducer), tenants); expect(async () => { - await purposeService.createPurposeVersion({ - purposeId: mockPurpose.id, - seed: { + await purposeService.createPurposeVersion( + mockPurpose.id, + { dailyCalls: 20, }, - organizationId: mockPurpose.consumerId, - correlationId: generateId(), - logger: genericLogger, - }); + { + authData: getRandomAuthData(mockPurpose.consumerId), + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + } + ); }).rejects.toThrowError(tenantNotFound(mockConsumer.id)); }); @@ -467,15 +498,18 @@ describe("createPurposeVersion", () => { // await writeInReadmodel(mockProducer, tenants); expect(async () => { - await purposeService.createPurposeVersion({ - purposeId: mockPurpose.id, - seed: { + await purposeService.createPurposeVersion( + mockPurpose.id, + { dailyCalls: 20, }, - organizationId: mockPurpose.consumerId, - correlationId: generateId(), - logger: genericLogger, - }); + { + authData: getRandomAuthData(mockPurpose.consumerId), + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + } + ); }).rejects.toThrowError(tenantNotFound(mockProducer.id)); }); @@ -493,15 +527,18 @@ describe("createPurposeVersion", () => { await writeInReadmodel(toReadModelTenant(mockProducer), tenants); expect(async () => { - await purposeService.createPurposeVersion({ - purposeId: mockPurpose.id, - seed: { + await purposeService.createPurposeVersion( + mockPurpose.id, + { dailyCalls: 20, }, - organizationId: mockPurpose.consumerId, - correlationId: generateId(), - logger: genericLogger, - }); + { + authData: getRandomAuthData(mockPurpose.consumerId), + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + } + ); }).rejects.toThrowError(tenantKindNotFound(consumer.id)); }); @@ -519,15 +556,18 @@ describe("createPurposeVersion", () => { await writeInReadmodel(toReadModelTenant(producer), tenants); expect(async () => { - await purposeService.createPurposeVersion({ - purposeId: mockPurpose.id, - seed: { + await purposeService.createPurposeVersion( + mockPurpose.id, + { dailyCalls: 20, }, - organizationId: mockPurpose.consumerId, - correlationId: generateId(), - logger: genericLogger, - }); + { + authData: getRandomAuthData(mockPurpose.consumerId), + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + } + ); }).rejects.toThrowError(tenantKindNotFound(producer.id)); }); @@ -544,15 +584,18 @@ describe("createPurposeVersion", () => { await writeInReadmodel(toReadModelTenant(mockProducer), tenants); expect(async () => { - await purposeService.createPurposeVersion({ - purposeId: mockPurpose.id, - seed: { + await purposeService.createPurposeVersion( + mockPurpose.id, + { dailyCalls: 20, }, - organizationId: mockPurpose.consumerId, - correlationId: generateId(), - logger: genericLogger, - }); + { + authData: getRandomAuthData(mockPurpose.consumerId), + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + } + ); }).rejects.toThrowError(missingRiskAnalysis(purpose.id)); }); }); diff --git a/packages/purpose-process/test/createReversePurpose.test.ts b/packages/purpose-process/test/createReversePurpose.test.ts index c017399dcb..028fef9340 100644 --- a/packages/purpose-process/test/createReversePurpose.test.ts +++ b/packages/purpose-process/test/createReversePurpose.test.ts @@ -8,6 +8,7 @@ import { getMockPurpose, getMockTenant, getMockValidRiskAnalysis, + getRandomAuthData, writeInReadmodel, } from "pagopa-interop-commons-test"; import { @@ -105,12 +106,12 @@ describe("createReversePurpose", () => { await writeInReadmodel(toReadModelAgreement(mockAgreement), agreements); const { purpose, isRiskAnalysisValid } = - await purposeService.createReversePurpose( - consumer.id, - reversePurposeSeed, - generateId(), - genericLogger - ); + await purposeService.createReversePurpose(reversePurposeSeed, { + authData: getRandomAuthData(consumer.id), + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + }); const writtenEvent = await readLastPurposeEvent(purpose.id); @@ -200,12 +201,12 @@ describe("createReversePurpose", () => { await writeInReadmodel(toReadModelAgreement(mockAgreement), agreements); expect( - purposeService.createReversePurpose( - producer.id, - reversePurposeSeed, - generateId(), - genericLogger - ) + purposeService.createReversePurpose(reversePurposeSeed, { + authData: getRandomAuthData(producer.id), + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + }) ).rejects.toThrowError(organizationIsNotTheConsumer(producer.id)); }); it("should throw eserviceModeNotAllowed if the eservice is in deliver mode", async () => { @@ -252,12 +253,12 @@ describe("createReversePurpose", () => { await writeInReadmodel(toReadModelAgreement(mockAgreement), agreements); expect( - purposeService.createReversePurpose( - consumer.id, - reversePurposeSeed, - generateId(), - genericLogger - ) + purposeService.createReversePurpose(reversePurposeSeed, { + authData: getRandomAuthData(consumer.id), + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + }) ).rejects.toThrowError( eServiceModeNotAllowed(mockEService.id, eserviceMode.receive) ); @@ -306,12 +307,12 @@ describe("createReversePurpose", () => { await writeInReadmodel(toReadModelAgreement(mockAgreement), agreements); expect( - purposeService.createReversePurpose( - consumer.id, - reversePurposeSeed, - generateId(), - genericLogger - ) + purposeService.createReversePurpose(reversePurposeSeed, { + authData: getRandomAuthData(consumer.id), + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + }) ).rejects.toThrowError( eserviceRiskAnalysisNotFound(mockEService.id, randomRiskAnalysisId) ); @@ -360,12 +361,12 @@ describe("createReversePurpose", () => { await writeInReadmodel(toReadModelAgreement(mockAgreement), agreements); expect( - purposeService.createReversePurpose( - consumer.id, - reversePurposeSeed, - generateId(), - genericLogger - ) + purposeService.createReversePurpose(reversePurposeSeed, { + authData: getRandomAuthData(consumer.id), + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + }) ).rejects.toThrowError(missingFreeOfChargeReason()); }); it("should throw tenantKindNotFound if the tenant kind doesn't exist", async () => { @@ -412,12 +413,12 @@ describe("createReversePurpose", () => { await writeInReadmodel(toReadModelAgreement(mockAgreement), agreements); expect( - purposeService.createReversePurpose( - consumer.id, - reversePurposeSeed, - generateId(), - genericLogger - ) + purposeService.createReversePurpose(reversePurposeSeed, { + authData: getRandomAuthData(consumer.id), + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + }) ).rejects.toThrowError(tenantKindNotFound(producer.id)); }); it("should throw agreementNotFound if the requester doesn't have an agreement for the selected eservice", async () => { @@ -456,12 +457,12 @@ describe("createReversePurpose", () => { await writeInReadmodel(toReadModelTenant(consumer), tenants); expect( - purposeService.createReversePurpose( - consumer.id, - reversePurposeSeed, - generateId(), - genericLogger - ) + purposeService.createReversePurpose(reversePurposeSeed, { + authData: getRandomAuthData(consumer.id), + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + }) ).rejects.toThrowError(agreementNotFound(mockEService.id, consumer.id)); }); it("should throw duplicatedPurposeTitle if a purpose with the same name already exists", async () => { @@ -517,12 +518,12 @@ describe("createReversePurpose", () => { await writeInReadmodel(toReadModelAgreement(mockAgreement), agreements); expect( - purposeService.createReversePurpose( - consumer.id, - reversePurposeSeed, - generateId(), - genericLogger - ) + purposeService.createReversePurpose(reversePurposeSeed, { + authData: getRandomAuthData(consumer.id), + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + }) ).rejects.toThrowError(duplicatedPurposeTitle(purposeTitle)); }); it("should throw riskAnalysisValidationFailed if the risk analysis is not valid", async () => { @@ -577,12 +578,12 @@ describe("createReversePurpose", () => { await writeInReadmodel(toReadModelAgreement(mockAgreement), agreements); expect( - purposeService.createReversePurpose( - consumer.id, - reversePurposeSeed, - generateId(), - genericLogger - ) + purposeService.createReversePurpose(reversePurposeSeed, { + authData: getRandomAuthData(consumer.id), + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + }) ).rejects.toThrowError( riskAnalysisValidationFailed([ unexpectedRulesVersionError(mockRiskAnalysis.riskAnalysisForm.version), diff --git a/packages/purpose-process/test/deletePurpose.test.ts b/packages/purpose-process/test/deletePurpose.test.ts index a0dc5e2dea..09af22f879 100644 --- a/packages/purpose-process/test/deletePurpose.test.ts +++ b/packages/purpose-process/test/deletePurpose.test.ts @@ -16,6 +16,7 @@ import { writeInReadmodel, decodeProtobufPayload, getMockPurposeVersion, + getRandomAuthData, } from "pagopa-interop-commons-test"; import { genericLogger } from "pagopa-interop-commons"; import { @@ -43,11 +44,11 @@ describe("deletePurpose", () => { await addOnePurpose(mockPurpose); await writeInReadmodel(toReadModelEService(mockEService), eservices); - await purposeService.deletePurpose({ - purposeId: mockPurpose.id, - organizationId: mockPurpose.consumerId, + await purposeService.deletePurpose(mockPurpose.id, { + authData: getRandomAuthData(mockPurpose.consumerId), correlationId: generateId(), logger: genericLogger, + serviceName: "", }); const writtenEvent = await readLastPurposeEvent(mockPurpose.id); @@ -78,11 +79,11 @@ describe("deletePurpose", () => { await addOnePurpose(mockPurpose); await writeInReadmodel(toReadModelEService(mockEService), eservices); - await purposeService.deletePurpose({ - purposeId: mockPurpose.id, - organizationId: mockPurpose.consumerId, + await purposeService.deletePurpose(mockPurpose.id, { + authData: getRandomAuthData(mockPurpose.consumerId), correlationId: generateId(), logger: genericLogger, + serviceName: "", }); const writtenEvent = await readLastPurposeEvent(mockPurpose.id); @@ -115,11 +116,11 @@ describe("deletePurpose", () => { await addOnePurpose(mockPurpose); await writeInReadmodel(toReadModelEService(mockEService), eservices); - await purposeService.deletePurpose({ - purposeId: mockPurpose.id, - organizationId: mockPurpose.consumerId, + await purposeService.deletePurpose(mockPurpose.id, { + authData: getRandomAuthData(mockPurpose.consumerId), correlationId: generateId(), logger: genericLogger, + serviceName: "", }); const writtenEvent = await readLastPurposeEvent(mockPurpose.id); @@ -144,11 +145,11 @@ describe("deletePurpose", () => { await addOnePurpose(mockPurpose); expect( - purposeService.deletePurpose({ - purposeId: randomId, - organizationId: mockPurpose.consumerId, + purposeService.deletePurpose(randomId, { + authData: getRandomAuthData(mockPurpose.consumerId), correlationId: generateId(), logger: genericLogger, + serviceName: "", }) ).rejects.toThrowError(purposeNotFound(randomId)); }); @@ -167,11 +168,11 @@ describe("deletePurpose", () => { await writeInReadmodel(toReadModelEService(mockEService), eservices); expect( - purposeService.deletePurpose({ - purposeId: mockPurpose.id, - organizationId: mockEService.producerId, + purposeService.deletePurpose(mockPurpose.id, { + authData: getRandomAuthData(mockEService.producerId), correlationId: generateId(), logger: genericLogger, + serviceName: "", }) ).rejects.toThrowError( organizationIsNotTheConsumer(mockEService.producerId) @@ -199,11 +200,11 @@ describe("deletePurpose", () => { await writeInReadmodel(toReadModelEService(mockEService), eservices); expect( - purposeService.deletePurpose({ - purposeId: mockPurpose.id, - organizationId: mockPurpose.consumerId, + purposeService.deletePurpose(mockPurpose.id, { + authData: getRandomAuthData(mockPurpose.consumerId), correlationId: generateId(), logger: genericLogger, + serviceName: "", }) ).rejects.toThrowError(purposeCannotBeDeleted(mockPurpose.id)); } diff --git a/packages/purpose-process/test/updatePurpose.test.ts b/packages/purpose-process/test/updatePurpose.test.ts index 8de30ed8db..9c0df40116 100644 --- a/packages/purpose-process/test/updatePurpose.test.ts +++ b/packages/purpose-process/test/updatePurpose.test.ts @@ -14,6 +14,7 @@ import { getMockValidRiskAnalysis, writeInReadmodel, decodeProtobufPayload, + getRandomAuthData, } from "pagopa-interop-commons-test/index.js"; import { tenantKind, @@ -136,12 +137,13 @@ describe("updatePurpose and updateReversePurpose", () => { await writeInReadmodel(toReadModelTenant(tenant), tenants); const { purpose, isRiskAnalysisValid } = await purposeService.updatePurpose( + purposeForDeliver.id, + purposeUpdateContent, { - purposeId: purposeForDeliver.id, - purposeUpdateContent, - organizationId: tenant.id, + authData: getRandomAuthData(tenant.id), correlationId: generateId(), logger: genericLogger, + serviceName: "", } ); @@ -182,12 +184,13 @@ describe("updatePurpose and updateReversePurpose", () => { }; const { purpose, isRiskAnalysisValid } = await purposeService.updatePurpose( + purposeForDeliver.id, + updateContentWithoutTitle, { - purposeId: purposeForDeliver.id, - purposeUpdateContent: updateContentWithoutTitle, - organizationId: tenant.id, + authData: getRandomAuthData(tenant.id), correlationId: generateId(), logger: genericLogger, + serviceName: "", } ); @@ -222,13 +225,16 @@ describe("updatePurpose and updateReversePurpose", () => { await writeInReadmodel(toReadModelTenant(tenant), tenants); const { purpose, isRiskAnalysisValid } = - await purposeService.updateReversePurpose({ - purposeId: purposeForReceive.id, + await purposeService.updateReversePurpose( + purposeForReceive.id, reversePurposeUpdateContent, - organizationId: tenant.id, - correlationId: generateId(), - logger: genericLogger, - }); + { + authData: getRandomAuthData(tenant.id), + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + } + ); const writtenEvent = await readLastPurposeEvent(purposeForReceive.id); expect(writtenEvent).toMatchObject({ @@ -261,12 +267,11 @@ describe("updatePurpose and updateReversePurpose", () => { const purposeId: PurposeId = unsafeBrandId(generateId()); expect( - purposeService.updatePurpose({ - purposeId, - purposeUpdateContent, - organizationId: tenant.id, + purposeService.updatePurpose(purposeId, purposeUpdateContent, { + authData: getRandomAuthData(tenant.id), correlationId: generateId(), logger: genericLogger, + serviceName: "", }) ).rejects.toThrowError(purposeNotFound(purposeId)); }); @@ -283,12 +288,11 @@ describe("updatePurpose and updateReversePurpose", () => { const organizationId: TenantId = unsafeBrandId(generateId()); expect( - purposeService.updatePurpose({ - purposeId: mockPurpose.id, - purposeUpdateContent, - organizationId, + purposeService.updatePurpose(mockPurpose.id, purposeUpdateContent, { + authData: getRandomAuthData(organizationId), correlationId: generateId(), logger: genericLogger, + serviceName: "", }) ).rejects.toThrowError(organizationIsNotTheConsumer(organizationId)); }); @@ -309,12 +313,11 @@ describe("updatePurpose and updateReversePurpose", () => { await writeInReadmodel(toReadModelTenant(tenant), tenants); expect( - purposeService.updatePurpose({ - purposeId: mockPurpose.id, - purposeUpdateContent, - organizationId: tenant.id, + purposeService.updatePurpose(mockPurpose.id, purposeUpdateContent, { + authData: getRandomAuthData(tenant.id), correlationId: generateId(), logger: genericLogger, + serviceName: "", }) ).rejects.toThrowError(purposeNotInDraftState(mockPurpose.id)); } @@ -329,16 +332,19 @@ describe("updatePurpose and updateReversePurpose", () => { await addOnePurpose(purposeWithDuplicatedTitle); expect( - purposeService.updatePurpose({ - purposeId: purposeForDeliver.id, - purposeUpdateContent: { + purposeService.updatePurpose( + purposeForDeliver.id, + { ...purposeUpdateContent, title: purposeWithDuplicatedTitle.title, }, - organizationId: tenant.id, - correlationId: generateId(), - logger: genericLogger, - }) + { + authData: getRandomAuthData(tenant.id), + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + } + ) ).rejects.toThrowError( duplicatedPurposeTitle(purposeWithDuplicatedTitle.title) ); @@ -354,12 +360,11 @@ describe("updatePurpose and updateReversePurpose", () => { await writeInReadmodel(toReadModelTenant(tenant), tenants); expect( - purposeService.updatePurpose({ - purposeId: mockPurpose.id, - purposeUpdateContent, - organizationId: tenant.id, + purposeService.updatePurpose(mockPurpose.id, purposeUpdateContent, { + authData: getRandomAuthData(tenant.id), correlationId: generateId(), logger: genericLogger, + serviceName: "", }) ).rejects.toThrowError(eserviceNotFound(eserviceId)); }); @@ -369,12 +374,11 @@ describe("updatePurpose and updateReversePurpose", () => { await writeInReadmodel(toReadModelTenant(tenant), tenants); expect( - purposeService.updatePurpose({ - purposeId: purposeForReceive.id, - purposeUpdateContent, - organizationId: tenant.id, + purposeService.updatePurpose(purposeForReceive.id, purposeUpdateContent, { + authData: getRandomAuthData(tenant.id), correlationId: generateId(), logger: genericLogger, + serviceName: "", }) ).rejects.toThrowError( eServiceModeNotAllowed(eServiceReceive.id, "Deliver") @@ -386,13 +390,16 @@ describe("updatePurpose and updateReversePurpose", () => { await writeInReadmodel(toReadModelTenant(tenant), tenants); expect( - purposeService.updateReversePurpose({ - purposeId: purposeForDeliver.id, + purposeService.updateReversePurpose( + purposeForDeliver.id, reversePurposeUpdateContent, - organizationId: tenant.id, - correlationId: generateId(), - logger: genericLogger, - }) + { + authData: getRandomAuthData(tenant.id), + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + } + ) ).rejects.toThrowError( eServiceModeNotAllowed(eServiceDeliver.id, "Receive") ); @@ -403,16 +410,19 @@ describe("updatePurpose and updateReversePurpose", () => { await writeInReadmodel(toReadModelTenant(tenant), tenants); expect( - purposeService.updatePurpose({ - purposeId: purposeForDeliver.id, - purposeUpdateContent: { + purposeService.updatePurpose( + purposeForDeliver.id, + { ...purposeUpdateContent, isFreeOfCharge: true, }, - organizationId: tenant.id, - correlationId: generateId(), - logger: genericLogger, - }) + { + authData: getRandomAuthData(tenant.id), + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + } + ) ).rejects.toThrowError(missingFreeOfChargeReason()); }); it("Should throw tenantNotFound if the tenant does not exist", async () => { @@ -420,12 +430,11 @@ describe("updatePurpose and updateReversePurpose", () => { await writeInReadmodel(toReadModelEService(eServiceDeliver), eservices); expect( - purposeService.updatePurpose({ - purposeId: purposeForDeliver.id, - purposeUpdateContent, - organizationId: tenant.id, + purposeService.updatePurpose(purposeForDeliver.id, purposeUpdateContent, { + authData: getRandomAuthData(tenant.id), correlationId: generateId(), logger: genericLogger, + serviceName: "", }) ).rejects.toThrowError(tenantNotFound(tenant.id)); @@ -433,13 +442,16 @@ describe("updatePurpose and updateReversePurpose", () => { await writeInReadmodel(toReadModelEService(eServiceReceive), eservices); expect( - purposeService.updateReversePurpose({ - purposeId: purposeForReceive.id, + purposeService.updateReversePurpose( + purposeForReceive.id, reversePurposeUpdateContent, - organizationId: tenant.id, - correlationId: generateId(), - logger: genericLogger, - }) + { + authData: getRandomAuthData(tenant.id), + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + } + ) ).rejects.toThrowError(tenantNotFound(tenant.id)); }); it("Should throw tenantKindNotFound if the tenant kind does not exist", async () => { @@ -453,12 +465,11 @@ describe("updatePurpose and updateReversePurpose", () => { await writeInReadmodel(toReadModelTenant(mockTenant), tenants); expect( - purposeService.updatePurpose({ - purposeId: purposeForDeliver.id, - purposeUpdateContent, - organizationId: mockTenant.id, + purposeService.updatePurpose(purposeForDeliver.id, purposeUpdateContent, { + authData: getRandomAuthData(mockTenant.id), correlationId: generateId(), logger: genericLogger, + serviceName: "", }) ).rejects.toThrowError(tenantKindNotFound(mockTenant.id)); }); @@ -481,13 +492,16 @@ describe("updatePurpose and updateReversePurpose", () => { }; expect( - purposeService.updatePurpose({ - purposeId: purposeForDeliver.id, - purposeUpdateContent: mockPurposeUpdateContent, - organizationId: tenant.id, - correlationId: generateId(), - logger: genericLogger, - }) + purposeService.updatePurpose( + purposeForDeliver.id, + mockPurposeUpdateContent, + { + authData: getRandomAuthData(tenant.id), + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + } + ) ).rejects.toThrowError( riskAnalysisValidationFailed([unexpectedRulesVersionError("0")]) ); @@ -507,13 +521,16 @@ describe("updatePurpose and updateReversePurpose", () => { await writeInReadmodel(toReadModelTenant(tenant), tenants); expect( - purposeService.updateReversePurpose({ - purposeId: purposeWithInvalidRiskAnalysis.id, + purposeService.updateReversePurpose( + purposeWithInvalidRiskAnalysis.id, reversePurposeUpdateContent, - organizationId: tenant.id, - correlationId: generateId(), - logger: genericLogger, - }) + { + authData: getRandomAuthData(tenant.id), + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + } + ) ).rejects.toThrowError( riskAnalysisValidationFailed([unexpectedRulesVersionError("0")]) ); From 2e7859ce00d4bed50f469cdd2ad9a11e7910eae8 Mon Sep 17 00:00:00 2001 From: AsterITA Date: Fri, 20 Dec 2024 09:21:25 +0100 Subject: [PATCH 02/23] fix getDelegation using correct params --- .../src/services/purposeService.ts | 24 +++++++++---------- .../src/services/readModelService.ts | 7 +++--- .../src/services/validators.ts | 2 +- 3 files changed, 16 insertions(+), 17 deletions(-) diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index c2cb6e3d52..2d687100b3 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -443,8 +443,8 @@ export function purposeServiceBuilder( assertRequesterCanActAsConsumer( purpose.data, authData, - await readModelService.getActiveConsumerDelegationByEserviceId( - purpose.data.eserviceId + await readModelService.getActiveConsumerDelegationByPurpose( + purpose.data ) ); @@ -479,8 +479,8 @@ export function purposeServiceBuilder( assertRequesterCanActAsConsumer( purpose.data, authData, - await readModelService.getActiveConsumerDelegationByEserviceId( - purpose.data.eserviceId + await readModelService.getActiveConsumerDelegationByPurpose( + purpose.data ) ); @@ -659,8 +659,8 @@ export function purposeServiceBuilder( assertRequesterCanActAsConsumer( purpose.data, authData, - await readModelService.getActiveConsumerDelegationByEserviceId( - purpose.data.eserviceId + await readModelService.getActiveConsumerDelegationByPurpose( + purpose.data ) ); @@ -1094,9 +1094,10 @@ export function purposeServiceBuilder( assertRequesterCanActAsConsumer( { eserviceId, consumerId }, authData, - await readModelService.getActiveConsumerDelegationByEserviceId( - eserviceId - ) + await readModelService.getActiveConsumerDelegationByPurpose({ + eserviceId, + consumerId, + }) ); const eservice = await retrieveEService(eserviceId, readModelService); @@ -1463,7 +1464,6 @@ const performUpdatePurpose = async ( repository: { createEvent: (createEvent: CreateEvent) => Promise; } - // eslint-disable-next-line max-params ): Promise<{ purpose: Purpose; isRiskAnalysisValid: boolean }> => { const purpose = await retrievePurpose(purposeId, readModelService); assertPurposeIsDraft(purpose.data); @@ -1480,9 +1480,7 @@ const performUpdatePurpose = async ( assertRequesterCanActAsConsumer( purpose.data, authData, - await readModelService.getActiveConsumerDelegationByEserviceId( - purpose.data.eserviceId - ) + await readModelService.getActiveConsumerDelegationByPurpose(purpose.data) ); const eservice = await retrieveEService( diff --git a/packages/purpose-process/src/services/readModelService.ts b/packages/purpose-process/src/services/readModelService.ts index d10c34ccfe..dc27265f5d 100644 --- a/packages/purpose-process/src/services/readModelService.ts +++ b/packages/purpose-process/src/services/readModelService.ts @@ -407,11 +407,12 @@ export function readModelServiceBuilder( "data.kind": delegationKind.delegatedProducer, }); }, - async getActiveConsumerDelegationByEserviceId( - eserviceId: EServiceId + async getActiveConsumerDelegationByPurpose( + purpose: Pick ): Promise { return getDelegation(delegations, { - "data.eserviceId": eserviceId, + "data.eserviceId": purpose.eserviceId, + "data.delegatorId": purpose.consumerId, "data.state": delegationState.active, "data.kind": delegationKind.delegatedConsumer, }); diff --git a/packages/purpose-process/src/services/validators.ts b/packages/purpose-process/src/services/validators.ts index 0721a9e21d..f33d8509fb 100644 --- a/packages/purpose-process/src/services/validators.ts +++ b/packages/purpose-process/src/services/validators.ts @@ -331,7 +331,7 @@ export const assertRequesterCanActAsConsumer = ( const assertRequesterIsDelegateConsumer = ( purpose: Pick, - authData: Pick, + authData: AuthData, activeConsumerDelegation: Delegation | undefined ): void => { if ( From d27dff33172e4464a9f4bd1eb64625de7f9a1414 Mon Sep 17 00:00:00 2001 From: AsterITA Date: Fri, 20 Dec 2024 11:31:25 +0100 Subject: [PATCH 03/23] added check for delegate on activate and suspend, various fixes --- .../src/model/domain/errors.ts | 14 ++++--- .../src/services/purposeService.ts | 16 ++++++++ .../src/services/validators.ts | 19 +++++---- .../src/utilities/errorMappers.ts | 41 +++++++++++++++---- 4 files changed, 69 insertions(+), 21 deletions(-) diff --git a/packages/purpose-process/src/model/domain/errors.ts b/packages/purpose-process/src/model/domain/errors.ts index 17aa09b845..676643bb37 100644 --- a/packages/purpose-process/src/model/domain/errors.ts +++ b/packages/purpose-process/src/model/domain/errors.ts @@ -1,5 +1,6 @@ import { ApiError, + DelegationId, DescriptorId, EServiceId, EServiceMode, @@ -41,7 +42,7 @@ export const errorCodes = { missingRiskAnalysis: "0024", purposeVersionStateConflict: "0025", riskAnalysisConfigLatestVersionNotFound: "0026", - operationNotAllowed: "0027", + operationRestrictedToDelegate: "0027", }; export type ErrorCodes = keyof typeof errorCodes; @@ -308,10 +309,13 @@ export function purposeVersionStateConflict( }); } -export function operationNotAllowed(requesterId: string): ApiError { +export function operationRestrictedToDelegate( + tenantId: TenantId, + delegationId: DelegationId +): ApiError { return new ApiError({ - detail: `Operation not allowed by ${requesterId}`, - code: "operationNotAllowed", - title: "Operation not allowed", + detail: `Tenant ${tenantId} is not a delegate for delegation ${delegationId}`, + code: "operationRestrictedToDelegate", + title: "Operation restricted to delegate", }); } diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index 2d687100b3..9c031bf8da 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -1404,6 +1404,22 @@ const getOrganizationRole = async ({ return ownership.PRODUCER; } + const activeConsumerDelegation = + await readModelService.getActiveConsumerDelegationByPurpose({ + eserviceId, + consumerId, + }); + + if ( + (activeConsumerDelegation && + organizationId === activeConsumerDelegation.delegateId) || + (!activeConsumerDelegation && + producerId !== consumerId && + organizationId === consumerId) + ) { + return ownership.CONSUMER; + } + throw organizationNotAllowed(organizationId); }; diff --git a/packages/purpose-process/src/services/validators.ts b/packages/purpose-process/src/services/validators.ts index f33d8509fb..bd22b2b0c7 100644 --- a/packages/purpose-process/src/services/validators.ts +++ b/packages/purpose-process/src/services/validators.ts @@ -26,7 +26,7 @@ import { duplicatedPurposeTitle, eServiceModeNotAllowed, missingFreeOfChargeReason, - operationNotAllowed, + operationRestrictedToDelegate, organizationIsNotTheConsumer, organizationIsNotTheProducer, organizationNotAllowed, @@ -332,15 +332,18 @@ export const assertRequesterCanActAsConsumer = ( const assertRequesterIsDelegateConsumer = ( purpose: Pick, authData: AuthData, - activeConsumerDelegation: Delegation | undefined + activeConsumerDelegation: Delegation ): void => { if ( - activeConsumerDelegation?.delegateId !== authData.organizationId || - activeConsumerDelegation?.delegatorId !== purpose.consumerId || - activeConsumerDelegation?.eserviceId !== purpose.eserviceId || - activeConsumerDelegation?.kind !== delegationKind.delegatedConsumer || - activeConsumerDelegation?.state !== delegationState.active + activeConsumerDelegation.delegateId !== authData.organizationId || + activeConsumerDelegation.delegatorId !== purpose.consumerId || + activeConsumerDelegation.eserviceId !== purpose.eserviceId || + activeConsumerDelegation.kind !== delegationKind.delegatedConsumer || + activeConsumerDelegation.state !== delegationState.active ) { - throw operationNotAllowed(authData.organizationId); + throw operationRestrictedToDelegate( + authData.organizationId, + activeConsumerDelegation.id + ); } }; diff --git a/packages/purpose-process/src/utilities/errorMappers.ts b/packages/purpose-process/src/utilities/errorMappers.ts index fb3dafb012..c43ab5cb09 100644 --- a/packages/purpose-process/src/utilities/errorMappers.ts +++ b/packages/purpose-process/src/utilities/errorMappers.ts @@ -40,7 +40,11 @@ export const deletePurposeVersionErrorMapper = ( "purposeVersionNotFound", () => HTTP_STATUS_NOT_FOUND ) - .with("organizationIsNotTheConsumer", () => HTTP_STATUS_FORBIDDEN) + .with( + "organizationIsNotTheConsumer", + "operationRestrictedToDelegate", + () => HTTP_STATUS_FORBIDDEN + ) .with("purposeVersionCannotBeDeleted", () => HTTP_STATUS_CONFLICT) .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); @@ -68,6 +72,7 @@ export const updatePurposeErrorMapper = (error: ApiError): number => .with( "organizationIsNotTheConsumer", "purposeNotInDraftState", + "operationRestrictedToDelegate", () => HTTP_STATUS_FORBIDDEN ) .with("purposeNotFound", () => HTTP_STATUS_NOT_FOUND) @@ -79,7 +84,11 @@ export const updateReversePurposeErrorMapper = updatePurposeErrorMapper; export const deletePurposeErrorMapper = (error: ApiError): number => match(error.code) .with("purposeNotFound", () => HTTP_STATUS_NOT_FOUND) - .with("organizationIsNotTheConsumer", () => HTTP_STATUS_FORBIDDEN) + .with( + "organizationIsNotTheConsumer", + "operationRestrictedToDelegate", + () => HTTP_STATUS_FORBIDDEN + ) .with("purposeCannotBeDeleted", () => HTTP_STATUS_CONFLICT) .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); @@ -92,7 +101,11 @@ export const archivePurposeVersionErrorMapper = ( "purposeVersionNotFound", () => HTTP_STATUS_NOT_FOUND ) - .with("organizationIsNotTheConsumer", () => HTTP_STATUS_FORBIDDEN) + .with( + "organizationIsNotTheConsumer", + "operationRestrictedToDelegate", + () => HTTP_STATUS_FORBIDDEN + ) .with("notValidVersionState", () => HTTP_STATUS_BAD_REQUEST) .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); @@ -117,6 +130,7 @@ export const createPurposeVersionErrorMapper = ( .with( "organizationIsNotTheConsumer", "organizationNotAllowed", + "operationRestrictedToDelegate", () => HTTP_STATUS_FORBIDDEN ) .with("purposeVersionStateConflict", () => HTTP_STATUS_CONFLICT) @@ -125,10 +139,17 @@ export const createPurposeVersionErrorMapper = ( export const createPurposeErrorMapper = (error: ApiError): number => match(error.code) - .with("organizationIsNotTheConsumer", () => HTTP_STATUS_FORBIDDEN) - .with("missingFreeOfChargeReason", () => HTTP_STATUS_BAD_REQUEST) - .with("agreementNotFound", () => HTTP_STATUS_BAD_REQUEST) - .with("riskAnalysisValidationFailed", () => HTTP_STATUS_BAD_REQUEST) + .with( + "organizationIsNotTheConsumer", + "operationRestrictedToDelegate", + () => HTTP_STATUS_FORBIDDEN + ) + .with( + "missingFreeOfChargeReason", + "agreementNotFound", + "riskAnalysisValidationFailed", + () => HTTP_STATUS_BAD_REQUEST + ) .with("duplicatedPurposeTitle", () => HTTP_STATUS_CONFLICT) .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); @@ -136,7 +157,11 @@ export const createReversePurposeErrorMapper = ( error: ApiError ): number => match(error.code) - .with("organizationIsNotTheConsumer", () => HTTP_STATUS_FORBIDDEN) + .with( + "organizationIsNotTheConsumer", + "operationRestrictedToDelegate", + () => HTTP_STATUS_FORBIDDEN + ) .with( "eserviceNotFound", "eServiceModeNotAllowed", From 1b7a6822ade2625dd3415b3101f2b8b62d6a19a5 Mon Sep 17 00:00:00 2001 From: AsterITA Date: Fri, 20 Dec 2024 12:23:52 +0100 Subject: [PATCH 04/23] readded eslint on arrow function --- packages/purpose-process/src/services/purposeService.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index 9c031bf8da..fcb9dd3495 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -1480,6 +1480,7 @@ const performUpdatePurpose = async ( repository: { createEvent: (createEvent: CreateEvent) => Promise; } + // eslint-disable-next-line max-params ): Promise<{ purpose: Purpose; isRiskAnalysisValid: boolean }> => { const purpose = await retrievePurpose(purposeId, readModelService); assertPurposeIsDraft(purpose.data); From 9ab6c3c3935c6547ad6cf5c0b555e2be75db9fed Mon Sep 17 00:00:00 2001 From: AsterITA Date: Fri, 20 Dec 2024 17:36:11 +0100 Subject: [PATCH 05/23] removed delegationId from input in createPurpose --- packages/api-clients/open-api/bffApi.yml | 3 -- packages/api-clients/open-api/purposeApi.yml | 3 -- .../src/services/purposeService.ts | 8 +---- .../src/services/purposeService.ts | 34 ++++++++----------- .../src/services/readModelService.ts | 10 ------ 5 files changed, 15 insertions(+), 43 deletions(-) diff --git a/packages/api-clients/open-api/bffApi.yml b/packages/api-clients/open-api/bffApi.yml index 484cff67f2..425d0afd98 100644 --- a/packages/api-clients/open-api/bffApi.yml +++ b/packages/api-clients/open-api/bffApi.yml @@ -15053,9 +15053,6 @@ components: consumerId: type: string format: uuid - delegationId: - type: string - format: uuid riskAnalysisForm: $ref: "#/components/schemas/RiskAnalysisFormSeed" title: diff --git a/packages/api-clients/open-api/purposeApi.yml b/packages/api-clients/open-api/purposeApi.yml index 24e9fba9a5..eef5a3b4e8 100644 --- a/packages/api-clients/open-api/purposeApi.yml +++ b/packages/api-clients/open-api/purposeApi.yml @@ -1114,9 +1114,6 @@ components: consumerId: type: string format: uuid - delegationId: - type: string - format: uuid riskAnalysisForm: $ref: "#/components/schemas/RiskAnalysisFormSeed" title: diff --git a/packages/backend-for-frontend/src/services/purposeService.ts b/packages/backend-for-frontend/src/services/purposeService.ts index 7b5ce329b3..9bc499771e 100644 --- a/packages/backend-for-frontend/src/services/purposeService.ts +++ b/packages/backend-for-frontend/src/services/purposeService.ts @@ -260,13 +260,7 @@ export function purposeServiceBuilder( { logger, headers }: WithLogger ): Promise { logger.info( - `Creating purpose with eService ${createSeed.eserviceId} and consumer ${ - createSeed.consumerId - }${ - createSeed.delegationId - ? ` with delegation ${createSeed.delegationId}` - : "" - }` + `Creating purpose with eService ${createSeed.eserviceId} and consumer ${createSeed.consumerId}` ); const result = await purposeProcessClient.createPurpose(createSeed, { headers, diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index fcb9dd3495..81f7ddd3cf 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -45,7 +45,6 @@ import { RiskAnalysis, CorrelationId, Delegation, - DelegationId, } from "pagopa-interop-models"; import { purposeApi } from "pagopa-interop-api-clients"; import { P, match } from "ts-pattern"; @@ -1012,33 +1011,28 @@ export function purposeServiceBuilder( { authData, correlationId, logger }: WithLogger ): Promise<{ purpose: Purpose; isRiskAnalysisValid: boolean }> { logger.info( - `Creating Purpose for EService ${purposeSeed.eserviceId} and Consumer ${ - purposeSeed.consumerId - }${ - purposeSeed.delegationId - ? ` with delegation ${purposeSeed.delegationId}` - : "" - }` + `Creating Purpose for EService ${purposeSeed.eserviceId} and Consumer ${purposeSeed.consumerId}` ); const eserviceId = unsafeBrandId(purposeSeed.eserviceId); const consumerId = unsafeBrandId(purposeSeed.consumerId); - const delegationId = purposeSeed.delegationId - ? unsafeBrandId(purposeSeed.delegationId) - : undefined; - - assertRequesterCanActAsConsumer( - { eserviceId, consumerId }, - authData, - delegationId - ? await readModelService.getActiveConsumerDelegationById(delegationId) - : undefined - ); assertConsistentFreeOfCharge( purposeSeed.isFreeOfCharge, purposeSeed.freeOfChargeReason ); + const consumerDelegation = + await readModelService.getActiveConsumerDelegationByPurpose({ + eserviceId, + consumerId, + }); + + assertRequesterCanActAsConsumer( + { eserviceId, consumerId }, + authData, + consumerDelegation + ); + const validatedFormSeed = validateAndTransformRiskAnalysis( purposeSeed.riskAnalysisForm, false, @@ -1061,7 +1055,7 @@ export function purposeServiceBuilder( createdAt: new Date(), eserviceId, consumerId, - delegationId, + delegationId: consumerDelegation?.id, versions: [ { id: generateId(), diff --git a/packages/purpose-process/src/services/readModelService.ts b/packages/purpose-process/src/services/readModelService.ts index dc27265f5d..c41e99672b 100644 --- a/packages/purpose-process/src/services/readModelService.ts +++ b/packages/purpose-process/src/services/readModelService.ts @@ -28,7 +28,6 @@ import { Delegation, delegationKind, DelegationReadModel, - DelegationId, } from "pagopa-interop-models"; import { Document, Filter, WithId } from "mongodb"; import { z } from "zod"; @@ -417,15 +416,6 @@ export function readModelServiceBuilder( "data.kind": delegationKind.delegatedConsumer, }); }, - async getActiveConsumerDelegationById( - delegationId: DelegationId - ): Promise { - return getDelegation(delegations, { - "data.id": delegationId, - "data.state": delegationState.active, - "data.kind": delegationKind.delegatedConsumer, - }); - }, }; } From 0ad08ef79f6857e11d5398867ee81b05479f54fd Mon Sep 17 00:00:00 2001 From: AsterITA Date: Tue, 7 Jan 2025 15:34:14 +0100 Subject: [PATCH 06/23] applied suggestions from review --- .../src/model/domain/errors.ts | 13 -- .../src/routers/PurposeRouter.ts | 16 +- .../src/services/purposeService.ts | 69 +++--- .../src/services/validators.ts | 142 +++++++----- .../src/utilities/errorMappers.ts | 13 +- .../test/rejectPurposeVersion.test.ts | 211 +++++++++++------- 6 files changed, 262 insertions(+), 202 deletions(-) diff --git a/packages/purpose-process/src/model/domain/errors.ts b/packages/purpose-process/src/model/domain/errors.ts index 676643bb37..fe5f43c493 100644 --- a/packages/purpose-process/src/model/domain/errors.ts +++ b/packages/purpose-process/src/model/domain/errors.ts @@ -1,6 +1,5 @@ import { ApiError, - DelegationId, DescriptorId, EServiceId, EServiceMode, @@ -42,7 +41,6 @@ export const errorCodes = { missingRiskAnalysis: "0024", purposeVersionStateConflict: "0025", riskAnalysisConfigLatestVersionNotFound: "0026", - operationRestrictedToDelegate: "0027", }; export type ErrorCodes = keyof typeof errorCodes; @@ -308,14 +306,3 @@ export function purposeVersionStateConflict( title: "Purpose version state conflict", }); } - -export function operationRestrictedToDelegate( - tenantId: TenantId, - delegationId: DelegationId -): ApiError { - return new ApiError({ - detail: `Tenant ${tenantId} is not a delegate for delegation ${delegationId}`, - code: "operationRestrictedToDelegate", - title: "Operation restricted to delegate", - }); -} diff --git a/packages/purpose-process/src/routers/PurposeRouter.ts b/packages/purpose-process/src/routers/PurposeRouter.ts index 6800ce50b8..2047f59779 100644 --- a/packages/purpose-process/src/routers/PurposeRouter.ts +++ b/packages/purpose-process/src/routers/PurposeRouter.ts @@ -392,14 +392,14 @@ const purposeRouter = ( async (req, res) => { const ctx = fromAppContext(req.ctx); try { - await purposeService.rejectPurposeVersion({ - purposeId: unsafeBrandId(req.params.purposeId), - versionId: unsafeBrandId(req.params.versionId), - organizationId: req.ctx.authData.organizationId, - rejectionReason: req.body.rejectionReason, - correlationId: req.ctx.correlationId, - logger: ctx.logger, - }); + await purposeService.rejectPurposeVersion( + { + purposeId: unsafeBrandId(req.params.purposeId), + versionId: unsafeBrandId(req.params.versionId), + rejectionReason: req.body.rejectionReason, + }, + ctx + ); return res.status(204).send(); } catch (error) { const errorRes = makeApiProblem( diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index 81f7ddd3cf..0d043fa8fa 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -108,9 +108,9 @@ import { validateRiskAnalysisOrThrow, assertPurposeTitleIsNotDuplicated, isOverQuota, - assertRequesterIsAllowedToRetrieveRiskAnalysisDocument, - assertRequesterIsProducer, assertRequesterCanActAsConsumer, + assertRequesterCanActAsProducer, + assertRequesterIsAllowedToRetrieveRiskAnalysisDocument, } from "./validators.js"; import { riskAnalysisDocumentBuilder } from "./riskAnalysisDocumentBuilder.js"; @@ -280,13 +280,12 @@ export function purposeServiceBuilder( readModelService ); - await assertRequesterIsAllowedToRetrieveRiskAnalysisDocument({ - eserviceId: eservice.id, - organizationId, - producerId: eservice.producerId, - consumerId: purpose.data.consumerId, - readModelService, - }); + await assertRequesterIsAllowedToRetrieveRiskAnalysisDocument( + purpose.data, + eservice, + { organizationId }, + readModelService + ); const version = retrievePurposeVersion(versionId, purpose); @@ -335,21 +334,18 @@ export function purposeServiceBuilder( }); await repository.createEvent(event); }, - async rejectPurposeVersion({ - purposeId, - versionId, - rejectionReason, - organizationId, - correlationId, - logger, - }: { - purposeId: PurposeId; - versionId: PurposeVersionId; - rejectionReason: string; - organizationId: TenantId; - correlationId: CorrelationId; - logger: Logger; - }): Promise { + async rejectPurposeVersion( + { + purposeId, + versionId, + rejectionReason, + }: { + purposeId: PurposeId; + versionId: PurposeVersionId; + rejectionReason: string; + }, + { correlationId, authData, logger }: WithLogger + ): Promise { logger.info(`Rejecting Version ${versionId} in Purpose ${purposeId}`); const purpose = await retrievePurpose(purposeId, readModelService); @@ -358,12 +354,13 @@ export function purposeServiceBuilder( readModelService ); - await assertRequesterIsProducer({ - eserviceId: eservice.id, - organizationId, - producerId: eservice.producerId, - readModelService, - }); + assertRequesterCanActAsProducer( + eservice, + authData, + await readModelService.getActiveProducerDelegationByEserviceId( + purpose.data.eserviceId + ) + ); const purposeVersion = retrievePurposeVersion(versionId, purpose); @@ -1085,13 +1082,16 @@ export function purposeServiceBuilder( const eserviceId: EServiceId = unsafeBrandId(seed.eServiceId); const consumerId: TenantId = unsafeBrandId(seed.consumerId); - assertRequesterCanActAsConsumer( - { eserviceId, consumerId }, - authData, + const consumerDelegation = await readModelService.getActiveConsumerDelegationByPurpose({ eserviceId, consumerId, - }) + }); + + assertRequesterCanActAsConsumer( + { eserviceId, consumerId }, + authData, + consumerDelegation ); const eservice = await retrieveEService(eserviceId, readModelService); @@ -1139,6 +1139,7 @@ export function purposeServiceBuilder( createdAt: new Date(), eserviceId, consumerId, + delegationId: consumerDelegation?.id, description: seed.description, versions: [newVersion], isFreeOfCharge: seed.isFreeOfCharge, diff --git a/packages/purpose-process/src/services/validators.ts b/packages/purpose-process/src/services/validators.ts index bd22b2b0c7..f065169d6b 100644 --- a/packages/purpose-process/src/services/validators.ts +++ b/packages/purpose-process/src/services/validators.ts @@ -26,7 +26,6 @@ import { duplicatedPurposeTitle, eServiceModeNotAllowed, missingFreeOfChargeReason, - operationRestrictedToDelegate, organizationIsNotTheConsumer, organizationIsNotTheProducer, organizationNotAllowed, @@ -85,9 +84,9 @@ export const assertConsistentFreeOfCharge = ( } }; -const assertOrganizationIsAConsumer = ( - purpose: Pick, - authData: AuthData +const assertRequesterIsConsumer = ( + purpose: Pick, + authData: Pick ): void => { if (authData.organizationId !== purpose.consumerId) { throw organizationIsNotTheConsumer(authData.organizationId); @@ -256,59 +255,85 @@ export async function isOverQuota( ); } -export const assertRequesterIsAllowedToRetrieveRiskAnalysisDocument = async ({ - eserviceId, - organizationId, - producerId, - consumerId, - readModelService, -}: { - eserviceId: EServiceId; - organizationId: TenantId; - producerId: TenantId; - consumerId: TenantId; - readModelService: ReadModelService; -}): Promise => { - if (organizationId === producerId || organizationId === consumerId) { - return; +export const assertRequesterIsAllowedToRetrieveRiskAnalysisDocument = async ( + purpose: Purpose, + eservice: EService, + authData: Pick, + readModelService: ReadModelService +): Promise => { + // This operation has a dedicated assertion because it's the only operation that + // can be performed also by the producer/consumer even when active producer/consumer delegations exist + try { + assertRequesterIsConsumer(purpose, authData); + } catch (error) { + try { + assertRequesterIsProducer(eservice, authData); + } catch (error) { + try { + const activeProducerDelegation = + await readModelService.getActiveProducerDelegationByEserviceId( + purpose.eserviceId + ); + assertRequesterIsDelegateProducer( + eservice, + authData, + activeProducerDelegation + ); + } catch (error) { + const activeConsumerDelegation = + await readModelService.getActiveConsumerDelegationByPurpose(purpose); + + assertRequesterIsDelegateConsumer( + purpose, + authData, + activeConsumerDelegation + ); + } + } } +}; - const activeProducerDelegation = - await readModelService.getActiveProducerDelegationByEserviceId(eserviceId); - - if ( - activeProducerDelegation && - organizationId === activeProducerDelegation.delegateId - ) { - return; +const assertRequesterIsProducer = ( + eservice: Pick, + authData: Pick +): void => { + if (authData.organizationId !== eservice.producerId) { + throw organizationIsNotTheProducer(authData.organizationId); } - - throw organizationNotAllowed(organizationId); }; -export const assertRequesterIsProducer = async ({ - eserviceId, - organizationId, - producerId, - readModelService, -}: { - eserviceId: EServiceId; - organizationId: TenantId; - producerId: TenantId; - readModelService: ReadModelService; -}): Promise => { - const activeProducerDelegation = - await readModelService.getActiveProducerDelegationByEserviceId(eserviceId); - +const assertRequesterIsDelegateProducer = ( + eservice: EService, + authData: Pick, + activeProducerDelegation: Delegation | undefined +): void => { if ( - (activeProducerDelegation && - organizationId === activeProducerDelegation.delegateId) || - (!activeProducerDelegation && organizationId === producerId) + activeProducerDelegation?.delegateId !== authData.organizationId || + activeProducerDelegation?.delegatorId !== eservice.producerId || + activeProducerDelegation?.kind !== delegationKind.delegatedProducer || + activeProducerDelegation?.state !== delegationState.active || + activeProducerDelegation?.eserviceId !== eservice.id ) { - return; + throw organizationNotAllowed(authData.organizationId); } +}; - throw organizationIsNotTheProducer(organizationId); +export const assertRequesterCanActAsProducer = ( + eservice: EService, + authData: AuthData, + activeProducerDelegation: Delegation | undefined +): void => { + if (!activeProducerDelegation) { + // No active producer delegation, the requester is authorized only if they are the producer + assertRequesterIsProducer(eservice, authData); + } else { + // Active producer delegation, the requester is authorized only if they are the delegate + assertRequesterIsDelegateProducer( + eservice, + authData, + activeProducerDelegation + ); + } }; export const assertRequesterCanActAsConsumer = ( @@ -318,7 +343,7 @@ export const assertRequesterCanActAsConsumer = ( ): void => { if (!activeConsumerDelegation) { // No active consumer delegation, the requester is authorized only if they are the consumer - assertOrganizationIsAConsumer(purpose, authData); + assertRequesterIsConsumer(purpose, authData); } else { // Active consumer delegation, the requester is authorized only if they are the delegate assertRequesterIsDelegateConsumer( @@ -331,19 +356,16 @@ export const assertRequesterCanActAsConsumer = ( const assertRequesterIsDelegateConsumer = ( purpose: Pick, - authData: AuthData, - activeConsumerDelegation: Delegation + authData: Pick, + activeConsumerDelegation: Delegation | undefined ): void => { if ( - activeConsumerDelegation.delegateId !== authData.organizationId || - activeConsumerDelegation.delegatorId !== purpose.consumerId || - activeConsumerDelegation.eserviceId !== purpose.eserviceId || - activeConsumerDelegation.kind !== delegationKind.delegatedConsumer || - activeConsumerDelegation.state !== delegationState.active + activeConsumerDelegation?.delegateId !== authData.organizationId || + activeConsumerDelegation?.delegatorId !== purpose.consumerId || + activeConsumerDelegation?.eserviceId !== purpose.eserviceId || + activeConsumerDelegation?.kind !== delegationKind.delegatedConsumer || + activeConsumerDelegation?.state !== delegationState.active ) { - throw operationRestrictedToDelegate( - authData.organizationId, - activeConsumerDelegation.id - ); + throw organizationNotAllowed(authData.organizationId); } }; diff --git a/packages/purpose-process/src/utilities/errorMappers.ts b/packages/purpose-process/src/utilities/errorMappers.ts index c43ab5cb09..a3c1b6a831 100644 --- a/packages/purpose-process/src/utilities/errorMappers.ts +++ b/packages/purpose-process/src/utilities/errorMappers.ts @@ -42,7 +42,7 @@ export const deletePurposeVersionErrorMapper = ( ) .with( "organizationIsNotTheConsumer", - "operationRestrictedToDelegate", + "organizationNotAllowed", () => HTTP_STATUS_FORBIDDEN ) .with("purposeVersionCannotBeDeleted", () => HTTP_STATUS_CONFLICT) @@ -72,7 +72,7 @@ export const updatePurposeErrorMapper = (error: ApiError): number => .with( "organizationIsNotTheConsumer", "purposeNotInDraftState", - "operationRestrictedToDelegate", + "organizationNotAllowed", () => HTTP_STATUS_FORBIDDEN ) .with("purposeNotFound", () => HTTP_STATUS_NOT_FOUND) @@ -86,7 +86,7 @@ export const deletePurposeErrorMapper = (error: ApiError): number => .with("purposeNotFound", () => HTTP_STATUS_NOT_FOUND) .with( "organizationIsNotTheConsumer", - "operationRestrictedToDelegate", + "organizationNotAllowed", () => HTTP_STATUS_FORBIDDEN ) .with("purposeCannotBeDeleted", () => HTTP_STATUS_CONFLICT) @@ -103,7 +103,7 @@ export const archivePurposeVersionErrorMapper = ( ) .with( "organizationIsNotTheConsumer", - "operationRestrictedToDelegate", + "organizationNotAllowed", () => HTTP_STATUS_FORBIDDEN ) .with("notValidVersionState", () => HTTP_STATUS_BAD_REQUEST) @@ -130,7 +130,6 @@ export const createPurposeVersionErrorMapper = ( .with( "organizationIsNotTheConsumer", "organizationNotAllowed", - "operationRestrictedToDelegate", () => HTTP_STATUS_FORBIDDEN ) .with("purposeVersionStateConflict", () => HTTP_STATUS_CONFLICT) @@ -141,7 +140,7 @@ export const createPurposeErrorMapper = (error: ApiError): number => match(error.code) .with( "organizationIsNotTheConsumer", - "operationRestrictedToDelegate", + "organizationNotAllowed", () => HTTP_STATUS_FORBIDDEN ) .with( @@ -159,7 +158,7 @@ export const createReversePurposeErrorMapper = ( match(error.code) .with( "organizationIsNotTheConsumer", - "operationRestrictedToDelegate", + "organizationNotAllowed", () => HTTP_STATUS_FORBIDDEN ) .with( diff --git a/packages/purpose-process/test/rejectPurposeVersion.test.ts b/packages/purpose-process/test/rejectPurposeVersion.test.ts index 5312c58641..4fd78e20d2 100644 --- a/packages/purpose-process/test/rejectPurposeVersion.test.ts +++ b/packages/purpose-process/test/rejectPurposeVersion.test.ts @@ -6,6 +6,7 @@ import { decodeProtobufPayload, getMockAuthData, getMockDelegation, + getRandomAuthData, } from "pagopa-interop-commons-test"; import { purposeVersionState, @@ -57,14 +58,19 @@ describe("rejectPurposeVersion", () => { await addOnePurpose(mockPurpose); await writeInReadmodel(toReadModelEService(mockEService), eservices); - await purposeService.rejectPurposeVersion({ - purposeId: mockPurpose.id, - versionId: mockPurposeVersion.id, - rejectionReason: "test", - organizationId: mockEService.producerId, - correlationId: generateId(), - logger: genericLogger, - }); + await purposeService.rejectPurposeVersion( + { + purposeId: mockPurpose.id, + versionId: mockPurposeVersion.id, + rejectionReason: "test", + }, + { + authData: getRandomAuthData(mockEService.producerId), + serviceName: "", + correlationId: generateId(), + logger: genericLogger, + } + ); const writtenEvent = await readLastPurposeEvent(mockPurpose.id); @@ -124,14 +130,19 @@ describe("rejectPurposeVersion", () => { await writeInReadmodel(delegation, delegations); - await purposeService.rejectPurposeVersion({ - purposeId: mockPurpose.id, - versionId: mockPurposeVersion.id, - rejectionReason: "test", - organizationId: delegate.organizationId, - correlationId: generateId(), - logger: genericLogger, - }); + await purposeService.rejectPurposeVersion( + { + purposeId: mockPurpose.id, + versionId: mockPurposeVersion.id, + rejectionReason: "test", + }, + { + authData: getRandomAuthData(delegate.organizationId), + serviceName: "", + correlationId: generateId(), + logger: genericLogger, + } + ); const writtenEvent = await readLastPurposeEvent(mockPurpose.id); @@ -177,14 +188,19 @@ describe("rejectPurposeVersion", () => { await writeInReadmodel(toReadModelEService(mockEService), eservices); expect( - purposeService.rejectPurposeVersion({ - purposeId: randomId, - versionId: mockPurposeVersion.id, - rejectionReason: "test", - organizationId: mockEService.producerId, - correlationId: generateId(), - logger: genericLogger, - }) + purposeService.rejectPurposeVersion( + { + purposeId: randomId, + versionId: mockPurposeVersion.id, + rejectionReason: "test", + }, + { + authData: getRandomAuthData(mockEService.producerId), + serviceName: "", + correlationId: generateId(), + logger: genericLogger, + } + ) ).rejects.toThrowError(purposeNotFound(randomId)); }); it("Should throw eserviceNotFound if the eservice doesn't exist", async () => { @@ -199,14 +215,19 @@ describe("rejectPurposeVersion", () => { await addOnePurpose(mockPurpose); expect( - purposeService.rejectPurposeVersion({ - purposeId: mockPurpose.id, - versionId: mockPurposeVersion.id, - rejectionReason: "test", - organizationId: mockPurpose.consumerId, - correlationId: generateId(), - logger: genericLogger, - }) + purposeService.rejectPurposeVersion( + { + purposeId: mockPurpose.id, + versionId: mockPurposeVersion.id, + rejectionReason: "test", + }, + { + authData: getRandomAuthData(mockPurpose.consumerId), + serviceName: "", + correlationId: generateId(), + logger: genericLogger, + } + ) ).rejects.toThrowError(eserviceNotFound(mockEService.id)); }); it("should throw organizationIsNotTheProducer if the requester is not the producer nor delegate", async () => { @@ -222,14 +243,19 @@ describe("rejectPurposeVersion", () => { await writeInReadmodel(toReadModelEService(mockEService), eservices); expect( - purposeService.rejectPurposeVersion({ - purposeId: mockPurpose.id, - versionId: mockPurposeVersion.id, - rejectionReason: "test", - organizationId: mockPurpose.consumerId, - correlationId: generateId(), - logger: genericLogger, - }) + purposeService.rejectPurposeVersion( + { + purposeId: mockPurpose.id, + versionId: mockPurposeVersion.id, + rejectionReason: "test", + }, + { + authData: getRandomAuthData(mockPurpose.consumerId), + serviceName: "", + correlationId: generateId(), + logger: genericLogger, + } + ) ).rejects.toThrowError( organizationIsNotTheProducer(mockPurpose.consumerId) ); @@ -257,14 +283,19 @@ describe("rejectPurposeVersion", () => { await writeInReadmodel(delegation, delegations); expect( - purposeService.rejectPurposeVersion({ - purposeId: mockPurpose.id, - versionId: mockPurposeVersion.id, - rejectionReason: "test", - organizationId: mockEService.producerId, - correlationId: generateId(), - logger: genericLogger, - }) + purposeService.rejectPurposeVersion( + { + purposeId: mockPurpose.id, + versionId: mockPurposeVersion.id, + rejectionReason: "test", + }, + { + authData: getRandomAuthData(mockEService.producerId), + serviceName: "", + correlationId: generateId(), + logger: genericLogger, + } + ) ).rejects.toThrowError( organizationIsNotTheProducer(mockEService.producerId) ); @@ -294,14 +325,19 @@ describe("rejectPurposeVersion", () => { const randomCaller = getMockAuthData(); expect( - purposeService.rejectPurposeVersion({ - purposeId: mockPurpose.id, - versionId: mockPurposeVersion.id, - rejectionReason: "test", - organizationId: randomCaller.organizationId, - correlationId: generateId(), - logger: genericLogger, - }) + purposeService.rejectPurposeVersion( + { + purposeId: mockPurpose.id, + versionId: mockPurposeVersion.id, + rejectionReason: "test", + }, + { + authData: getRandomAuthData(randomCaller.organizationId), + serviceName: "", + correlationId: generateId(), + logger: genericLogger, + } + ) ).rejects.toThrowError( organizationIsNotTheProducer(randomCaller.organizationId) ); @@ -333,14 +369,19 @@ describe("rejectPurposeVersion", () => { await writeInReadmodel(delegation, delegations); expect( - purposeService.rejectPurposeVersion({ - purposeId: mockPurpose.id, - versionId: mockPurposeVersion.id, - rejectionReason: "test", - organizationId: delegate.organizationId, - correlationId: generateId(), - logger: genericLogger, - }) + purposeService.rejectPurposeVersion( + { + purposeId: mockPurpose.id, + versionId: mockPurposeVersion.id, + rejectionReason: "test", + }, + { + authData: getRandomAuthData(delegate.organizationId), + serviceName: "", + correlationId: generateId(), + logger: genericLogger, + } + ) ).rejects.toThrowError( organizationIsNotTheProducer(delegate.organizationId) ); @@ -360,14 +401,19 @@ describe("rejectPurposeVersion", () => { await writeInReadmodel(toReadModelEService(mockEService), eservices); expect( - purposeService.rejectPurposeVersion({ - purposeId: mockPurpose.id, - versionId: randomVersionId, - rejectionReason: "test", - organizationId: mockEService.producerId, - correlationId: generateId(), - logger: genericLogger, - }) + purposeService.rejectPurposeVersion( + { + purposeId: mockPurpose.id, + versionId: randomVersionId, + rejectionReason: "test", + }, + { + authData: getRandomAuthData(mockEService.producerId), + serviceName: "", + correlationId: generateId(), + logger: genericLogger, + } + ) ).rejects.toThrowError( purposeVersionNotFound(mockPurpose.id, randomVersionId) ); @@ -392,14 +438,19 @@ describe("rejectPurposeVersion", () => { await writeInReadmodel(toReadModelEService(mockEService), eservices); expect( - purposeService.rejectPurposeVersion({ - purposeId: mockPurpose.id, - versionId: mockPurposeVersion.id, - rejectionReason: "test", - organizationId: mockEService.producerId, - correlationId: generateId(), - logger: genericLogger, - }) + purposeService.rejectPurposeVersion( + { + purposeId: mockPurpose.id, + versionId: mockPurposeVersion.id, + rejectionReason: "test", + }, + { + authData: getRandomAuthData(mockEService.producerId), + serviceName: "", + correlationId: generateId(), + logger: genericLogger, + } + ) ).rejects.toThrowError( notValidVersionState(mockPurposeVersion.id, mockPurposeVersion.state) ); From 3befcdcab4c2a0127edca2a0262d6d7b1b6d4887 Mon Sep 17 00:00:00 2001 From: AsterITA Date: Tue, 7 Jan 2025 16:13:25 +0100 Subject: [PATCH 07/23] fix tests --- packages/purpose-process/test/getRiskAnalysisDocument.test.ts | 1 + packages/purpose-process/test/rejectPurposeVersion.test.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/packages/purpose-process/test/getRiskAnalysisDocument.test.ts b/packages/purpose-process/test/getRiskAnalysisDocument.test.ts index 49c94cc3ed..b28ca6cca2 100644 --- a/packages/purpose-process/test/getRiskAnalysisDocument.test.ts +++ b/packages/purpose-process/test/getRiskAnalysisDocument.test.ts @@ -105,6 +105,7 @@ describe("getRiskAnalysisDocument", () => { eserviceId: mockEService.id, delegateId: delegate.organizationId, state: delegationState.active, + delegatorId: mockEService.producerId, }); await writeInReadmodel(delegation, delegations); diff --git a/packages/purpose-process/test/rejectPurposeVersion.test.ts b/packages/purpose-process/test/rejectPurposeVersion.test.ts index 4fd78e20d2..4db5380b4e 100644 --- a/packages/purpose-process/test/rejectPurposeVersion.test.ts +++ b/packages/purpose-process/test/rejectPurposeVersion.test.ts @@ -126,6 +126,7 @@ describe("rejectPurposeVersion", () => { eserviceId: mockEService.id, delegateId: delegate.organizationId, state: delegationState.active, + delegatorId: mockEService.producerId, }); await writeInReadmodel(delegation, delegations); From e18bfd6575b34d0015eda4c6c124be6d875360f9 Mon Sep 17 00:00:00 2001 From: Roberto Gregnanin <128478646+rGregnanin@users.noreply.github.com> Date: Tue, 7 Jan 2025 16:26:36 +0100 Subject: [PATCH 08/23] added missing tests --- .../test/updateAgreement.test.ts | 4 +- packages/agreement-process/test/utils.ts | 25 --- .../agreement-readmodel-writer/package.json | 1 - packages/commons-test/src/testUtils.ts | 27 +++ .../test/updatePurpose.test.ts | 179 +++++++++++++++++- 5 files changed, 207 insertions(+), 29 deletions(-) diff --git a/packages/agreement-process/test/updateAgreement.test.ts b/packages/agreement-process/test/updateAgreement.test.ts index 46ea8dad38..e4a348810b 100644 --- a/packages/agreement-process/test/updateAgreement.test.ts +++ b/packages/agreement-process/test/updateAgreement.test.ts @@ -5,6 +5,7 @@ import { decodeProtobufPayload, randomArrayItem, getMockDelegation, + addSomeRandomDelegations, } from "pagopa-interop-commons-test"; import { AgreementId, @@ -27,7 +28,6 @@ import { agreementUpdatableStates } from "../src/model/domain/agreement-validato import { addOneAgreement, addOneDelegation, - addSomeRandomDelegations, agreementService, readLastAgreementEvent, } from "./utils.js"; @@ -153,7 +153,7 @@ describe("update agreement", () => { }); await addOneAgreement(agreement); await addOneDelegation(delegation); - await addSomeRandomDelegations(agreement); + await addSomeRandomDelegations(agreement, addOneDelegation); const returnedAgreement = await agreementService.updateAgreement( agreement.id, diff --git a/packages/agreement-process/test/utils.ts b/packages/agreement-process/test/utils.ts index f78be677b9..fc3a55c8c4 100644 --- a/packages/agreement-process/test/utils.ts +++ b/packages/agreement-process/test/utils.ts @@ -10,7 +10,6 @@ import { ReadEvent, readEventByStreamIdAndVersion, randomArrayItem, - getMockDelegation, } from "pagopa-interop-commons-test"; import { afterAll, afterEach, expect, inject, vi } from "vitest"; import { @@ -32,8 +31,6 @@ import { Delegation, AgreementStamp, UserId, - delegationState, - delegationKind, } from "pagopa-interop-models"; import { agreementApi } from "pagopa-interop-api-clients"; import { @@ -268,25 +265,3 @@ export const getRandomPastStamp = (userId: UserId): AgreementStamp => ({ who: userId, when: subDays(new Date(), randomInt(10)), }); - -export const addSomeRandomDelegations = async ( - agreement: Agreement -): Promise => { - const states = [delegationState.rejected, delegationState.revoked]; - const kinds = [ - delegationKind.delegatedProducer, - delegationKind.delegatedConsumer, - ]; - - for (const state of states) { - for (const kind of kinds) { - await addOneDelegation( - getMockDelegation({ - eserviceId: agreement.eserviceId, - kind, - state, - }) - ); - } - } -}; diff --git a/packages/agreement-readmodel-writer/package.json b/packages/agreement-readmodel-writer/package.json index 9f5817d3ac..176cfb433a 100644 --- a/packages/agreement-readmodel-writer/package.json +++ b/packages/agreement-readmodel-writer/package.json @@ -20,7 +20,6 @@ "author": "", "license": "Apache-2.0", "devDependencies": { - "@anatine/zod-mock": "3.13.4", "@faker-js/faker": "8.4.1", "@pagopa/eslint-config": "3.0.0", "@types/node": "20.14.6", diff --git a/packages/commons-test/src/testUtils.ts b/packages/commons-test/src/testUtils.ts index 8c4f5d9d19..4a0a30d32f 100644 --- a/packages/commons-test/src/testUtils.ts +++ b/packages/commons-test/src/testUtils.ts @@ -72,6 +72,8 @@ import { DelegationKind, unsafeBrandId, UserId, + delegationState, + delegationKind, } from "pagopa-interop-models"; import { AuthData, dateToSeconds } from "pagopa-interop-commons"; import { z } from "zod"; @@ -666,3 +668,28 @@ const signClientAssertion = async ({ .setProtectedHeader(headers) .sign(privateKey); }; + +export const addSomeRandomDelegations = async < + T extends { eserviceId: EServiceId } +>( + domainObject: T, + addOneDelegation: (delegation: Delegation) => Promise +): Promise => { + const states = [delegationState.rejected, delegationState.revoked]; + const kinds = [ + delegationKind.delegatedProducer, + delegationKind.delegatedConsumer, + ]; + + for (const state of states) { + for (const kind of kinds) { + await addOneDelegation( + getMockDelegation({ + eserviceId: domainObject.eserviceId, + kind, + state, + }) + ); + } + } +}; diff --git a/packages/purpose-process/test/updatePurpose.test.ts b/packages/purpose-process/test/updatePurpose.test.ts index 9c0df40116..2bc8c37ad5 100644 --- a/packages/purpose-process/test/updatePurpose.test.ts +++ b/packages/purpose-process/test/updatePurpose.test.ts @@ -15,6 +15,8 @@ import { writeInReadmodel, decodeProtobufPayload, getRandomAuthData, + getMockDelegation, + addSomeRandomDelegations, } from "pagopa-interop-commons-test/index.js"; import { tenantKind, @@ -33,6 +35,8 @@ import { RiskAnalysis, eserviceMode, toReadModelTenant, + delegationKind, + delegationState, } from "pagopa-interop-models"; import { purposeApi } from "pagopa-interop-api-clients"; import { describe, it, expect, vi, beforeAll, afterAll } from "vitest"; @@ -47,6 +51,7 @@ import { tenantKindNotFound, riskAnalysisValidationFailed, duplicatedPurposeTitle, + operationRestrictedToDelegate, } from "../src/model/domain/errors.js"; import { getMockEService, @@ -57,6 +62,7 @@ import { eservices, purposeService, tenants, + addOneDelegation, } from "./utils.js"; describe("updatePurpose and updateReversePurpose", () => { @@ -172,7 +178,6 @@ describe("updatePurpose and updateReversePurpose", () => { expect(writtenPayload.purpose).toEqual(toPurposeV2(purpose)); expect(isRiskAnalysisValid).toBe(true); }); - it("Should write on event store for the update of a purpose of an e-service in mode DELIVER (no title change)", async () => { await addOnePurpose(purposeForDeliver); await writeInReadmodel(toReadModelEService(eServiceDeliver), eservices); @@ -260,6 +265,117 @@ describe("updatePurpose and updateReversePurpose", () => { expect(writtenPayload.purpose).toEqual(toPurposeV2(purpose)); expect(isRiskAnalysisValid).toBe(true); }); + it("should succeed when requester is Consumer Delegate and the Purpose is in a updatable state and the e-service is in mode DELIVER", async () => { + const authData = getRandomAuthData(); + + const delegation = getMockDelegation({ + kind: delegationKind.delegatedConsumer, + eserviceId: purposeForDeliver.eserviceId, + delegatorId: purposeForDeliver.consumerId, + delegateId: authData.organizationId, + state: delegationState.active, + }); + + await addOnePurpose(purposeForDeliver); + await addOneDelegation(delegation); + await addSomeRandomDelegations(purposeForDeliver, addOneDelegation); + await writeInReadmodel(toReadModelEService(eServiceDeliver), eservices); + await writeInReadmodel(toReadModelTenant(tenant), tenants); + + const updateContentWithoutTitle = { + ...purposeUpdateContent, + title: purposeForDeliver.title, + }; + + const { purpose, isRiskAnalysisValid } = await purposeService.updatePurpose( + purposeForDeliver.id, + updateContentWithoutTitle, + { + authData, + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + } + ); + + const writtenEvent = await readLastPurposeEvent(purposeForDeliver.id); + + expect(writtenEvent).toMatchObject({ + stream_id: purposeForDeliver.id, + version: "1", + type: "DraftPurposeUpdated", + event_version: 2, + }); + + const writtenPayload = decodeProtobufPayload({ + messageType: DraftPurposeUpdatedV2, + payload: writtenEvent.data, + }); + + const expectedPurpose: Purpose = createUpdatedPurpose( + purposeForDeliver, + updateContentWithoutTitle, + validRiskAnalysis, + writtenPayload.purpose!.riskAnalysisForm! + ); + + expect(writtenPayload.purpose).toEqual(toPurposeV2(expectedPurpose)); + expect(writtenPayload.purpose).toEqual(toPurposeV2(purpose)); + expect(isRiskAnalysisValid).toBe(true); + }); + it("should succeed when requester is Consumer Delegate and the Purpose is in a updatable state and the e-service is in mode RECEIVE", async () => { + const authData = getRandomAuthData(); + + const delegation = getMockDelegation({ + kind: delegationKind.delegatedConsumer, + eserviceId: purposeForReceive.eserviceId, + delegatorId: purposeForReceive.consumerId, + delegateId: authData.organizationId, + state: delegationState.active, + }); + + await addOnePurpose(purposeForReceive); + await addOneDelegation(delegation); + await addSomeRandomDelegations(purposeForDeliver, addOneDelegation); + await writeInReadmodel(toReadModelEService(eServiceReceive), eservices); + await writeInReadmodel(toReadModelTenant(tenant), tenants); + + const { purpose, isRiskAnalysisValid } = + await purposeService.updateReversePurpose( + purposeForReceive.id, + reversePurposeUpdateContent, + { + authData, + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + } + ); + + const writtenEvent = await readLastPurposeEvent(purposeForReceive.id); + expect(writtenEvent).toMatchObject({ + stream_id: purposeForReceive.id, + version: "1", + type: "DraftPurposeUpdated", + event_version: 2, + }); + + const writtenPayload = decodeProtobufPayload({ + messageType: DraftPurposeUpdatedV2, + payload: writtenEvent.data, + }); + + const expectedPurpose: Purpose = createUpdatedPurpose( + purposeForReceive, + reversePurposeUpdateContent, + validRiskAnalysis, + writtenPayload.purpose!.riskAnalysisForm! + ); + + expect(writtenPayload.purpose).toEqual(toPurposeV2(expectedPurpose)); + expect(writtenPayload.purpose).toEqual(toPurposeV2(purpose)); + expect(isRiskAnalysisValid).toBe(true); + }); it("Should throw purposeNotFound if the purpose doesn't exist", async () => { await writeInReadmodel(toReadModelEService(eServiceDeliver), eservices); await writeInReadmodel(toReadModelTenant(tenant), tenants); @@ -535,4 +651,65 @@ describe("updatePurpose and updateReversePurpose", () => { riskAnalysisValidationFailed([unexpectedRulesVersionError("0")]) ); }); + it("should throw operationRestrictedToDelegate when the requester is the Consumer but there is a Consumer Delegation in updatePurpose", async () => { + const authData = getRandomAuthData(); + const purpose = { + ...purposeForDeliver, + consumerId: authData.organizationId, + }; + const delegation = getMockDelegation({ + kind: delegationKind.delegatedConsumer, + eserviceId: purpose.eserviceId, + delegatorId: purpose.consumerId, + delegateId: generateId(), + state: delegationState.active, + }); + await addOnePurpose(purpose); + await addOneDelegation(delegation); + await writeInReadmodel(toReadModelEService(eServiceDeliver), eservices); + await writeInReadmodel(toReadModelTenant(tenant), tenants); + expect( + purposeService.updatePurpose(purposeForDeliver.id, purposeUpdateContent, { + authData, + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + }) + ).rejects.toThrowError( + operationRestrictedToDelegate(authData.organizationId, delegation.id) + ); + }); + it("should throw operationRestrictedToDelegate when the requester is the Consumer but there is a Consumer Delegation in updateReversePurpose", async () => { + const authData = getRandomAuthData(); + const purpose = { + ...purposeForDeliver, + consumerId: authData.organizationId, + }; + const delegation = getMockDelegation({ + kind: delegationKind.delegatedConsumer, + eserviceId: purpose.eserviceId, + delegatorId: purpose.consumerId, + delegateId: generateId(), + state: delegationState.active, + }); + await addOnePurpose(purpose); + await addOneDelegation(delegation); + await writeInReadmodel(toReadModelEService(eServiceDeliver), eservices); + await writeInReadmodel(toReadModelTenant(tenant), tenants); + + expect( + purposeService.updateReversePurpose( + purposeForDeliver.id, + reversePurposeUpdateContent, + { + authData, + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + } + ) + ).rejects.toThrowError( + operationRestrictedToDelegate(authData.organizationId, delegation.id) + ); + }); }); From e241b4897b73402dc02be040bb7ce084d74ab2a6 Mon Sep 17 00:00:00 2001 From: AsterITA Date: Tue, 7 Jan 2025 16:46:26 +0100 Subject: [PATCH 09/23] minor refactor on getOrganizationRole --- packages/purpose-process/src/services/purposeService.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index 0d043fa8fa..c920d7c0ee 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -1392,8 +1392,7 @@ const getOrganizationRole = async ({ await readModelService.getActiveProducerDelegationByEserviceId(eserviceId); if ( - (activeProducerDelegation && - organizationId === activeProducerDelegation.delegateId) || + activeProducerDelegation?.delegateId === organizationId || (!activeProducerDelegation && organizationId === producerId) ) { return ownership.PRODUCER; @@ -1406,8 +1405,7 @@ const getOrganizationRole = async ({ }); if ( - (activeConsumerDelegation && - organizationId === activeConsumerDelegation.delegateId) || + activeConsumerDelegation?.delegateId === organizationId || (!activeConsumerDelegation && producerId !== consumerId && organizationId === consumerId) From 0716349aab8d15e781a7c23a0d315ecc117c9509 Mon Sep 17 00:00:00 2001 From: Roberto Gregnanin <128478646+rGregnanin@users.noreply.github.com> Date: Tue, 7 Jan 2025 16:54:40 +0100 Subject: [PATCH 10/23] fix tests --- .../test/activateAgreement.test.ts | 24 +++++++++---------- .../test/agreementConsumerDocuments.test.ts | 12 +++++----- .../test/archiveAgreement.test.ts | 4 ++-- .../test/cloneAgreement.test.ts | 6 ++--- .../test/deleteAgreement.test.ts | 4 ++-- .../test/submitAgreement.test.ts | 14 +++++------ .../test/upgradeAgreement.test.ts | 8 +++---- .../test/updatePurpose.test.ts | 14 ++++------- 8 files changed, 41 insertions(+), 45 deletions(-) diff --git a/packages/agreement-process/test/activateAgreement.test.ts b/packages/agreement-process/test/activateAgreement.test.ts index d558003711..dd4675ef04 100644 --- a/packages/agreement-process/test/activateAgreement.test.ts +++ b/packages/agreement-process/test/activateAgreement.test.ts @@ -11,6 +11,7 @@ import { timeAtRomeZone, } from "pagopa-interop-commons"; import { + addSomeRandomDelegations, decodeProtobufPayload, getMockAgreement, getMockAgreementAttribute, @@ -83,7 +84,6 @@ import { addOneDelegation, addOneEService, addOneTenant, - addSomeRandomDelegations, agreementService, authDataAndDelegationsFromRequesterIs, fileManager, @@ -309,7 +309,7 @@ describe("activate agreement", () => { await addOneAttribute(verifiedAttribute); const relatedAgreements = await addRelatedAgreements(agreement); - await addSomeRandomDelegations(agreement); + await addSomeRandomDelegations(agreement, addOneDelegation); await addDelegationsAndDelegates({ producerDelegation, delegateProducer, @@ -719,7 +719,7 @@ describe("activate agreement", () => { await addOneAgreement(agreement); await addOneDelegation(consumerDelegation); - await addSomeRandomDelegations(agreement); + await addSomeRandomDelegations(agreement, addOneDelegation); await expect( agreementService.activateAgreement(agreement.id, { @@ -750,7 +750,7 @@ describe("activate agreement", () => { await addOneAgreement(agreement); await addOneDelegation(producerDelegation); - await addSomeRandomDelegations(agreement); + await addSomeRandomDelegations(agreement, addOneDelegation); await expect( agreementService.activateAgreement(agreement.id, { @@ -881,7 +881,7 @@ describe("activate agreement", () => { await addOneAgreement(agreement); const relatedAgreements = await addRelatedAgreements(agreement); - await addSomeRandomDelegations(agreement); + await addSomeRandomDelegations(agreement, addOneDelegation); await addDelegationsAndDelegates({ producerDelegation, delegateProducer, @@ -1237,7 +1237,7 @@ describe("activate agreement", () => { await addOneAgreement(agreement); const relatedAgreements = await addRelatedAgreements(agreement); - await addSomeRandomDelegations(agreement); + await addSomeRandomDelegations(agreement, addOneDelegation); await addDelegationsAndDelegates({ producerDelegation, delegateProducer, @@ -1299,7 +1299,7 @@ describe("activate agreement", () => { await addOneAgreement(agreement); const relatedAgreements = await addRelatedAgreements(agreement); - await addSomeRandomDelegations(agreement); + await addSomeRandomDelegations(agreement, addOneDelegation); await addDelegationsAndDelegates({ producerDelegation, delegateProducer, @@ -1538,7 +1538,7 @@ describe("activate agreement", () => { await addOneAgreement(agreement); const relatedAgreements = await addRelatedAgreements(agreement); - await addSomeRandomDelegations(agreement); + await addSomeRandomDelegations(agreement, addOneDelegation); await addDelegationsAndDelegates({ producerDelegation, delegateProducer, @@ -1593,7 +1593,7 @@ describe("activate agreement", () => { await addOneAgreement(agreement); const relatedAgreements = await addRelatedAgreements(agreement); - await addSomeRandomDelegations(agreement); + await addSomeRandomDelegations(agreement, addOneDelegation); await addDelegationsAndDelegates({ producerDelegation, delegateProducer, @@ -1673,7 +1673,7 @@ describe("activate agreement", () => { await addOneAgreement(agreement); await addOneDelegation(producerDelegation); - await addSomeRandomDelegations(agreement); + await addSomeRandomDelegations(agreement, addOneDelegation); await expect( agreementService.activateAgreement(agreement.id, { @@ -1704,7 +1704,7 @@ describe("activate agreement", () => { await addOneAgreement(agreement); await addOneDelegation(consumerDelegation); - await addSomeRandomDelegations(agreement); + await addSomeRandomDelegations(agreement, addOneDelegation); await expect( agreementService.activateAgreement(agreement.id, { @@ -1758,7 +1758,7 @@ describe("activate agreement", () => { await addOneAgreement(agreement); await addOneDelegation(producerDelegation); await addOneDelegation(consumerDelegation); - await addSomeRandomDelegations(agreement); + await addSomeRandomDelegations(agreement, addOneDelegation); await expect( agreementService.activateAgreement(agreement.id, { diff --git a/packages/agreement-process/test/agreementConsumerDocuments.test.ts b/packages/agreement-process/test/agreementConsumerDocuments.test.ts index 8d33685115..81768b2c18 100644 --- a/packages/agreement-process/test/agreementConsumerDocuments.test.ts +++ b/packages/agreement-process/test/agreementConsumerDocuments.test.ts @@ -2,6 +2,7 @@ import { generateMock } from "@anatine/zod-mock"; import { fileManagerDeleteError, genericLogger } from "pagopa-interop-commons"; import { + addSomeRandomDelegations, decodeProtobufPayload, getMockAgreement, getMockDelegation, @@ -42,7 +43,6 @@ import { addOneDelegation, addOneEService, addOneTenant, - addSomeRandomDelegations, agreementService, fileManager, getMockConsumerDocument, @@ -126,7 +126,7 @@ describe("agreement consumer document", () => { await addOneEService(eservice); await addOneAgreement(agreement); await addOneDelegation(delegation); - await addSomeRandomDelegations(agreement); + await addSomeRandomDelegations(agreement, addOneDelegation); const result = await agreementService.getAgreementConsumerDocument( agreement.id, @@ -167,7 +167,7 @@ describe("agreement consumer document", () => { await addOneAgreement(agreement); await addOneDelegation(delegation); - await addSomeRandomDelegations(agreement); + await addSomeRandomDelegations(agreement, addOneDelegation); for (const document of consumerDocuments) { const result = await agreementService.getAgreementConsumerDocument( @@ -429,7 +429,7 @@ describe("agreement consumer document", () => { state: delegationState.active, }); await addOneDelegation(delegation); - await addSomeRandomDelegations(agreement); + await addSomeRandomDelegations(agreement, addOneDelegation); const returnedConsumerDocument = await agreementService.addConsumerDocument( @@ -543,7 +543,7 @@ describe("agreement consumer document", () => { state: delegationState.active, }); await addOneDelegation(delegation); - await addSomeRandomDelegations(agreement); + await addSomeRandomDelegations(agreement, addOneDelegation); const actualConsumerDocument = agreementService.addConsumerDocument( agreement.id, @@ -699,7 +699,7 @@ describe("agreement consumer document", () => { }); await addOneDelegation(delegation); - await addSomeRandomDelegations(agreement1); + await addSomeRandomDelegations(agreement1, addOneDelegation); await uploadDocument( agreement1.id, diff --git a/packages/agreement-process/test/archiveAgreement.test.ts b/packages/agreement-process/test/archiveAgreement.test.ts index d586cd3866..4ad9210eef 100644 --- a/packages/agreement-process/test/archiveAgreement.test.ts +++ b/packages/agreement-process/test/archiveAgreement.test.ts @@ -1,6 +1,7 @@ import { fail } from "assert"; import { genericLogger } from "pagopa-interop-commons"; import { + addSomeRandomDelegations, decodeProtobufPayload, getMockAgreement, getMockDelegation, @@ -30,7 +31,6 @@ import { import { addOneAgreement, addOneDelegation, - addSomeRandomDelegations, agreementService, readLastAgreementEvent, } from "./utils.js"; @@ -124,7 +124,7 @@ describe("archive agreement", () => { await addOneAgreement(agreement); await addOneDelegation(delegation); - await addSomeRandomDelegations(agreement); + await addSomeRandomDelegations(agreement, addOneDelegation); const returnedAgreement = await agreementService.archiveAgreement( agreement.id, diff --git a/packages/agreement-process/test/cloneAgreement.test.ts b/packages/agreement-process/test/cloneAgreement.test.ts index 177026cc6d..e2fb3973f9 100644 --- a/packages/agreement-process/test/cloneAgreement.test.ts +++ b/packages/agreement-process/test/cloneAgreement.test.ts @@ -3,6 +3,7 @@ /* eslint-disable fp/no-delete */ import { FileManagerError, genericLogger } from "pagopa-interop-commons"; import { + addSomeRandomDelegations, decodeProtobufPayload, getMockAgreement, getMockCertifiedTenantAttribute, @@ -50,7 +51,6 @@ import { addOneDelegation, addOneEService, addOneTenant, - addSomeRandomDelegations, agreementService, fileManager, getMockConsumerDocument, @@ -275,7 +275,7 @@ describe("clone agreement", () => { state: delegationState.active, }); await addOneDelegation(delegation); - await addSomeRandomDelegations(agreementToBeCloned); + await addSomeRandomDelegations(agreementToBeCloned, addOneDelegation); const anotherNonConflictingAgreement = { ...getMockAgreement( @@ -415,7 +415,7 @@ describe("clone agreement", () => { state: delegationState.active, }); await addOneDelegation(delegation); - await addSomeRandomDelegations(agreement); + await addSomeRandomDelegations(agreement, addOneDelegation); await expect( agreementService.cloneAgreement(agreement.id, { authData, diff --git a/packages/agreement-process/test/deleteAgreement.test.ts b/packages/agreement-process/test/deleteAgreement.test.ts index 6326b9ffdd..afce096869 100644 --- a/packages/agreement-process/test/deleteAgreement.test.ts +++ b/packages/agreement-process/test/deleteAgreement.test.ts @@ -1,6 +1,7 @@ /* eslint-disable functional/no-let */ import { fileManagerDeleteError, genericLogger } from "pagopa-interop-commons"; import { + addSomeRandomDelegations, decodeProtobufPayload, getMockAgreement, getMockDelegation, @@ -27,7 +28,6 @@ import { config } from "../src/config/config.js"; import { addOneAgreement, addOneDelegation, - addSomeRandomDelegations, agreementService, fileManager, getMockConsumerDocument, @@ -125,7 +125,7 @@ describe("delete agreement", () => { await addOneAgreement(agreement); await addOneDelegation(delegation); - await addSomeRandomDelegations(agreement); + await addSomeRandomDelegations(agreement, addOneDelegation); await Promise.all( consumerDocuments.map((doc) => diff --git a/packages/agreement-process/test/submitAgreement.test.ts b/packages/agreement-process/test/submitAgreement.test.ts index 179465fc16..45f91cfff6 100644 --- a/packages/agreement-process/test/submitAgreement.test.ts +++ b/packages/agreement-process/test/submitAgreement.test.ts @@ -12,6 +12,7 @@ import { } from "pagopa-interop-commons"; import { addDays, subDays } from "date-fns"; import { + addSomeRandomDelegations, decodeProtobufPayload, getMockAgreement, getMockAttribute, @@ -77,7 +78,6 @@ import { addOneDelegation, addOneEService, addOneTenant, - addSomeRandomDelegations, agreementService, authDataAndDelegationsFromRequesterIs, fileManager, @@ -1076,7 +1076,7 @@ describe("submit agreement", () => { await addOneTenant(producerAndConsumer); await addOneTenant(producer); await addOneAgreement(agreement); - await addSomeRandomDelegations(agreement); + await addSomeRandomDelegations(agreement, addOneDelegation); await addDelegationsAndDelegates({ producerDelegation, delegateProducer, @@ -1267,7 +1267,7 @@ describe("submit agreement", () => { await addOneAttribute(declaredAttribute); await addOneAttribute(certifiedAttribute); await addOneAgreement(agreement); - await addSomeRandomDelegations(agreement); + await addSomeRandomDelegations(agreement, addOneDelegation); await addDelegationsAndDelegates({ producerDelegation, delegateProducer, @@ -1509,7 +1509,7 @@ describe("submit agreement", () => { await addOneTenant(consumer); await addOneTenant(producer); await addOneAgreement(agreement); - await addSomeRandomDelegations(agreement); + await addSomeRandomDelegations(agreement, addOneDelegation); await addDelegationsAndDelegates({ producerDelegation: undefined, delegateProducer: undefined, @@ -1710,7 +1710,7 @@ describe("submit agreement", () => { await addOneAttribute(declaredAttribute); await addOneAttribute(certifiedAttribute); await addOneAgreement(agreement); - await addSomeRandomDelegations(agreement); + await addSomeRandomDelegations(agreement, addOneDelegation); await addDelegationsAndDelegates({ producerDelegation, delegateProducer, @@ -1992,7 +1992,7 @@ describe("submit agreement", () => { await addOneAttribute(declaredAttribute); await addOneAttribute(verifiedAttribute); await addOneAgreement(agreement); - await addSomeRandomDelegations(agreement); + await addSomeRandomDelegations(agreement, addOneDelegation); await addDelegationsAndDelegates({ producerDelegation: undefined, delegateProducer: undefined, @@ -2148,7 +2148,7 @@ describe("submit agreement", () => { await addOneAttribute(declaredAttribute); await addOneAttribute(verifiedAttribute); await addOneAgreement(agreement); - await addSomeRandomDelegations(agreement); + await addSomeRandomDelegations(agreement, addOneDelegation); await addDelegationsAndDelegates({ producerDelegation: undefined, delegateProducer: undefined, diff --git a/packages/agreement-process/test/upgradeAgreement.test.ts b/packages/agreement-process/test/upgradeAgreement.test.ts index 3d7c478b47..8bd4407f5c 100644 --- a/packages/agreement-process/test/upgradeAgreement.test.ts +++ b/packages/agreement-process/test/upgradeAgreement.test.ts @@ -10,6 +10,7 @@ import { timeAtRomeZone, } from "pagopa-interop-commons"; import { + addSomeRandomDelegations, decodeProtobufPayload, getMockAgreement, getMockAttribute, @@ -72,7 +73,6 @@ import { addOneDelegation, addOneEService, addOneTenant, - addSomeRandomDelegations, agreementService, authDataAndDelegationsFromRequesterIs, fileManager, @@ -181,7 +181,7 @@ describe("upgrade Agreement", () => { const { authData, consumerDelegation, delegateConsumer } = authDataAndDelegationsFromRequesterIs(requesterIs, agreement); - await addSomeRandomDelegations(agreement); + await addSomeRandomDelegations(agreement, addOneDelegation); await addDelegationsAndDelegates({ producerDelegation: undefined, delegateProducer: undefined, @@ -436,7 +436,7 @@ describe("upgrade Agreement", () => { }) : undefined; - await addSomeRandomDelegations(agreement); + await addSomeRandomDelegations(agreement, addOneDelegation); await addDelegationsAndDelegates({ producerDelegation, delegateProducer, @@ -801,7 +801,7 @@ describe("upgrade Agreement", () => { const { authData, consumerDelegation, delegateConsumer } = authDataAndDelegationsFromRequesterIs(requesterIs, agreement); - await addSomeRandomDelegations(agreement); + await addSomeRandomDelegations(agreement, addOneDelegation); await addDelegationsAndDelegates({ producerDelegation: undefined, delegateProducer: undefined, diff --git a/packages/purpose-process/test/updatePurpose.test.ts b/packages/purpose-process/test/updatePurpose.test.ts index 2bc8c37ad5..25fb270bc4 100644 --- a/packages/purpose-process/test/updatePurpose.test.ts +++ b/packages/purpose-process/test/updatePurpose.test.ts @@ -51,7 +51,7 @@ import { tenantKindNotFound, riskAnalysisValidationFailed, duplicatedPurposeTitle, - operationRestrictedToDelegate, + organizationNotAllowed, } from "../src/model/domain/errors.js"; import { getMockEService, @@ -651,7 +651,7 @@ describe("updatePurpose and updateReversePurpose", () => { riskAnalysisValidationFailed([unexpectedRulesVersionError("0")]) ); }); - it("should throw operationRestrictedToDelegate when the requester is the Consumer but there is a Consumer Delegation in updatePurpose", async () => { + it("should throw organizationNotAllowed when the requester is the Consumer but there is a Consumer Delegation in updatePurpose", async () => { const authData = getRandomAuthData(); const purpose = { ...purposeForDeliver, @@ -675,11 +675,9 @@ describe("updatePurpose and updateReversePurpose", () => { logger: genericLogger, serviceName: "", }) - ).rejects.toThrowError( - operationRestrictedToDelegate(authData.organizationId, delegation.id) - ); + ).rejects.toThrowError(organizationNotAllowed(authData.organizationId)); }); - it("should throw operationRestrictedToDelegate when the requester is the Consumer but there is a Consumer Delegation in updateReversePurpose", async () => { + it("should throw organizationNotAllowed when the requester is the Consumer but there is a Consumer Delegation in updateReversePurpose", async () => { const authData = getRandomAuthData(); const purpose = { ...purposeForDeliver, @@ -708,8 +706,6 @@ describe("updatePurpose and updateReversePurpose", () => { serviceName: "", } ) - ).rejects.toThrowError( - operationRestrictedToDelegate(authData.organizationId, delegation.id) - ); + ).rejects.toThrowError(organizationNotAllowed(authData.organizationId)); }); }); From dac1f7a944c8c290911ceadfdf5910e4c1cc6676 Mon Sep 17 00:00:00 2001 From: AsterITA Date: Wed, 8 Jan 2025 12:55:31 +0100 Subject: [PATCH 11/23] bump outbound, added delegationId in Purpose where missing --- .../agreement-outbound-writer/package.json | 2 +- packages/api-clients/open-api/bffApi.yml | 3 ++ packages/api-clients/open-api/purposeApi.yml | 3 ++ .../src/services/purposeService.ts | 1 + packages/catalog-outbound-writer/package.json | 2 +- .../delegation-outbound-writer/package.json | 2 +- .../models/proto/v2/purpose/purpose.proto | 2 ++ .../src/purpose/protobufConverterFromV2.ts | 5 ++- packages/purpose-outbound-writer/package.json | 2 +- packages/tenant-outbound-writer/package.json | 2 +- pnpm-lock.yaml | 35 +++++++------------ 11 files changed, 31 insertions(+), 28 deletions(-) diff --git a/packages/agreement-outbound-writer/package.json b/packages/agreement-outbound-writer/package.json index e2301b26bc..9f2abc9464 100644 --- a/packages/agreement-outbound-writer/package.json +++ b/packages/agreement-outbound-writer/package.json @@ -29,7 +29,7 @@ "vitest": "1.6.0" }, "dependencies": { - "@pagopa/interop-outbound-models": "1.0.12-b", + "@pagopa/interop-outbound-models": "1.0.12-l", "@protobuf-ts/runtime": "2.9.4", "connection-string": "4.4.0", "dotenv-flow": "4.1.0", diff --git a/packages/api-clients/open-api/bffApi.yml b/packages/api-clients/open-api/bffApi.yml index 1e4c522f3c..0ceea595bb 100644 --- a/packages/api-clients/open-api/bffApi.yml +++ b/packages/api-clients/open-api/bffApi.yml @@ -15361,6 +15361,9 @@ components: description: "total daily calls available for this e-service." type: integer format: int32 + delegationId: + type: string + format: uuid required: - id - title diff --git a/packages/api-clients/open-api/purposeApi.yml b/packages/api-clients/open-api/purposeApi.yml index eef5a3b4e8..832b89e19b 100644 --- a/packages/api-clients/open-api/purposeApi.yml +++ b/packages/api-clients/open-api/purposeApi.yml @@ -911,6 +911,9 @@ components: type: boolean freeOfChargeReason: type: string + delegationId: + type: string + format: uuid required: - id - eserviceId diff --git a/packages/backend-for-frontend/src/services/purposeService.ts b/packages/backend-for-frontend/src/services/purposeService.ts index 9bc499771e..0f52fd28d1 100644 --- a/packages/backend-for-frontend/src/services/purposeService.ts +++ b/packages/backend-for-frontend/src/services/purposeService.ts @@ -175,6 +175,7 @@ export function purposeServiceBuilder( dailyCallsTotal: currentDescriptor.dailyCallsTotal, rejectedVersion: rejectedVersion && toBffApiPurposeVersion(rejectedVersion), + delegationId: purpose.delegationId, }; }; diff --git a/packages/catalog-outbound-writer/package.json b/packages/catalog-outbound-writer/package.json index 3c9d5e26e5..ed1f2e9196 100644 --- a/packages/catalog-outbound-writer/package.json +++ b/packages/catalog-outbound-writer/package.json @@ -29,7 +29,7 @@ "vitest": "1.6.0" }, "dependencies": { - "@pagopa/interop-outbound-models": "1.0.12-h", + "@pagopa/interop-outbound-models": "1.0.12-l", "@protobuf-ts/runtime": "2.9.4", "connection-string": "4.4.0", "dotenv-flow": "4.1.0", diff --git a/packages/delegation-outbound-writer/package.json b/packages/delegation-outbound-writer/package.json index 0dba703a55..b8379ece99 100644 --- a/packages/delegation-outbound-writer/package.json +++ b/packages/delegation-outbound-writer/package.json @@ -27,7 +27,7 @@ "vitest": "1.6.0" }, "dependencies": { - "@pagopa/interop-outbound-models": "1.0.12-h", + "@pagopa/interop-outbound-models": "1.0.12-l", "@protobuf-ts/runtime": "2.9.4", "connection-string": "4.4.0", "dotenv-flow": "4.1.0", diff --git a/packages/models/proto/v2/purpose/purpose.proto b/packages/models/proto/v2/purpose/purpose.proto index eb8dc99afc..6933ee3210 100644 --- a/packages/models/proto/v2/purpose/purpose.proto +++ b/packages/models/proto/v2/purpose/purpose.proto @@ -5,6 +5,7 @@ package purpose.v2; import "v2/purpose/riskAnalysis.proto"; message PurposeV2 { + reserved 4; string id = 1; string eserviceId = 2; string consumerId = 3; @@ -18,6 +19,7 @@ message PurposeV2 { optional int64 updatedAt = 12; bool isFreeOfCharge = 13; optional string freeOfChargeReason = 14; + optional string delegationId = 15; } message PurposeVersionV2 { diff --git a/packages/models/src/purpose/protobufConverterFromV2.ts b/packages/models/src/purpose/protobufConverterFromV2.ts index 64ec302423..471875a254 100644 --- a/packages/models/src/purpose/protobufConverterFromV2.ts +++ b/packages/models/src/purpose/protobufConverterFromV2.ts @@ -1,4 +1,4 @@ -import { RiskAnalysisId, unsafeBrandId } from "../brandedIds.js"; +import { DelegationId, RiskAnalysisId, unsafeBrandId } from "../brandedIds.js"; import { PurposeStateV2, PurposeVersionDocumentV2, @@ -87,4 +87,7 @@ export const fromPurposeV2 = (input: PurposeV2): Purpose => ({ riskAnalysisForm: input.riskAnalysisForm ? fromPurposeRiskAnalysisFormV2(input.riskAnalysisForm) : undefined, + delegationId: input.delegationId + ? unsafeBrandId(input.delegationId) + : undefined, }); diff --git a/packages/purpose-outbound-writer/package.json b/packages/purpose-outbound-writer/package.json index 5f7f62b872..9406a34c35 100644 --- a/packages/purpose-outbound-writer/package.json +++ b/packages/purpose-outbound-writer/package.json @@ -29,7 +29,7 @@ "vitest": "1.6.0" }, "dependencies": { - "@pagopa/interop-outbound-models": "1.0.12-b", + "@pagopa/interop-outbound-models": "1.0.12-l", "@protobuf-ts/runtime": "2.9.4", "connection-string": "4.4.0", "dotenv-flow": "4.1.0", diff --git a/packages/tenant-outbound-writer/package.json b/packages/tenant-outbound-writer/package.json index 6d8a08b450..6535889bc3 100644 --- a/packages/tenant-outbound-writer/package.json +++ b/packages/tenant-outbound-writer/package.json @@ -29,7 +29,7 @@ "vitest": "1.6.0" }, "dependencies": { - "@pagopa/interop-outbound-models": "1.0.12-b", + "@pagopa/interop-outbound-models": "1.0.12-l", "@protobuf-ts/runtime": "2.9.4", "connection-string": "4.4.0", "dotenv-flow": "4.1.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 93f831f5cf..3002551d3e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -98,8 +98,8 @@ importers: packages/agreement-outbound-writer: dependencies: '@pagopa/interop-outbound-models': - specifier: 1.0.12-b - version: 1.0.12-b + specifier: 1.0.12-l + version: 1.0.12-l '@protobuf-ts/runtime': specifier: 2.9.4 version: 2.9.4 @@ -1063,8 +1063,8 @@ importers: packages/catalog-outbound-writer: dependencies: '@pagopa/interop-outbound-models': - specifier: 1.0.12-h - version: 1.0.12-h + specifier: 1.0.12-l + version: 1.0.12-l '@protobuf-ts/runtime': specifier: 2.9.4 version: 2.9.4 @@ -1794,8 +1794,8 @@ importers: packages/delegation-outbound-writer: dependencies: '@pagopa/interop-outbound-models': - specifier: 1.0.12-h - version: 1.0.12-h + specifier: 1.0.12-l + version: 1.0.12-l '@protobuf-ts/runtime': specifier: 2.9.4 version: 2.9.4 @@ -2653,8 +2653,8 @@ importers: packages/purpose-outbound-writer: dependencies: '@pagopa/interop-outbound-models': - specifier: 1.0.12-b - version: 1.0.12-b + specifier: 1.0.12-l + version: 1.0.12-l '@protobuf-ts/runtime': specifier: 2.9.4 version: 2.9.4 @@ -2958,8 +2958,8 @@ importers: packages/tenant-outbound-writer: dependencies: '@pagopa/interop-outbound-models': - specifier: 1.0.12-b - version: 1.0.12-b + specifier: 1.0.12-l + version: 1.0.12-l '@protobuf-ts/runtime': specifier: 2.9.4 version: 2.9.4 @@ -4021,11 +4021,8 @@ packages: '@pagopa/eslint-config@3.0.0': resolution: {integrity: sha512-eYIPdiuYRbRPR5k0OuteRNqYb0Z2nfJ/lZohejB7ylfBeSDWwkaV8Z19AXP4RymE6oEesyPDZ6i0yNaE9tQrHw==} - '@pagopa/interop-outbound-models@1.0.12-b': - resolution: {integrity: sha512-9ae07TWBNxAm5SgvjCHNekcUTidI2Jf0LZeofqQHhPPk10/WmkX1ZufvrKbyKYwSCw0BjpwhQwJLKVh+xRgJ8A==} - - '@pagopa/interop-outbound-models@1.0.12-h': - resolution: {integrity: sha512-9HCSpSBbj7Id2kHSozP5inaepVK8ktlzQHfy6epablGyMhH0ZAzZoEqakad7UPVPeT/qBx5wrydBm/Y5c2iQFg==} + '@pagopa/interop-outbound-models@1.0.12-l': + resolution: {integrity: sha512-c1ZT5a7q7Mv/xgTqTT+5UVkdFGHY07YRnyFKA5yju1vI3bBmHFpEAsAIDoMcP13ue/cVxI1/mVtsX893StZO5A==} '@pkgjs/parseargs@0.11.0': resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} @@ -9330,13 +9327,7 @@ snapshots: - tsutils - typescript - '@pagopa/interop-outbound-models@1.0.12-b': - dependencies: - '@protobuf-ts/runtime': 2.9.4 - ts-pattern: 5.2.0 - zod: 3.23.8 - - '@pagopa/interop-outbound-models@1.0.12-h': + '@pagopa/interop-outbound-models@1.0.12-l': dependencies: '@protobuf-ts/runtime': 2.9.4 ts-pattern: 5.2.0 From b639a79c79ff0fa72fd60bd7cbdb22cded8c13b6 Mon Sep 17 00:00:00 2001 From: Roberto Gregnanin <128478646+rGregnanin@users.noreply.github.com> Date: Thu, 9 Jan 2025 11:19:55 +0100 Subject: [PATCH 12/23] added test --- .../test/updatePurpose.test.ts | 107 ++++++++++++++++++ 1 file changed, 107 insertions(+) diff --git a/packages/purpose-process/test/updatePurpose.test.ts b/packages/purpose-process/test/updatePurpose.test.ts index 25fb270bc4..ea8a172d2e 100644 --- a/packages/purpose-process/test/updatePurpose.test.ts +++ b/packages/purpose-process/test/updatePurpose.test.ts @@ -17,6 +17,7 @@ import { getRandomAuthData, getMockDelegation, addSomeRandomDelegations, + getMockAgreement, } from "pagopa-interop-commons-test/index.js"; import { tenantKind, @@ -37,6 +38,7 @@ import { toReadModelTenant, delegationKind, delegationState, + Agreement, } from "pagopa-interop-models"; import { purposeApi } from "pagopa-interop-api-clients"; import { describe, it, expect, vi, beforeAll, afterAll } from "vitest"; @@ -63,6 +65,9 @@ import { purposeService, tenants, addOneDelegation, + addOneTenant, + addOneEService, + addOneAgreement, } from "./utils.js"; describe("updatePurpose and updateReversePurpose", () => { @@ -376,6 +381,108 @@ describe("updatePurpose and updateReversePurpose", () => { expect(writtenPayload.purpose).toEqual(toPurposeV2(purpose)); expect(isRiskAnalysisValid).toBe(true); }); + it("should succeed when requester is Consumer Delegate and the eservice was created by a delegated tenant and the Purpose is in a updatable state and the e-service is in mode RECEIVE", async () => { + const producerDelegator = { + ...getMockTenant(), + id: generateId(), + kind: tenantType, + }; + const producer = { + ...getMockTenant(), + id: generateId(), + kind: tenantType, + }; + const consumerDelegator = { + ...getMockTenant(), + id: generateId(), + kind: tenantType, + }; + const consumer = { + ...getMockTenant(), + id: generateId(), + kind: tenantType, + }; + + const eservice: EService = { + ...getMockEService(), + mode: eserviceMode.receive, + producerId: producer.id, + }; + const agreement: Agreement = { + ...getMockAgreement(), + producerId: producer.id, + consumerId: consumer.id, + eserviceId: eservice.id, + }; + const delegatePurpose: Purpose = { + ...purposeForReceive, + consumerId: consumer.id, + eserviceId: eservice.id, + }; + + const producerDelegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, + eserviceId: eservice.id, + delegatorId: producerDelegator.id, + delegateId: producer.id, + state: delegationState.active, + }); + + const consumerDelegation = getMockDelegation({ + kind: delegationKind.delegatedConsumer, + eserviceId: eservice.id, + delegatorId: consumerDelegator.id, + delegateId: consumer.id, + state: delegationState.active, + }); + + await addOneTenant(producerDelegator); + await addOneTenant(producer); + await addOneTenant(consumerDelegator); + await addOneTenant(consumer); + await addOneEService(eservice); + await addOneAgreement(agreement); + await addOnePurpose(delegatePurpose); + await addOneDelegation(producerDelegation); + await addOneDelegation(consumerDelegation); + await addSomeRandomDelegations(delegatePurpose, addOneDelegation); + + const { purpose, isRiskAnalysisValid } = + await purposeService.updateReversePurpose( + delegatePurpose.id, + reversePurposeUpdateContent, + { + authData: getRandomAuthData(consumer.id), + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + } + ); + + const writtenEvent = await readLastPurposeEvent(delegatePurpose.id); + expect(writtenEvent).toMatchObject({ + stream_id: delegatePurpose.id, + version: "1", + type: "DraftPurposeUpdated", + event_version: 2, + }); + + const writtenPayload = decodeProtobufPayload({ + messageType: DraftPurposeUpdatedV2, + payload: writtenEvent.data, + }); + + const expectedPurpose: Purpose = createUpdatedPurpose( + delegatePurpose, + reversePurposeUpdateContent, + validRiskAnalysis, + writtenPayload.purpose!.riskAnalysisForm! + ); + + expect(writtenPayload.purpose).toEqual(toPurposeV2(expectedPurpose)); + expect(writtenPayload.purpose).toEqual(toPurposeV2(purpose)); + expect(isRiskAnalysisValid).toBe(true); + }); it("Should throw purposeNotFound if the purpose doesn't exist", async () => { await writeInReadmodel(toReadModelEService(eServiceDeliver), eservices); await writeInReadmodel(toReadModelTenant(tenant), tenants); From 340ecfa891300f8f1fe57665d4e5de312e55a955 Mon Sep 17 00:00:00 2001 From: AsterITA Date: Thu, 9 Jan 2025 12:11:29 +0100 Subject: [PATCH 13/23] added missing delegationIds --- packages/datalake-data-export/src/config/models/models.ts | 1 + packages/purpose-process/src/model/domain/apiConverter.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/packages/datalake-data-export/src/config/models/models.ts b/packages/datalake-data-export/src/config/models/models.ts index 081661b129..688423e4a2 100644 --- a/packages/datalake-data-export/src/config/models/models.ts +++ b/packages/datalake-data-export/src/config/models/models.ts @@ -98,6 +98,7 @@ export const ExportedPurpose = Purpose.pick({ id: true, eserviceId: true, consumerId: true, + delegationId: true, suspendedByConsumer: true, suspendedByProducer: true, title: true, diff --git a/packages/purpose-process/src/model/domain/apiConverter.ts b/packages/purpose-process/src/model/domain/apiConverter.ts index 5b58558474..f435a97bfa 100644 --- a/packages/purpose-process/src/model/domain/apiConverter.ts +++ b/packages/purpose-process/src/model/domain/apiConverter.ts @@ -117,6 +117,7 @@ export const purposeToApiPurpose = ( id: purpose.id, eserviceId: purpose.eserviceId, consumerId: purpose.consumerId, + delegationId: purpose.delegationId, versions: purpose.versions.map(purposeVersionToApiPurposeVersion), suspendedByConsumer: purpose.suspendedByConsumer, suspendedByProducer: purpose.suspendedByProducer, From 761268742a5885fee45417a19423aa3e7d96733f Mon Sep 17 00:00:00 2001 From: Roberto Gregnanin <128478646+rGregnanin@users.noreply.github.com> Date: Thu, 9 Jan 2025 15:28:39 +0100 Subject: [PATCH 14/23] added test --- .../test/updatePurpose.test.ts | 106 ++++++++++++++++++ 1 file changed, 106 insertions(+) diff --git a/packages/purpose-process/test/updatePurpose.test.ts b/packages/purpose-process/test/updatePurpose.test.ts index ea8a172d2e..8fdaa30434 100644 --- a/packages/purpose-process/test/updatePurpose.test.ts +++ b/packages/purpose-process/test/updatePurpose.test.ts @@ -381,6 +381,112 @@ describe("updatePurpose and updateReversePurpose", () => { expect(writtenPayload.purpose).toEqual(toPurposeV2(purpose)); expect(isRiskAnalysisValid).toBe(true); }); + it("should succeed when requester is Consumer Delegate and the eservice was created by a delegated tenant and the Purpose is in a updatable state and the e-service is in mode DELIVER", async () => { + const producerDelegator = { + ...getMockTenant(), + id: generateId(), + kind: tenantType, + }; + const producer = { + ...getMockTenant(), + id: generateId(), + kind: tenantType, + }; + const consumerDelegator = { + ...getMockTenant(), + id: generateId(), + kind: tenantType, + }; + const consumer = { + ...getMockTenant(), + id: generateId(), + kind: tenantType, + }; + + const eservice: EService = { + ...getMockEService(), + mode: eserviceMode.deliver, + producerId: producer.id, + }; + const agreement: Agreement = { + ...getMockAgreement(), + producerId: producer.id, + consumerId: consumer.id, + eserviceId: eservice.id, + }; + const delegatePurpose: Purpose = { + ...purposeForDeliver, + consumerId: consumer.id, + eserviceId: eservice.id, + }; + + const producerDelegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, + eserviceId: eservice.id, + delegatorId: producerDelegator.id, + delegateId: producer.id, + state: delegationState.active, + }); + + const consumerDelegation = getMockDelegation({ + kind: delegationKind.delegatedConsumer, + eserviceId: eservice.id, + delegatorId: consumerDelegator.id, + delegateId: consumer.id, + state: delegationState.active, + }); + + await addOneTenant(producerDelegator); + await addOneTenant(producer); + await addOneTenant(consumerDelegator); + await addOneTenant(consumer); + await addOneEService(eservice); + await addOneAgreement(agreement); + await addOnePurpose(delegatePurpose); + await addOneDelegation(producerDelegation); + await addOneDelegation(consumerDelegation); + await addSomeRandomDelegations(delegatePurpose, addOneDelegation); + + const updateContentWithoutTitle = { + ...purposeUpdateContent, + title: delegatePurpose.title, + }; + + const { purpose, isRiskAnalysisValid } = await purposeService.updatePurpose( + delegatePurpose.id, + updateContentWithoutTitle, + { + authData: getRandomAuthData(consumer.id), + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + } + ); + + const writtenEvent = await readLastPurposeEvent(delegatePurpose.id); + expect(writtenEvent).toMatchObject({ + stream_id: delegatePurpose.id, + version: "1", + type: "DraftPurposeUpdated", + event_version: 2, + }); + + const writtenPayload = decodeProtobufPayload({ + messageType: DraftPurposeUpdatedV2, + payload: writtenEvent.data, + }); + + const expectedPurpose: Purpose = createUpdatedPurpose( + delegatePurpose, + updateContentWithoutTitle, + validRiskAnalysis, + writtenPayload.purpose!.riskAnalysisForm! + ); + + expect(writtenPayload.purpose).toEqual(toPurposeV2(expectedPurpose)); + expect(writtenPayload.purpose).toEqual(toPurposeV2(purpose)); + expect(isRiskAnalysisValid).toBe(true); + }); it("should succeed when requester is Consumer Delegate and the eservice was created by a delegated tenant and the Purpose is in a updatable state and the e-service is in mode RECEIVE", async () => { const producerDelegator = { ...getMockTenant(), From 21372de44df6f49e45f69653c66d553ca5f783ad Mon Sep 17 00:00:00 2001 From: Roberto Gregnanin <128478646+rGregnanin@users.noreply.github.com> Date: Fri, 10 Jan 2025 17:05:43 +0100 Subject: [PATCH 15/23] fix name --- .../test/updatePurpose.test.ts | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/packages/purpose-process/test/updatePurpose.test.ts b/packages/purpose-process/test/updatePurpose.test.ts index 8fdaa30434..f8cf1ff180 100644 --- a/packages/purpose-process/test/updatePurpose.test.ts +++ b/packages/purpose-process/test/updatePurpose.test.ts @@ -382,22 +382,22 @@ describe("updatePurpose and updateReversePurpose", () => { expect(isRiskAnalysisValid).toBe(true); }); it("should succeed when requester is Consumer Delegate and the eservice was created by a delegated tenant and the Purpose is in a updatable state and the e-service is in mode DELIVER", async () => { - const producerDelegator = { + const producer = { ...getMockTenant(), id: generateId(), kind: tenantType, }; - const producer = { + const producerDelegate = { ...getMockTenant(), id: generateId(), kind: tenantType, }; - const consumerDelegator = { + const consumer = { ...getMockTenant(), id: generateId(), kind: tenantType, }; - const consumer = { + const consumerDelegate = { ...getMockTenant(), id: generateId(), kind: tenantType, @@ -423,22 +423,22 @@ describe("updatePurpose and updateReversePurpose", () => { const producerDelegation = getMockDelegation({ kind: delegationKind.delegatedProducer, eserviceId: eservice.id, - delegatorId: producerDelegator.id, - delegateId: producer.id, + delegatorId: producer.id, + delegateId: producerDelegate.id, state: delegationState.active, }); const consumerDelegation = getMockDelegation({ kind: delegationKind.delegatedConsumer, eserviceId: eservice.id, - delegatorId: consumerDelegator.id, - delegateId: consumer.id, + delegatorId: consumer.id, + delegateId: consumerDelegate.id, state: delegationState.active, }); - await addOneTenant(producerDelegator); + await addOneTenant(producerDelegate); await addOneTenant(producer); - await addOneTenant(consumerDelegator); + await addOneTenant(consumerDelegate); await addOneTenant(consumer); await addOneEService(eservice); await addOneAgreement(agreement); @@ -456,7 +456,7 @@ describe("updatePurpose and updateReversePurpose", () => { delegatePurpose.id, updateContentWithoutTitle, { - authData: getRandomAuthData(consumer.id), + authData: getRandomAuthData(consumerDelegate.id), correlationId: generateId(), logger: genericLogger, serviceName: "", From 93e0d58d519f91d43852a1f941841b8adf8070d5 Mon Sep 17 00:00:00 2001 From: AsterITA Date: Mon, 13 Jan 2025 12:06:05 +0100 Subject: [PATCH 16/23] added missing checks for delegate --- .../src/routers/PurposeRouter.ts | 42 +- .../src/services/purposeService.ts | 259 ++++------ .../test/activatePurposeVersion.test.ts | 485 +++++++++++------- .../test/deletePurposeVersion.test.ts | 115 +++-- .../test/suspendPurposeVersion.test.ts | 241 +++++---- 5 files changed, 661 insertions(+), 481 deletions(-) diff --git a/packages/purpose-process/src/routers/PurposeRouter.ts b/packages/purpose-process/src/routers/PurposeRouter.ts index 2047f59779..236a4c3ae8 100644 --- a/packages/purpose-process/src/routers/PurposeRouter.ts +++ b/packages/purpose-process/src/routers/PurposeRouter.ts @@ -336,13 +336,13 @@ const purposeRouter = ( async (req, res) => { const ctx = fromAppContext(req.ctx); try { - await purposeService.deletePurposeVersion({ - purposeId: unsafeBrandId(req.params.purposeId), - versionId: unsafeBrandId(req.params.versionId), - organizationId: req.ctx.authData.organizationId, - correlationId: req.ctx.correlationId, - logger: ctx.logger, - }); + await purposeService.deletePurposeVersion( + { + purposeId: unsafeBrandId(req.params.purposeId), + versionId: unsafeBrandId(req.params.versionId), + }, + ctx + ); return res.status(204).send(); } catch (error) { const errorRes = makeApiProblem( @@ -419,13 +419,13 @@ const purposeRouter = ( const ctx = fromAppContext(req.ctx); try { const { purposeId, versionId } = req.params; - const purposeVersion = await purposeService.activatePurposeVersion({ - purposeId: unsafeBrandId(purposeId), - versionId: unsafeBrandId(versionId), - organizationId: req.ctx.authData.organizationId, - correlationId: req.ctx.correlationId, - logger: ctx.logger, - }); + const purposeVersion = await purposeService.activatePurposeVersion( + { + purposeId: unsafeBrandId(purposeId), + versionId: unsafeBrandId(versionId), + }, + ctx + ); return res .status(200) .send( @@ -482,13 +482,13 @@ const purposeRouter = ( async (req, res) => { const ctx = fromAppContext(req.ctx); try { - const suspendedVersion = await purposeService.suspendPurposeVersion({ - purposeId: unsafeBrandId(req.params.purposeId), - versionId: unsafeBrandId(req.params.versionId), - organizationId: req.ctx.authData.organizationId, - correlationId: req.ctx.correlationId, - logger: ctx.logger, - }); + const suspendedVersion = await purposeService.suspendPurposeVersion( + { + purposeId: unsafeBrandId(req.params.purposeId), + versionId: unsafeBrandId(req.params.versionId), + }, + ctx + ); return res .status(200) .send( diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index c920d7c0ee..1f8a8d2b62 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -44,7 +44,6 @@ import { RiskAnalysisId, RiskAnalysis, CorrelationId, - Delegation, } from "pagopa-interop-models"; import { purposeApi } from "pagopa-interop-api-clients"; import { P, match } from "ts-pattern"; @@ -244,18 +243,33 @@ export function purposeServiceBuilder( readModelService ); - const activeProducerDelegation = - await readModelService.getActiveProducerDelegationByEserviceId( - purpose.data.eserviceId + const isAllowedToRetrieveRiskAnalysis = + await assertRequesterIsAllowedToRetrieveRiskAnalysisDocument( + purpose.data, + eservice, + { organizationId }, + readModelService + ).then( + () => true, + () => false ); - return authorizeRiskAnalysisForm({ - purpose: purpose.data, - producerId: eservice.producerId, - organizationId, - tenantKind: await retrieveTenantKind(organizationId, readModelService), - activeProducerDelegation, - }); + if (!isAllowedToRetrieveRiskAnalysis) { + return { + purpose: { ...purpose.data, riskAnalysisForm: undefined }, + isRiskAnalysisValid: false, + }; + } + + const isRiskAnalysisValid = purposeIsDraft(purpose.data) + ? isRiskAnalysisFormValid( + purpose.data.riskAnalysisForm, + false, + await retrieveTenantKind(organizationId, readModelService) + ) + : true; + + return { purpose: purpose.data, isRiskAnalysisValid }; }, async getRiskAnalysisDocument({ purposeId, @@ -291,26 +305,27 @@ export function purposeServiceBuilder( return retrievePurposeVersionDocument(purposeId, version, documentId); }, - async deletePurposeVersion({ - purposeId, - versionId, - organizationId, - correlationId, - logger, - }: { - purposeId: PurposeId; - versionId: PurposeVersionId; - organizationId: TenantId; - correlationId: CorrelationId; - logger: Logger; - }): Promise { + async deletePurposeVersion( + { + purposeId, + versionId, + }: { + purposeId: PurposeId; + versionId: PurposeVersionId; + }, + { correlationId, authData, logger }: WithLogger + ): Promise { logger.info(`Deleting Version ${versionId} in Purpose ${purposeId}`); const purpose = await retrievePurpose(purposeId, readModelService); - if (organizationId !== purpose.data.consumerId) { - throw organizationIsNotTheConsumer(organizationId); - } + assertRequesterCanActAsConsumer( + purpose.data, + authData, + await readModelService.getActiveConsumerDelegationByPurpose( + purpose.data + ) + ); const purposeVersion = retrievePurposeVersion(versionId, purpose); @@ -512,19 +527,16 @@ export function purposeServiceBuilder( await repository.createEvent(event); return archivedVersion; }, - async suspendPurposeVersion({ - purposeId, - versionId, - organizationId, - correlationId, - logger, - }: { - purposeId: PurposeId; - versionId: PurposeVersionId; - organizationId: TenantId; - correlationId: CorrelationId; - logger: Logger; - }): Promise { + async suspendPurposeVersion( + { + purposeId, + versionId, + }: { + purposeId: PurposeId; + versionId: PurposeVersionId; + }, + { authData, correlationId, logger }: WithLogger + ): Promise { logger.info(`Suspending Version ${versionId} in Purpose ${purposeId}`); const purpose = await retrievePurpose(purposeId, readModelService); @@ -540,10 +552,10 @@ export function purposeServiceBuilder( ); const suspender = await getOrganizationRole({ - eserviceId: eservice.id, - organizationId, + eservice, producerId: eservice.producerId, consumerId: purpose.data.consumerId, + authData, readModelService, }); @@ -605,37 +617,29 @@ export function purposeServiceBuilder( purpose.eserviceId, readModelService ); - if (eservice === undefined) { - throw eserviceNotFound(purpose.eserviceId); - } - const activeProducerDelegation = - await readModelService.getActiveProducerDelegationByEserviceId( - eservice.id + const isAllowedToRetrieveRiskAnalysis = + await assertRequesterIsAllowedToRetrieveRiskAnalysisDocument( + purpose, + eservice, + { organizationId }, + readModelService + ).then( + () => true, + () => false ); - return { - purpose, - eservice, - activeProducerDelegation, - }; + return { purpose, isAllowedToRetrieveRiskAnalysis }; }) ); const purposesToReturn = mappingPurposeEservice.map( - ({ purpose, eservice, activeProducerDelegation }) => { - const isProducerOrConsumer = - organizationId === purpose.consumerId || - organizationId === eservice.producerId || - organizationId === activeProducerDelegation?.delegateId; - - return { - ...purpose, - riskAnalysisForm: isProducerOrConsumer - ? purpose.riskAnalysisForm - : undefined, - }; - } + ({ purpose, isAllowedToRetrieveRiskAnalysis }) => ({ + ...purpose, + riskAnalysisForm: isAllowedToRetrieveRiskAnalysis + ? purpose.riskAnalysisForm + : undefined, + }) ); return { @@ -779,19 +783,16 @@ export function purposeServiceBuilder( return newPurposeVersion; }, - async activatePurposeVersion({ - purposeId, - versionId, - organizationId, - correlationId, - logger, - }: { - purposeId: PurposeId; - versionId: PurposeVersionId; - organizationId: TenantId; - correlationId: CorrelationId; - logger: Logger; - }): Promise { + async activatePurposeVersion( + { + purposeId, + versionId, + }: { + purposeId: PurposeId; + versionId: PurposeVersionId; + }, + { authData, correlationId, logger }: WithLogger + ): Promise { logger.info(`Activating Version ${versionId} in Purpose ${purposeId}`); const purpose = await retrievePurpose(purposeId, readModelService); @@ -824,10 +825,10 @@ export function purposeServiceBuilder( } const purposeOwnership = await getOrganizationRole({ - eserviceId: eservice.id, - organizationId, + eservice, producerId: eservice.producerId, consumerId: purpose.data.consumerId, + authData, readModelService, }); @@ -877,7 +878,7 @@ export function purposeServiceBuilder( purposeOwnership: ownership.PRODUCER, }, () => { - throw organizationIsNotTheConsumer(organizationId); + throw organizationIsNotTheConsumer(authData.organizationId); } ) .with( @@ -886,7 +887,7 @@ export function purposeServiceBuilder( purposeOwnership: ownership.CONSUMER, }, () => { - throw organizationIsNotTheProducer(organizationId); + throw organizationIsNotTheProducer(authData.organizationId); } ) .with( @@ -997,7 +998,7 @@ export function purposeServiceBuilder( ) ) .otherwise(() => { - throw organizationNotAllowed(organizationId); + throw organizationNotAllowed(authData.organizationId); }); await repository.createEvent(event); @@ -1333,87 +1334,51 @@ export function purposeServiceBuilder( export type PurposeService = ReturnType; -const authorizeRiskAnalysisForm = ({ - purpose, - producerId, - organizationId, - tenantKind, - activeProducerDelegation, -}: { - purpose: Purpose; - producerId: TenantId; - organizationId: TenantId; - tenantKind: TenantKind; - activeProducerDelegation: Delegation | undefined; -}): { purpose: Purpose; isRiskAnalysisValid: boolean } => { - if ( - organizationId === purpose.consumerId || - organizationId === producerId || - organizationId === activeProducerDelegation?.delegateId - ) { - if (purposeIsDraft(purpose)) { - const isRiskAnalysisValid = isRiskAnalysisFormValid( - purpose.riskAnalysisForm, - false, - tenantKind - ); - return { purpose, isRiskAnalysisValid }; - } else { - return { purpose, isRiskAnalysisValid: true }; - } - } else { - return { - purpose: { ...purpose, riskAnalysisForm: undefined }, - isRiskAnalysisValid: false, - }; - } -}; - const getOrganizationRole = async ({ - eserviceId, - organizationId, + eservice, producerId, consumerId, + authData, readModelService, }: { - eserviceId: EServiceId; - organizationId: TenantId; + eservice: EService; producerId: TenantId; consumerId: TenantId; + authData: AuthData; readModelService: ReadModelService; }): Promise => { - if (producerId === consumerId && organizationId === producerId) { + if (producerId === consumerId && authData.organizationId === producerId) { return ownership.SELF_CONSUMER; - } else if (producerId !== consumerId && organizationId === consumerId) { - return ownership.CONSUMER; } - const activeProducerDelegation = - await readModelService.getActiveProducerDelegationByEserviceId(eserviceId); - - if ( - activeProducerDelegation?.delegateId === organizationId || - (!activeProducerDelegation && organizationId === producerId) - ) { + try { + assertRequesterCanActAsProducer( + eservice, + authData, + await readModelService.getActiveProducerDelegationByEserviceId( + eservice.id + ) + ); return ownership.PRODUCER; + } catch { + /* empty */ } - const activeConsumerDelegation = - await readModelService.getActiveConsumerDelegationByPurpose({ - eserviceId, - consumerId, - }); - - if ( - activeConsumerDelegation?.delegateId === organizationId || - (!activeConsumerDelegation && - producerId !== consumerId && - organizationId === consumerId) - ) { + try { + assertRequesterCanActAsConsumer( + { eserviceId: eservice.id, consumerId }, + authData, + await readModelService.getActiveConsumerDelegationByPurpose({ + eserviceId: eservice.id, + consumerId, + }) + ); return ownership.CONSUMER; + } catch { + /* empty */ } - throw organizationNotAllowed(organizationId); + throw organizationNotAllowed(authData.organizationId); }; const replacePurposeVersion = ( diff --git a/packages/purpose-process/test/activatePurposeVersion.test.ts b/packages/purpose-process/test/activatePurposeVersion.test.ts index c24c7cd809..3cfae4c8b4 100644 --- a/packages/purpose-process/test/activatePurposeVersion.test.ts +++ b/packages/purpose-process/test/activatePurposeVersion.test.ts @@ -13,7 +13,7 @@ import { readLastEventByStreamId, decodeProtobufPayload, getMockDelegation, - getMockAuthData, + getRandomAuthData, } from "pagopa-interop-commons-test"; import { PurposeVersion, @@ -131,13 +131,18 @@ describe("activatePurposeVersion", () => { await addOneTenant(mockConsumer); await addOneTenant(mockProducer); - const purposeVersion = await purposeService.activatePurposeVersion({ - purposeId: mockPurpose.id, - versionId: mockPurposeVersion.id, - organizationId: mockProducer.id, - correlationId: generateId(), - logger: genericLogger, - }); + const purposeVersion = await purposeService.activatePurposeVersion( + { + purposeId: mockPurpose.id, + versionId: mockPurposeVersion.id, + }, + { + authData: getRandomAuthData(mockProducer.id), + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + } + ); const writtenEvent = await readLastEventByStreamId( mockPurpose.id, @@ -187,13 +192,18 @@ describe("activatePurposeVersion", () => { await addOneTenant(mockConsumer); await addOneTenant(mockProducer); - const purposeVersion = await purposeService.activatePurposeVersion({ - purposeId: mockPurpose.id, - versionId: mockPurposeVersion.id, - organizationId: mockConsumer.id, - correlationId: generateId(), - logger: genericLogger, - }); + const purposeVersion = await purposeService.activatePurposeVersion( + { + purposeId: mockPurpose.id, + versionId: mockPurposeVersion.id, + }, + { + authData: getRandomAuthData(mockConsumer.id), + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + } + ); const writtenEvent = await readLastEventByStreamId( mockPurpose.id, @@ -243,13 +253,18 @@ describe("activatePurposeVersion", () => { await addOneTenant(mockConsumer); await addOneTenant(mockProducer); - const purposeVersion = await purposeService.activatePurposeVersion({ - purposeId: mockPurpose.id, - versionId: mockPurposeVersion.id, - organizationId: mockProducer.id, - correlationId: generateId(), - logger: genericLogger, - }); + const purposeVersion = await purposeService.activatePurposeVersion( + { + purposeId: mockPurpose.id, + versionId: mockPurposeVersion.id, + }, + { + authData: getRandomAuthData(mockProducer.id), + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + } + ); const writtenEvent = await readLastEventByStreamId( mockPurpose.id, @@ -300,13 +315,18 @@ describe("activatePurposeVersion", () => { await addOneTenant(mockConsumer); await addOneTenant(mockProducer); - const purposeVersion = await purposeService.activatePurposeVersion({ - purposeId: mockPurpose.id, - versionId: mockPurposeVersion.id, - organizationId: mockConsumer.id, - correlationId: generateId(), - logger: genericLogger, - }); + const purposeVersion = await purposeService.activatePurposeVersion( + { + purposeId: mockPurpose.id, + versionId: mockPurposeVersion.id, + }, + { + authData: getRandomAuthData(mockConsumer.id), + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + } + ); const writtenEvent = await readLastEventByStreamId( mockPurpose.id, @@ -366,13 +386,18 @@ describe("activatePurposeVersion", () => { await addOneTenant(mockConsumer); await addOneTenant(mockProducer); - const purposeVersion = await purposeService.activatePurposeVersion({ - purposeId: mockPurpose.id, - versionId: mockPurposeVersion.id, - organizationId: mockConsumer.id, - correlationId: generateId(), - logger: genericLogger, - }); + const purposeVersion = await purposeService.activatePurposeVersion( + { + purposeId: mockPurpose.id, + versionId: mockPurposeVersion.id, + }, + { + authData: getRandomAuthData(mockConsumer.id), + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + } + ); const writtenEvent = await readLastEventByStreamId( mockPurpose.id, @@ -421,13 +446,18 @@ describe("activatePurposeVersion", () => { await addOneTenant(mockConsumer); await addOneTenant(mockProducer); - const purposeVersion = await purposeService.activatePurposeVersion({ - purposeId: mockPurpose.id, - versionId: mockPurposeVersion.id, - organizationId: mockConsumer.id, - correlationId: generateId(), - logger: genericLogger, - }); + const purposeVersion = await purposeService.activatePurposeVersion( + { + purposeId: mockPurpose.id, + versionId: mockPurposeVersion.id, + }, + { + authData: getRandomAuthData(mockConsumer.id), + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + } + ); const writtenEvent = await readLastEventByStreamId( mockPurpose.id, @@ -472,13 +502,18 @@ describe("activatePurposeVersion", () => { await addOneTenant(mockConsumer); await addOneTenant(mockProducer); - const purposeVersion = await purposeService.activatePurposeVersion({ - purposeId: mockPurpose.id, - versionId: mockPurposeVersion.id, - organizationId: mockConsumer.id, - correlationId: generateId(), - logger: genericLogger, - }); + const purposeVersion = await purposeService.activatePurposeVersion( + { + purposeId: mockPurpose.id, + versionId: mockPurposeVersion.id, + }, + { + authData: getRandomAuthData(mockConsumer.id), + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + } + ); const writtenEvent = await readLastEventByStreamId( mockPurpose.id, @@ -521,13 +556,18 @@ describe("activatePurposeVersion", () => { await addOneTenant(mockProducer); expect(async () => { - await purposeService.activatePurposeVersion({ - purposeId: purpose.id, - versionId: purposeVersion.id, - organizationId: mockConsumer.id, - correlationId: generateId(), - logger: genericLogger, - }); + await purposeService.activatePurposeVersion( + { + purposeId: purpose.id, + versionId: purposeVersion.id, + }, + { + authData: getRandomAuthData(mockConsumer.id), + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + } + ); }).rejects.toThrowError(organizationIsNotTheProducer(mockConsumer.id)); }); @@ -545,13 +585,18 @@ describe("activatePurposeVersion", () => { await addOneTenant(mockProducer); expect(async () => { - await purposeService.activatePurposeVersion({ - purposeId: purpose.id, - versionId: purposeVersion.id, - organizationId: mockProducer.id, - correlationId: generateId(), - logger: genericLogger, - }); + await purposeService.activatePurposeVersion( + { + purposeId: purpose.id, + versionId: purposeVersion.id, + }, + { + authData: getRandomAuthData(mockProducer.id), + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + } + ); }).rejects.toThrowError(organizationIsNotTheConsumer(mockProducer.id)); }); @@ -579,13 +624,18 @@ describe("activatePurposeVersion", () => { await addOneTenant(mockProducer); expect(async () => { - await purposeService.activatePurposeVersion({ - purposeId: mockPurpose.id, - versionId: mockPurposeVersion.id, - organizationId: consumer.id, - correlationId: generateId(), - logger: genericLogger, - }); + await purposeService.activatePurposeVersion( + { + purposeId: mockPurpose.id, + versionId: mockPurposeVersion.id, + }, + { + authData: getRandomAuthData(consumer.id), + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + } + ); }).rejects.toThrowError(tenantKindNotFound(consumer.id)); }); @@ -607,13 +657,18 @@ describe("activatePurposeVersion", () => { await addOneTenant(mockProducer); expect(async () => { - await purposeService.activatePurposeVersion({ - purposeId: mockPurpose.id, - versionId: mockPurposeVersion.id, - organizationId: mockConsumer.id, - correlationId: generateId(), - logger: genericLogger, - }); + await purposeService.activatePurposeVersion( + { + purposeId: mockPurpose.id, + versionId: mockPurposeVersion.id, + }, + { + authData: getRandomAuthData(mockConsumer.id), + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + } + ); }).rejects.toThrowError(missingRiskAnalysis(mockPurpose.id)); }); @@ -624,13 +679,18 @@ describe("activatePurposeVersion", () => { await addOneTenant(mockProducer); expect(async () => { - await purposeService.activatePurposeVersion({ - purposeId: mockPurpose.id, - versionId: mockPurposeVersion.id, - organizationId: mockConsumer.id, - correlationId: generateId(), - logger: genericLogger, - }); + await purposeService.activatePurposeVersion( + { + purposeId: mockPurpose.id, + versionId: mockPurposeVersion.id, + }, + { + authData: getRandomAuthData(mockConsumer.id), + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + } + ); }).rejects.toThrowError(eserviceNotFound(mockEService.id)); }); @@ -647,13 +707,18 @@ describe("activatePurposeVersion", () => { await addOneTenant(mockProducer); expect(async () => { - await purposeService.activatePurposeVersion({ - purposeId: mockPurpose.id, - versionId: mockPurposeVersion.id, - organizationId: mockConsumer.id, - correlationId: generateId(), - logger: genericLogger, - }); + await purposeService.activatePurposeVersion( + { + purposeId: mockPurpose.id, + versionId: mockPurposeVersion.id, + }, + { + authData: getRandomAuthData(mockConsumer.id), + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + } + ); }).rejects.toThrowError( agreementNotFound(mockEService.id, mockConsumer.id) ); @@ -681,13 +746,18 @@ describe("activatePurposeVersion", () => { await addOneTenant(mockProducer); expect(async () => { - await purposeService.activatePurposeVersion({ - purposeId: mockPurpose.id, - versionId: mockPurposeVersion.id, - organizationId: mockConsumer.id, - correlationId: generateId(), - logger: genericLogger, - }); + await purposeService.activatePurposeVersion( + { + purposeId: mockPurpose.id, + versionId: mockPurposeVersion.id, + }, + { + authData: getRandomAuthData(mockConsumer.id), + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + } + ); }).rejects.toThrowError( agreementNotFound(mockEService.id, mockConsumer.id) ); @@ -705,13 +775,18 @@ describe("activatePurposeVersion", () => { await addOneTenant(anotherTenant); expect(async () => { - await purposeService.activatePurposeVersion({ - purposeId: mockPurpose.id, - versionId: mockPurposeVersion.id, - organizationId: anotherTenant.id, - correlationId: generateId(), - logger: genericLogger, - }); + await purposeService.activatePurposeVersion( + { + purposeId: mockPurpose.id, + versionId: mockPurposeVersion.id, + }, + { + authData: getRandomAuthData(anotherTenant.id), + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + } + ); }).rejects.toThrowError(organizationNotAllowed(anotherTenant.id)); }); @@ -722,24 +797,28 @@ describe("activatePurposeVersion", () => { await addOneTenant(mockConsumer); await addOneTenant(mockProducer); - const delegate = getMockAuthData(); const delegation = getMockDelegation({ + delegatorId: mockProducer.id, kind: delegationKind.delegatedProducer, eserviceId: mockEService.id, - delegateId: delegate.organizationId, state: delegationState.active, }); await addOneDelegation(delegation); expect(async () => { - await purposeService.activatePurposeVersion({ - purposeId: mockPurpose.id, - versionId: mockPurposeVersion.id, - organizationId: mockProducer.id, - correlationId: generateId(), - logger: genericLogger, - }); + await purposeService.activatePurposeVersion( + { + purposeId: mockPurpose.id, + versionId: mockPurposeVersion.id, + }, + { + authData: getRandomAuthData(mockProducer.id), + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + } + ); }).rejects.toThrowError(organizationNotAllowed(mockProducer.id)); }); @@ -754,25 +833,29 @@ describe("activatePurposeVersion", () => { await addOneTenant(mockConsumer); await addOneTenant(mockProducer); - const delegate = getMockAuthData(); const delegation = getMockDelegation({ + delegatorId: mockEService.producerId, kind: delegationKind.delegatedProducer, eserviceId: mockEService.id, - delegateId: delegate.organizationId, state: delegationState, }); await addOneDelegation(delegation); expect(async () => { - await purposeService.activatePurposeVersion({ - purposeId: mockPurpose.id, - versionId: mockPurposeVersion.id, - organizationId: delegate.organizationId, - correlationId: generateId(), - logger: genericLogger, - }); - }).rejects.toThrowError(organizationNotAllowed(delegate.organizationId)); + await purposeService.activatePurposeVersion( + { + purposeId: mockPurpose.id, + versionId: mockPurposeVersion.id, + }, + { + authData: getRandomAuthData(delegation.delegateId), + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + } + ); + }).rejects.toThrowError(organizationNotAllowed(delegation.delegateId)); } ); @@ -794,13 +877,18 @@ describe("activatePurposeVersion", () => { await addOneTenant(mockProducer); expect(async () => { - await purposeService.activatePurposeVersion({ - purposeId: purpose.id, - versionId: mockPurposeVersion.id, - organizationId: mockConsumer.id, - correlationId: generateId(), - logger: genericLogger, - }); + await purposeService.activatePurposeVersion( + { + purposeId: purpose.id, + versionId: mockPurposeVersion.id, + }, + { + authData: getRandomAuthData(mockConsumer.id), + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + } + ); }).rejects.toThrowError(missingRiskAnalysis(purpose.id)); }); @@ -830,13 +918,18 @@ describe("activatePurposeVersion", () => { ); expect(async () => { - await purposeService.activatePurposeVersion({ - purposeId: purpose.id, - versionId: mockPurposeVersion.id, - organizationId: mockConsumer.id, - correlationId: generateId(), - logger: genericLogger, - }); + await purposeService.activatePurposeVersion( + { + purposeId: purpose.id, + versionId: mockPurposeVersion.id, + }, + { + authData: getRandomAuthData(mockConsumer.id), + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + } + ); }).rejects.toThrowError( riskAnalysisValidationFailed( result.type === "invalid" ? result.issues : [] @@ -860,13 +953,18 @@ describe("activatePurposeVersion", () => { await addOneTenant(mockProducer); expect(async () => { - await purposeService.activatePurposeVersion({ - purposeId: mockPurpose.id, - versionId: mockPurposeVersion.id, - organizationId: mockConsumer.id, - correlationId: generateId(), - logger: genericLogger, - }); + await purposeService.activatePurposeVersion( + { + purposeId: mockPurpose.id, + versionId: mockPurposeVersion.id, + }, + { + authData: getRandomAuthData(mockConsumer.id), + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + } + ); }).rejects.toThrowError(tenantNotFound(mockConsumer.id)); }); @@ -877,13 +975,18 @@ describe("activatePurposeVersion", () => { await addOneTenant(mockConsumer); expect(async () => { - await purposeService.activatePurposeVersion({ - purposeId: mockPurpose.id, - versionId: mockPurposeVersion.id, - organizationId: mockProducer.id, - correlationId: generateId(), - logger: genericLogger, - }); + await purposeService.activatePurposeVersion( + { + purposeId: mockPurpose.id, + versionId: mockPurposeVersion.id, + }, + { + authData: getRandomAuthData(mockProducer.id), + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + } + ); }).rejects.toThrowError(tenantNotFound(mockProducer.id)); }); @@ -901,13 +1004,18 @@ describe("activatePurposeVersion", () => { await addOneTenant(mockProducer); expect(async () => { - await purposeService.activatePurposeVersion({ - purposeId: mockPurpose.id, - versionId: mockPurposeVersion.id, - organizationId: mockProducer.id, - correlationId: generateId(), - logger: genericLogger, - }); + await purposeService.activatePurposeVersion( + { + purposeId: mockPurpose.id, + versionId: mockPurposeVersion.id, + }, + { + authData: getRandomAuthData(mockProducer.id), + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + } + ); }).rejects.toThrowError(tenantKindNotFound(consumer.id)); }); @@ -925,13 +1033,18 @@ describe("activatePurposeVersion", () => { await addOneTenant(producer); expect(async () => { - await purposeService.activatePurposeVersion({ - purposeId: mockPurpose.id, - versionId: mockPurposeVersion.id, - organizationId: mockProducer.id, - correlationId: generateId(), - logger: genericLogger, - }); + await purposeService.activatePurposeVersion( + { + purposeId: mockPurpose.id, + versionId: mockPurposeVersion.id, + }, + { + authData: getRandomAuthData(mockProducer.id), + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + } + ); }).rejects.toThrowError(tenantKindNotFound(producer.id)); }); @@ -955,13 +1068,18 @@ describe("activatePurposeVersion", () => { await addOneTenant(mockProducer); expect(async () => { - await purposeService.activatePurposeVersion({ - purposeId: purpose.id, - versionId: purposeVersion.id, - organizationId: mockProducer.id, - correlationId: generateId(), - logger: genericLogger, - }); + await purposeService.activatePurposeVersion( + { + purposeId: purpose.id, + versionId: purposeVersion.id, + }, + { + authData: getRandomAuthData(mockProducer.id), + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + } + ); }).rejects.toThrowError(organizationNotAllowed(mockProducer.id)); } ); @@ -986,13 +1104,18 @@ describe("activatePurposeVersion", () => { await addOneTenant(mockProducer); expect(async () => { - await purposeService.activatePurposeVersion({ - purposeId: purpose.id, - versionId: purposeVersion.id, - organizationId: mockConsumer.id, - correlationId: generateId(), - logger: genericLogger, - }); + await purposeService.activatePurposeVersion( + { + purposeId: purpose.id, + versionId: purposeVersion.id, + }, + { + authData: getRandomAuthData(mockConsumer.id), + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + } + ); }).rejects.toThrowError(organizationNotAllowed(mockConsumer.id)); } ); diff --git a/packages/purpose-process/test/deletePurposeVersion.test.ts b/packages/purpose-process/test/deletePurposeVersion.test.ts index bd17341ee0..ebb0f42cb9 100644 --- a/packages/purpose-process/test/deletePurposeVersion.test.ts +++ b/packages/purpose-process/test/deletePurposeVersion.test.ts @@ -5,6 +5,7 @@ import { getMockPurpose, writeInReadmodel, decodeProtobufPayload, + getRandomAuthData, } from "pagopa-interop-commons-test"; import { purposeVersionState, @@ -52,13 +53,18 @@ describe("deletePurposeVersion", () => { await addOnePurpose(mockPurpose); await writeInReadmodel(toReadModelEService(mockEService), eservices); - await purposeService.deletePurposeVersion({ - purposeId: mockPurpose.id, - versionId: mockPurposeVersion1.id, - organizationId: mockPurpose.consumerId, - correlationId: generateId(), - logger: genericLogger, - }); + await purposeService.deletePurposeVersion( + { + purposeId: mockPurpose.id, + versionId: mockPurposeVersion1.id, + }, + { + authData: getRandomAuthData(mockPurpose.consumerId), + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + } + ); const writtenEvent = await readLastPurposeEvent(mockPurpose.id); @@ -98,13 +104,18 @@ describe("deletePurposeVersion", () => { await writeInReadmodel(toReadModelEService(mockEService), eservices); expect( - purposeService.deletePurposeVersion({ - purposeId: randomId, - versionId: mockPurposeVersion.id, - organizationId: mockEService.producerId, - correlationId: generateId(), - logger: genericLogger, - }) + purposeService.deletePurposeVersion( + { + purposeId: randomId, + versionId: mockPurposeVersion.id, + }, + { + authData: getRandomAuthData(mockEService.producerId), + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + } + ) ).rejects.toThrowError(purposeNotFound(randomId)); }); it("should throw purposeVersionNotFound if the purpose version doesn't exist", async () => { @@ -120,13 +131,18 @@ describe("deletePurposeVersion", () => { await writeInReadmodel(toReadModelEService(mockEService), eservices); expect( - purposeService.deletePurposeVersion({ - purposeId: mockPurpose.id, - versionId: randomVersionId, - organizationId: mockPurpose.consumerId, - correlationId: generateId(), - logger: genericLogger, - }) + purposeService.deletePurposeVersion( + { + purposeId: mockPurpose.id, + versionId: randomVersionId, + }, + { + authData: getRandomAuthData(mockPurpose.consumerId), + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + } + ) ).rejects.toThrowError( purposeVersionNotFound(mockPurpose.id, randomVersionId) ); @@ -147,13 +163,18 @@ describe("deletePurposeVersion", () => { await writeInReadmodel(toReadModelEService(mockEService), eservices); expect( - purposeService.deletePurposeVersion({ - purposeId: mockPurpose.id, - versionId: mockPurposeVersion.id, - organizationId: mockEService.producerId, - correlationId: generateId(), - logger: genericLogger, - }) + purposeService.deletePurposeVersion( + { + purposeId: mockPurpose.id, + versionId: mockPurposeVersion.id, + }, + { + authData: getRandomAuthData(mockEService.producerId), + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + } + ) ).rejects.toThrowError( organizationIsNotTheConsumer(mockEService.producerId) ); @@ -180,13 +201,18 @@ describe("deletePurposeVersion", () => { await writeInReadmodel(toReadModelEService(mockEService), eservices); expect( - purposeService.deletePurposeVersion({ - purposeId: mockPurpose.id, - versionId: mockPurposeVersion.id, - organizationId: mockPurpose.consumerId, - correlationId: generateId(), - logger: genericLogger, - }) + purposeService.deletePurposeVersion( + { + purposeId: mockPurpose.id, + versionId: mockPurposeVersion.id, + }, + { + authData: getRandomAuthData(mockPurpose.consumerId), + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + } + ) ).rejects.toThrowError( purposeVersionCannotBeDeleted(mockPurpose.id, mockPurposeVersion.id) ); @@ -207,13 +233,18 @@ describe("deletePurposeVersion", () => { await writeInReadmodel(toReadModelEService(mockEService), eservices); expect( - purposeService.deletePurposeVersion({ - purposeId: mockPurpose.id, - versionId: mockPurposeVersion.id, - organizationId: mockPurpose.consumerId, - correlationId: generateId(), - logger: genericLogger, - }) + purposeService.deletePurposeVersion( + { + purposeId: mockPurpose.id, + versionId: mockPurposeVersion.id, + }, + { + authData: getRandomAuthData(mockPurpose.consumerId), + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + } + ) ).rejects.toThrowError( purposeVersionCannotBeDeleted(mockPurpose.id, mockPurposeVersion.id) ); diff --git a/packages/purpose-process/test/suspendPurposeVersion.test.ts b/packages/purpose-process/test/suspendPurposeVersion.test.ts index b0f715932a..90b2ad80b9 100644 --- a/packages/purpose-process/test/suspendPurposeVersion.test.ts +++ b/packages/purpose-process/test/suspendPurposeVersion.test.ts @@ -5,8 +5,8 @@ import { getMockPurpose, writeInReadmodel, decodeProtobufPayload, - getMockAuthData, getMockDelegation, + getRandomAuthData, } from "pagopa-interop-commons-test"; import { PurposeVersion, @@ -58,13 +58,18 @@ describe("suspendPurposeVersion", () => { await addOnePurpose(mockPurpose); await writeInReadmodel(toReadModelEService(mockEService), eservices); - const returnedPurposeVersion = await purposeService.suspendPurposeVersion({ - purposeId: mockPurpose.id, - versionId: mockPurposeVersion1.id, - organizationId: mockPurpose.consumerId, - correlationId: generateId(), - logger: genericLogger, - }); + const returnedPurposeVersion = await purposeService.suspendPurposeVersion( + { + purposeId: mockPurpose.id, + versionId: mockPurposeVersion1.id, + }, + { + authData: getRandomAuthData(mockPurpose.consumerId), + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + } + ); const writtenEvent = await readLastPurposeEvent(mockPurpose.id); @@ -120,13 +125,18 @@ describe("suspendPurposeVersion", () => { await addOnePurpose(mockPurpose); await writeInReadmodel(toReadModelEService(mockEService), eservices); - const returnedPurposeVersion = await purposeService.suspendPurposeVersion({ - purposeId: mockPurpose.id, - versionId: mockPurposeVersion1.id, - organizationId: mockEService.producerId, - correlationId: generateId(), - logger: genericLogger, - }); + const returnedPurposeVersion = await purposeService.suspendPurposeVersion( + { + purposeId: mockPurpose.id, + versionId: mockPurposeVersion1.id, + }, + { + authData: getRandomAuthData(mockEService.producerId), + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + } + ); const writtenEvent = await readLastPurposeEvent(mockPurpose.id); @@ -182,23 +192,27 @@ describe("suspendPurposeVersion", () => { await addOnePurpose(mockPurpose); await writeInReadmodel(toReadModelEService(mockEService), eservices); - const delegate = getMockAuthData(); const delegation = getMockDelegation({ + delegatorId: mockEService.producerId, kind: delegationKind.delegatedProducer, eserviceId: mockEService.id, - delegateId: delegate.organizationId, state: delegationState.active, }); await writeInReadmodel(delegation, delegations); - const returnedPurposeVersion = await purposeService.suspendPurposeVersion({ - purposeId: mockPurpose.id, - versionId: mockPurposeVersion1.id, - organizationId: delegate.organizationId, - correlationId: generateId(), - logger: genericLogger, - }); + const returnedPurposeVersion = await purposeService.suspendPurposeVersion( + { + purposeId: mockPurpose.id, + versionId: mockPurposeVersion1.id, + }, + { + authData: getRandomAuthData(delegation.delegateId), + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + } + ); const writtenEvent = await readLastPurposeEvent(mockPurpose.id); @@ -255,13 +269,18 @@ describe("suspendPurposeVersion", () => { await addOnePurpose(mockPurpose); await writeInReadmodel(toReadModelEService(mockEService), eservices); - const returnedPurposeVersion = await purposeService.suspendPurposeVersion({ - purposeId: mockPurpose.id, - versionId: mockPurposeVersion1.id, - organizationId: mockEService.producerId, - correlationId: generateId(), - logger: genericLogger, - }); + const returnedPurposeVersion = await purposeService.suspendPurposeVersion( + { + purposeId: mockPurpose.id, + versionId: mockPurposeVersion1.id, + }, + { + authData: getRandomAuthData(mockEService.producerId), + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + } + ); const writtenEvent = await readLastPurposeEvent(mockPurpose.id); @@ -307,13 +326,18 @@ describe("suspendPurposeVersion", () => { await addOnePurpose(mockPurpose); expect( - purposeService.suspendPurposeVersion({ - purposeId: randomPurposeId, - versionId: randomVersionId, - organizationId: generateId(), - correlationId: generateId(), - logger: genericLogger, - }) + purposeService.suspendPurposeVersion( + { + purposeId: randomPurposeId, + versionId: randomVersionId, + }, + { + authData: getRandomAuthData(), + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + } + ) ).rejects.toThrowError(purposeNotFound(randomPurposeId)); }); it("should throw purposeVersionNotFound if the purpose version doesn't exist", async () => { @@ -330,20 +354,25 @@ describe("suspendPurposeVersion", () => { await writeInReadmodel(toReadModelEService(mockEService), eservices); expect( - purposeService.suspendPurposeVersion({ - purposeId: mockPurpose.id, - versionId: randomVersionId, - organizationId: mockPurpose.consumerId, - correlationId: generateId(), - logger: genericLogger, - }) + purposeService.suspendPurposeVersion( + { + purposeId: mockPurpose.id, + versionId: randomVersionId, + }, + { + authData: getRandomAuthData(mockPurpose.consumerId), + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + } + ) ).rejects.toThrowError( purposeVersionNotFound(mockPurpose.id, randomVersionId) ); }); it("should throw organizationNotAllowed if the requester is not the producer nor the consumer", async () => { const mockEService = getMockEService(); - const randomId: TenantId = generateId(); + const randomAuthData = getRandomAuthData(); const mockPurposeVersion: PurposeVersion = { ...getMockPurposeVersion(), state: purposeVersionState.active, @@ -358,14 +387,21 @@ describe("suspendPurposeVersion", () => { await writeInReadmodel(toReadModelEService(mockEService), eservices); expect( - purposeService.suspendPurposeVersion({ - purposeId: mockPurpose.id, - versionId: mockPurposeVersion.id, - organizationId: randomId, - correlationId: generateId(), - logger: genericLogger, - }) - ).rejects.toThrowError(organizationNotAllowed(randomId)); + purposeService.suspendPurposeVersion( + { + purposeId: mockPurpose.id, + versionId: mockPurposeVersion.id, + }, + { + authData: randomAuthData, + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + } + ) + ).rejects.toThrowError( + organizationNotAllowed(randomAuthData.organizationId) + ); }); it("should throw organizationNotAllowed if the requester is not the e-service active delegation delegate", async () => { const mockEService = getMockEService(); @@ -381,26 +417,32 @@ describe("suspendPurposeVersion", () => { await addOnePurpose(mockPurpose); await writeInReadmodel(toReadModelEService(mockEService), eservices); - const delegate = getMockAuthData(); + const delegateId = generateId(); const delegation = getMockDelegation({ + delegatorId: mockEService.producerId, kind: delegationKind.delegatedProducer, eserviceId: mockEService.id, - delegateId: delegate.organizationId, + delegateId, state: delegationState.active, }); await writeInReadmodel(delegation, delegations); - const randomCaller = getMockAuthData(); + const randomCaller = getRandomAuthData(); expect( - purposeService.suspendPurposeVersion({ - purposeId: mockPurpose.id, - versionId: mockPurposeVersion.id, - organizationId: randomCaller.organizationId, - correlationId: generateId(), - logger: genericLogger, - }) + purposeService.suspendPurposeVersion( + { + purposeId: mockPurpose.id, + versionId: mockPurposeVersion.id, + }, + { + authData: randomCaller, + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + } + ) ).rejects.toThrowError(organizationNotAllowed(randomCaller.organizationId)); }); it.each( @@ -421,25 +463,33 @@ describe("suspendPurposeVersion", () => { await addOnePurpose(mockPurpose); await writeInReadmodel(toReadModelEService(mockEService), eservices); - const delegate = getMockAuthData(); + const delegateAuthData = getRandomAuthData(); const delegation = getMockDelegation({ + delegatorId: mockEService.producerId, kind: delegationKind.delegatedProducer, eserviceId: mockEService.id, - delegateId: delegate.organizationId, + delegateId: delegateAuthData.organizationId, state: delegationState, }); await writeInReadmodel(delegation, delegations); expect( - purposeService.suspendPurposeVersion({ - purposeId: mockPurpose.id, - versionId: mockPurposeVersion.id, - organizationId: delegate.organizationId, - correlationId: generateId(), - logger: genericLogger, - }) - ).rejects.toThrowError(organizationNotAllowed(delegate.organizationId)); + purposeService.suspendPurposeVersion( + { + purposeId: mockPurpose.id, + versionId: mockPurposeVersion.id, + }, + { + authData: delegateAuthData, + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + } + ) + ).rejects.toThrowError( + organizationNotAllowed(delegateAuthData.organizationId) + ); } ); it("should throw organizationNotAllowed if the requester is the producer but the purpose e-service has an active delegation", async () => { @@ -456,24 +506,30 @@ describe("suspendPurposeVersion", () => { await addOnePurpose(mockPurpose); await writeInReadmodel(toReadModelEService(mockEService), eservices); - const delegate = getMockAuthData(); + const delegateId = generateId(); const delegation = getMockDelegation({ + delegatorId: mockEService.producerId, kind: delegationKind.delegatedProducer, eserviceId: mockEService.id, - delegateId: delegate.organizationId, + delegateId, state: delegationState.active, }); await writeInReadmodel(delegation, delegations); expect( - purposeService.suspendPurposeVersion({ - purposeId: mockPurpose.id, - versionId: mockPurposeVersion.id, - organizationId: mockEService.producerId, - correlationId: generateId(), - logger: genericLogger, - }) + purposeService.suspendPurposeVersion( + { + purposeId: mockPurpose.id, + versionId: mockPurposeVersion.id, + }, + { + authData: getRandomAuthData(mockEService.producerId), + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + } + ) ).rejects.toThrowError(organizationNotAllowed(mockEService.producerId)); }); it.each( @@ -498,13 +554,18 @@ describe("suspendPurposeVersion", () => { await writeInReadmodel(toReadModelEService(mockEService), eservices); expect( - purposeService.suspendPurposeVersion({ - purposeId: mockPurpose.id, - versionId: mockPurposeVersion.id, - organizationId: mockPurpose.consumerId, - correlationId: generateId(), - logger: genericLogger, - }) + purposeService.suspendPurposeVersion( + { + purposeId: mockPurpose.id, + versionId: mockPurposeVersion.id, + }, + { + authData: getRandomAuthData(mockPurpose.consumerId), + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + } + ) ).rejects.toThrowError( notValidVersionState(mockPurposeVersion.id, mockPurposeVersion.state) ); From 9c2e428b3c2123c89d479e71df39fff5d51b6cf9 Mon Sep 17 00:00:00 2001 From: AsterITA Date: Mon, 13 Jan 2025 12:28:09 +0100 Subject: [PATCH 17/23] fix tests --- packages/purpose-process/src/services/purposeService.ts | 7 ++++++- packages/purpose-process/test/getPurposes.test.ts | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index 1f8a8d2b62..b13672a67d 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -243,6 +243,11 @@ export function purposeServiceBuilder( readModelService ); + const tenantKind = await retrieveTenantKind( + organizationId, + readModelService + ); + const isAllowedToRetrieveRiskAnalysis = await assertRequesterIsAllowedToRetrieveRiskAnalysisDocument( purpose.data, @@ -265,7 +270,7 @@ export function purposeServiceBuilder( ? isRiskAnalysisFormValid( purpose.data.riskAnalysisForm, false, - await retrieveTenantKind(organizationId, readModelService) + tenantKind ) : true; diff --git a/packages/purpose-process/test/getPurposes.test.ts b/packages/purpose-process/test/getPurposes.test.ts index a2ee002bde..da22824a20 100644 --- a/packages/purpose-process/test/getPurposes.test.ts +++ b/packages/purpose-process/test/getPurposes.test.ts @@ -642,6 +642,7 @@ describe("getPurposes", async () => { await addOneTenant(delegate); const delegation = getMockDelegation({ + delegatorId: producerId, delegateId, state: delegationState.active, eserviceId: eservice.id, From b6b6c104329e1a2f9c05113b1fbf3d35d04e8157 Mon Sep 17 00:00:00 2001 From: Roberto Gregnanin <128478646+rGregnanin@users.noreply.github.com> Date: Tue, 14 Jan 2025 12:00:19 +0100 Subject: [PATCH 18/23] added delegation tests --- .../purpose-process/test/getPurposes.test.ts | 132 ++++++++++++++++++ 1 file changed, 132 insertions(+) diff --git a/packages/purpose-process/test/getPurposes.test.ts b/packages/purpose-process/test/getPurposes.test.ts index da22824a20..e141e58330 100644 --- a/packages/purpose-process/test/getPurposes.test.ts +++ b/packages/purpose-process/test/getPurposes.test.ts @@ -1,8 +1,10 @@ import { + Agreement, EService, EServiceId, Purpose, TenantId, + agreementState, delegationKind, delegationState, generateId, @@ -19,9 +21,12 @@ import { getMockValidRiskAnalysisForm, getMockDelegation, getMockTenant, + addSomeRandomDelegations, + getMockAgreement, } from "pagopa-interop-commons-test/index.js"; import { genericLogger } from "pagopa-interop-commons"; import { + addOneAgreement, addOneDelegation, addOneEService, addOnePurpose, @@ -526,6 +531,133 @@ describe("getPurposes", async () => { expect(result.totalCount).toBe(0); expect(result.results).toEqual([]); }); + it("should get the purposes if the requester is an e-service delegated consumer", async () => { + const delegation = getMockDelegation({ + kind: delegationKind.delegatedConsumer, + eserviceId: mockPurpose1.eserviceId, + delegatorId: mockPurpose1.consumerId, + delegateId: consumerId1, + state: delegationState.active, + }); + + await addOneDelegation(delegation); + + const result = await purposeService.getPurposes( + delegation.delegateId, + { + eservicesIds: [], + consumersIds: [], + producersIds: [], + states: [], + excludeDraft: undefined, + }, + { offset: 0, limit: 50 }, + genericLogger + ); + expect(result.totalCount).toBe(7); + expect(result.results).toEqual([ + mockPurpose1, + mockPurpose2, + mockPurpose3, + mockPurpose4, + mockPurpose5, + mockPurpose6, + mockPurpose7, + ]); + }); + it("should succeed when requester is Consumer Delegate and the eservice was created by a delegated tenant and you should get the purposes", async () => { + const producer = { + ...getMockTenant(), + id: generateId(), + kind: tenantKind.PA, + }; + const producerDelegate = { + ...getMockTenant(), + id: generateId(), + kind: tenantKind.PA, + }; + const consumer = { + ...getMockTenant(), + id: generateId(), + kind: tenantKind.PA, + }; + const consumerDelegate = { + ...getMockTenant(), + id: generateId(), + kind: tenantKind.PA, + }; + + const eservice: EService = { + ...getMockEService(), + producerId: producer.id, + }; + const agreement: Agreement = { + ...getMockAgreement(), + producerId: producer.id, + consumerId: consumer.id, + eserviceId: eservice.id, + state: agreementState.active, + }; + + const delegatePurpose: Purpose = { + ...getMockPurpose(), + consumerId: consumer.id, + eserviceId: eservice.id, + versions: [getMockPurposeVersion(purposeVersionState.active)], + }; + + const producerDelegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, + eserviceId: eservice.id, + delegatorId: producer.id, + delegateId: producerDelegate.id, + state: delegationState.active, + }); + + const consumerDelegation = getMockDelegation({ + kind: delegationKind.delegatedConsumer, + eserviceId: eservice.id, + delegatorId: consumer.id, + delegateId: consumerDelegate.id, + state: delegationState.active, + }); + + await addOneTenant(producerDelegate); + await addOneTenant(producer); + await addOneTenant(consumerDelegate); + await addOneTenant(consumer); + await addOneEService(eservice); + await addOneAgreement(agreement); + await addOnePurpose(delegatePurpose); + await addOneDelegation(producerDelegation); + await addOneDelegation(consumerDelegation); + await addSomeRandomDelegations(delegatePurpose, addOneDelegation); + + const result = await purposeService.getPurposes( + consumerDelegation.delegateId, + { + eservicesIds: [], + consumersIds: [], + producersIds: [], + states: [], + excludeDraft: undefined, + }, + { offset: 0, limit: 50 }, + genericLogger + ); + + expect(result.totalCount).toBe(8); + expect(result.results).toEqual([ + mockPurpose1, + delegatePurpose, + mockPurpose2, + mockPurpose3, + mockPurpose4, + mockPurpose5, + mockPurpose6, + mockPurpose7, + ]); + }); describe("Producer Delegation active for provided producerIds filter", async () => { it("should get the purposes if they exist (parameters: producersIds with only delegateId)", async () => { From 85a22053cf3af8c4f36a37b5ce90a9340f006165 Mon Sep 17 00:00:00 2001 From: Roberto Gregnanin <128478646+rGregnanin@users.noreply.github.com> Date: Wed, 15 Jan 2025 15:36:37 +0100 Subject: [PATCH 19/23] WIP --- .../src/services/purposeService.ts | 24 +++++++++-- .../src/services/validators.ts | 12 ++++++ .../test/updatePurpose.test.ts | 40 +++++++++++++------ 3 files changed, 59 insertions(+), 17 deletions(-) diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index 501a102c24..4ddf7b6a17 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -44,6 +44,7 @@ import { RiskAnalysisId, RiskAnalysis, CorrelationId, + DelegationId, } from "pagopa-interop-models"; import { purposeApi } from "pagopa-interop-api-clients"; import { P, match } from "ts-pattern"; @@ -1454,13 +1455,28 @@ const performUpdatePurpose = async ( }); } + console.log("purpose.data", purpose.data); + + console.log("authData", authData); + + console.log("purpose.data.delegationId", purpose.data.delegationId); + + const degId = purpose.data.delegationId + ? purpose.data.delegationId + : ("123456" as DelegationId); + + const x = await readModelService.getActiveConsumerDelegationByDelegationId( + degId + ); + + console.log("x", x); + + console.log(); + assertRequesterCanActAsConsumer( purpose.data, authData, - purpose.data.delegationId && - (await readModelService.getActiveConsumerDelegationByDelegationId( - purpose.data.delegationId - )) + purpose.data.delegationId && x ); const eservice = await retrieveEService( diff --git a/packages/purpose-process/src/services/validators.ts b/packages/purpose-process/src/services/validators.ts index 45b5b8ac31..53f7184c3d 100644 --- a/packages/purpose-process/src/services/validators.ts +++ b/packages/purpose-process/src/services/validators.ts @@ -350,6 +350,8 @@ export const assertRequesterCanActAsConsumer = ( authData: AuthData, activeConsumerDelegation: Delegation | undefined ): void => { + console.log("activeConsumerDelegation", activeConsumerDelegation); + if (!activeConsumerDelegation) { // No active consumer delegation, the requester is authorized only if they are the consumer assertRequesterIsConsumer(purpose, authData); @@ -368,6 +370,16 @@ const assertRequesterIsDelegateConsumer = ( authData: Pick, activeConsumerDelegation: Delegation | undefined ): void => { + console.log( + "AuthData in assertRequesterIsDelegateConsumer", + authData.organizationId + ); + + console.log( + "activeConsumerDelegation in assertRequesterIsDelegateConsumer", + activeConsumerDelegation?.delegateId + ); + if ( activeConsumerDelegation?.delegateId !== authData.organizationId || activeConsumerDelegation?.delegatorId !== purpose.consumerId || diff --git a/packages/purpose-process/test/updatePurpose.test.ts b/packages/purpose-process/test/updatePurpose.test.ts index f8cf1ff180..82583b3444 100644 --- a/packages/purpose-process/test/updatePurpose.test.ts +++ b/packages/purpose-process/test/updatePurpose.test.ts @@ -54,6 +54,7 @@ import { riskAnalysisValidationFailed, duplicatedPurposeTitle, organizationNotAllowed, + organizationIsNotTheDelegatedConsumer, } from "../src/model/domain/errors.js"; import { getMockEService, @@ -271,7 +272,7 @@ describe("updatePurpose and updateReversePurpose", () => { expect(isRiskAnalysisValid).toBe(true); }); it("should succeed when requester is Consumer Delegate and the Purpose is in a updatable state and the e-service is in mode DELIVER", async () => { - const authData = getRandomAuthData(); + const authData = getRandomAuthData(purposeForDeliver.consumerId); const delegation = getMockDelegation({ kind: delegationKind.delegatedConsumer, @@ -329,7 +330,7 @@ describe("updatePurpose and updateReversePurpose", () => { expect(isRiskAnalysisValid).toBe(true); }); it("should succeed when requester is Consumer Delegate and the Purpose is in a updatable state and the e-service is in mode RECEIVE", async () => { - const authData = getRandomAuthData(); + const authData = getRandomAuthData(purposeForReceive.consumerId); const delegation = getMockDelegation({ kind: delegationKind.delegatedConsumer, @@ -456,7 +457,7 @@ describe("updatePurpose and updateReversePurpose", () => { delegatePurpose.id, updateContentWithoutTitle, { - authData: getRandomAuthData(consumerDelegate.id), + authData: getRandomAuthData(consumer.id), correlationId: generateId(), logger: genericLogger, serviceName: "", @@ -864,31 +865,44 @@ describe("updatePurpose and updateReversePurpose", () => { riskAnalysisValidationFailed([unexpectedRulesVersionError("0")]) ); }); - it("should throw organizationNotAllowed when the requester is the Consumer but there is a Consumer Delegation in updatePurpose", async () => { - const authData = getRandomAuthData(); - const purpose = { - ...purposeForDeliver, - consumerId: authData.organizationId, - }; + it.only("should throw organizationNotAllowed when the requester is the Consumer but there is a Consumer Delegation in updatePurpose", async () => { + const authData = getRandomAuthData(tenant.id); + const delegation = getMockDelegation({ kind: delegationKind.delegatedConsumer, - eserviceId: purpose.eserviceId, - delegatorId: purpose.consumerId, + eserviceId: eServiceDeliver.id, + delegatorId: tenant.id, delegateId: generateId(), state: delegationState.active, }); + + console.log(delegation); + + const purpose = { + ...purposeForDeliver, + consumerId: tenant.id, + delegation: delegation.id, + }; + + console.log("purpose in test", purpose); + await addOnePurpose(purpose); await addOneDelegation(delegation); await writeInReadmodel(toReadModelEService(eServiceDeliver), eservices); await writeInReadmodel(toReadModelTenant(tenant), tenants); expect( - purposeService.updatePurpose(purposeForDeliver.id, purposeUpdateContent, { + purposeService.updatePurpose(purpose.id, purposeUpdateContent, { authData, correlationId: generateId(), logger: genericLogger, serviceName: "", }) - ).rejects.toThrowError(organizationNotAllowed(authData.organizationId)); + ).rejects.toThrowError( + organizationIsNotTheDelegatedConsumer( + authData.organizationId, + delegation.id + ) + ); }); it("should throw organizationNotAllowed when the requester is the Consumer but there is a Consumer Delegation in updateReversePurpose", async () => { const authData = getRandomAuthData(); From 1370f3d3dadb32d17cc8a183e321482c940f7c26 Mon Sep 17 00:00:00 2001 From: Roberto Gregnanin <128478646+rGregnanin@users.noreply.github.com> Date: Thu, 16 Jan 2025 12:07:35 +0100 Subject: [PATCH 20/23] fix --- .../src/services/purposeService.ts | 24 +-- .../src/services/readModelService.ts | 4 +- .../src/services/validators.ts | 12 -- .../test/updatePurpose.test.ts | 159 +++++++++++++----- 4 files changed, 123 insertions(+), 76 deletions(-) diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index 4ddf7b6a17..501a102c24 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -44,7 +44,6 @@ import { RiskAnalysisId, RiskAnalysis, CorrelationId, - DelegationId, } from "pagopa-interop-models"; import { purposeApi } from "pagopa-interop-api-clients"; import { P, match } from "ts-pattern"; @@ -1455,28 +1454,13 @@ const performUpdatePurpose = async ( }); } - console.log("purpose.data", purpose.data); - - console.log("authData", authData); - - console.log("purpose.data.delegationId", purpose.data.delegationId); - - const degId = purpose.data.delegationId - ? purpose.data.delegationId - : ("123456" as DelegationId); - - const x = await readModelService.getActiveConsumerDelegationByDelegationId( - degId - ); - - console.log("x", x); - - console.log(); - assertRequesterCanActAsConsumer( purpose.data, authData, - purpose.data.delegationId && x + purpose.data.delegationId && + (await readModelService.getActiveConsumerDelegationByDelegationId( + purpose.data.delegationId + )) ); const eservice = await retrieveEService( diff --git a/packages/purpose-process/src/services/readModelService.ts b/packages/purpose-process/src/services/readModelService.ts index 26854e5e10..c7700a95e6 100644 --- a/packages/purpose-process/src/services/readModelService.ts +++ b/packages/purpose-process/src/services/readModelService.ts @@ -425,10 +425,10 @@ export function readModelServiceBuilder( delegationId: DelegationId ): Promise { return getDelegation(delegations, { - "data.delegationId": delegationId, + "data.id": delegationId, "data.state": delegationState.active, "data.kind": delegationKind.delegatedConsumer, - }); + } satisfies ReadModelFilter); }, }; } diff --git a/packages/purpose-process/src/services/validators.ts b/packages/purpose-process/src/services/validators.ts index 53f7184c3d..45b5b8ac31 100644 --- a/packages/purpose-process/src/services/validators.ts +++ b/packages/purpose-process/src/services/validators.ts @@ -350,8 +350,6 @@ export const assertRequesterCanActAsConsumer = ( authData: AuthData, activeConsumerDelegation: Delegation | undefined ): void => { - console.log("activeConsumerDelegation", activeConsumerDelegation); - if (!activeConsumerDelegation) { // No active consumer delegation, the requester is authorized only if they are the consumer assertRequesterIsConsumer(purpose, authData); @@ -370,16 +368,6 @@ const assertRequesterIsDelegateConsumer = ( authData: Pick, activeConsumerDelegation: Delegation | undefined ): void => { - console.log( - "AuthData in assertRequesterIsDelegateConsumer", - authData.organizationId - ); - - console.log( - "activeConsumerDelegation in assertRequesterIsDelegateConsumer", - activeConsumerDelegation?.delegateId - ); - if ( activeConsumerDelegation?.delegateId !== authData.organizationId || activeConsumerDelegation?.delegatorId !== purpose.consumerId || diff --git a/packages/purpose-process/test/updatePurpose.test.ts b/packages/purpose-process/test/updatePurpose.test.ts index 82583b3444..2e332ac62d 100644 --- a/packages/purpose-process/test/updatePurpose.test.ts +++ b/packages/purpose-process/test/updatePurpose.test.ts @@ -39,6 +39,7 @@ import { delegationKind, delegationState, Agreement, + DelegationId, } from "pagopa-interop-models"; import { purposeApi } from "pagopa-interop-api-clients"; import { describe, it, expect, vi, beforeAll, afterAll } from "vitest"; @@ -53,7 +54,6 @@ import { tenantKindNotFound, riskAnalysisValidationFailed, duplicatedPurposeTitle, - organizationNotAllowed, organizationIsNotTheDelegatedConsumer, } from "../src/model/domain/errors.js"; import { @@ -272,29 +272,35 @@ describe("updatePurpose and updateReversePurpose", () => { expect(isRiskAnalysisValid).toBe(true); }); it("should succeed when requester is Consumer Delegate and the Purpose is in a updatable state and the e-service is in mode DELIVER", async () => { - const authData = getRandomAuthData(purposeForDeliver.consumerId); + const authData = getRandomAuthData(); + + const delegatePurpose: Purpose = { + ...purposeForDeliver, + delegationId: generateId(), + }; const delegation = getMockDelegation({ + id: delegatePurpose.delegationId, kind: delegationKind.delegatedConsumer, - eserviceId: purposeForDeliver.eserviceId, - delegatorId: purposeForDeliver.consumerId, + eserviceId: delegatePurpose.eserviceId, + delegatorId: delegatePurpose.consumerId, delegateId: authData.organizationId, state: delegationState.active, }); - await addOnePurpose(purposeForDeliver); + await addOnePurpose(delegatePurpose); await addOneDelegation(delegation); - await addSomeRandomDelegations(purposeForDeliver, addOneDelegation); + await addSomeRandomDelegations(delegatePurpose, addOneDelegation); await writeInReadmodel(toReadModelEService(eServiceDeliver), eservices); await writeInReadmodel(toReadModelTenant(tenant), tenants); const updateContentWithoutTitle = { ...purposeUpdateContent, - title: purposeForDeliver.title, + title: delegatePurpose.title, }; const { purpose, isRiskAnalysisValid } = await purposeService.updatePurpose( - purposeForDeliver.id, + delegatePurpose.id, updateContentWithoutTitle, { authData, @@ -304,10 +310,10 @@ describe("updatePurpose and updateReversePurpose", () => { } ); - const writtenEvent = await readLastPurposeEvent(purposeForDeliver.id); + const writtenEvent = await readLastPurposeEvent(delegatePurpose.id); expect(writtenEvent).toMatchObject({ - stream_id: purposeForDeliver.id, + stream_id: delegatePurpose.id, version: "1", type: "DraftPurposeUpdated", event_version: 2, @@ -319,7 +325,7 @@ describe("updatePurpose and updateReversePurpose", () => { }); const expectedPurpose: Purpose = createUpdatedPurpose( - purposeForDeliver, + delegatePurpose, updateContentWithoutTitle, validRiskAnalysis, writtenPayload.purpose!.riskAnalysisForm! @@ -330,9 +336,15 @@ describe("updatePurpose and updateReversePurpose", () => { expect(isRiskAnalysisValid).toBe(true); }); it("should succeed when requester is Consumer Delegate and the Purpose is in a updatable state and the e-service is in mode RECEIVE", async () => { - const authData = getRandomAuthData(purposeForReceive.consumerId); + const authData = getRandomAuthData(); + + const delegatePurpose: Purpose = { + ...purposeForReceive, + delegationId: generateId(), + }; const delegation = getMockDelegation({ + id: delegatePurpose.delegationId, kind: delegationKind.delegatedConsumer, eserviceId: purposeForReceive.eserviceId, delegatorId: purposeForReceive.consumerId, @@ -340,7 +352,7 @@ describe("updatePurpose and updateReversePurpose", () => { state: delegationState.active, }); - await addOnePurpose(purposeForReceive); + await addOnePurpose(delegatePurpose); await addOneDelegation(delegation); await addSomeRandomDelegations(purposeForDeliver, addOneDelegation); await writeInReadmodel(toReadModelEService(eServiceReceive), eservices); @@ -348,7 +360,7 @@ describe("updatePurpose and updateReversePurpose", () => { const { purpose, isRiskAnalysisValid } = await purposeService.updateReversePurpose( - purposeForReceive.id, + delegatePurpose.id, reversePurposeUpdateContent, { authData, @@ -358,9 +370,9 @@ describe("updatePurpose and updateReversePurpose", () => { } ); - const writtenEvent = await readLastPurposeEvent(purposeForReceive.id); + const writtenEvent = await readLastPurposeEvent(delegatePurpose.id); expect(writtenEvent).toMatchObject({ - stream_id: purposeForReceive.id, + stream_id: delegatePurpose.id, version: "1", type: "DraftPurposeUpdated", event_version: 2, @@ -372,7 +384,7 @@ describe("updatePurpose and updateReversePurpose", () => { }); const expectedPurpose: Purpose = createUpdatedPurpose( - purposeForReceive, + delegatePurpose, reversePurposeUpdateContent, validRiskAnalysis, writtenPayload.purpose!.riskAnalysisForm! @@ -865,33 +877,30 @@ describe("updatePurpose and updateReversePurpose", () => { riskAnalysisValidationFailed([unexpectedRulesVersionError("0")]) ); }); - it.only("should throw organizationNotAllowed when the requester is the Consumer but there is a Consumer Delegation in updatePurpose", async () => { - const authData = getRandomAuthData(tenant.id); + it("should throw organizationIsNotTheDelegatedConsumer when the requester is the Consumer but there is a Consumer Delegation in updatePurpose", async () => { + const authData = getRandomAuthData(); + + const delegatePurpose: Purpose = { + ...purposeForDeliver, + consumerId: tenant.id, + delegationId: generateId(), + }; const delegation = getMockDelegation({ + id: delegatePurpose.delegationId, kind: delegationKind.delegatedConsumer, - eserviceId: eServiceDeliver.id, - delegatorId: tenant.id, + eserviceId: delegatePurpose.eserviceId, + delegatorId: delegatePurpose.consumerId, delegateId: generateId(), state: delegationState.active, }); - console.log(delegation); - - const purpose = { - ...purposeForDeliver, - consumerId: tenant.id, - delegation: delegation.id, - }; - - console.log("purpose in test", purpose); - - await addOnePurpose(purpose); + await addOnePurpose(delegatePurpose); await addOneDelegation(delegation); await writeInReadmodel(toReadModelEService(eServiceDeliver), eservices); await writeInReadmodel(toReadModelTenant(tenant), tenants); expect( - purposeService.updatePurpose(purpose.id, purposeUpdateContent, { + purposeService.updatePurpose(delegatePurpose.id, purposeUpdateContent, { authData, correlationId: generateId(), logger: genericLogger, @@ -904,27 +913,32 @@ describe("updatePurpose and updateReversePurpose", () => { ) ); }); - it("should throw organizationNotAllowed when the requester is the Consumer but there is a Consumer Delegation in updateReversePurpose", async () => { + it("should throw organizationIsNotTheDelegatedConsumer when the requester is the Consumer but there is a Consumer Delegation in updateReversePurpose", async () => { const authData = getRandomAuthData(); - const purpose = { - ...purposeForDeliver, - consumerId: authData.organizationId, + + const delegatePurpose: Purpose = { + ...purposeForReceive, + consumerId: tenant.id, + delegationId: generateId(), }; + const delegation = getMockDelegation({ + id: delegatePurpose.delegationId, kind: delegationKind.delegatedConsumer, - eserviceId: purpose.eserviceId, - delegatorId: purpose.consumerId, + eserviceId: delegatePurpose.eserviceId, + delegatorId: delegatePurpose.consumerId, delegateId: generateId(), state: delegationState.active, }); - await addOnePurpose(purpose); + + await addOnePurpose(delegatePurpose); await addOneDelegation(delegation); await writeInReadmodel(toReadModelEService(eServiceDeliver), eservices); await writeInReadmodel(toReadModelTenant(tenant), tenants); expect( purposeService.updateReversePurpose( - purposeForDeliver.id, + delegatePurpose.id, reversePurposeUpdateContent, { authData, @@ -933,6 +947,67 @@ describe("updatePurpose and updateReversePurpose", () => { serviceName: "", } ) - ).rejects.toThrowError(organizationNotAllowed(authData.organizationId)); + ).rejects.toThrowError( + organizationIsNotTheDelegatedConsumer( + authData.organizationId, + delegation.id + ) + ); + }); + it("should throw organizationIsNotTheDelegatedConsumer when the requester is the Consumer but there is a Consumer Delegation in updatePurpose", async () => { + const authData = getRandomAuthData(); + + const delegatePurpose: Purpose = { + ...purposeForDeliver, + consumerId: tenant.id, + delegationId: generateId(), + }; + + await addOnePurpose(delegatePurpose); + await writeInReadmodel(toReadModelEService(eServiceDeliver), eservices); + await writeInReadmodel(toReadModelTenant(tenant), tenants); + + expect( + purposeService.updateReversePurpose( + delegatePurpose.id, + reversePurposeUpdateContent, + { + authData, + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + } + ) + ).rejects.toThrowError( + organizationIsNotTheConsumer(authData.organizationId) + ); + }); + it("should throw organizationIsNotTheDelegatedConsumer when the requester is the Consumer but there is a Consumer Delegation in updateReversePurpose", async () => { + const authData = getRandomAuthData(); + + const delegatePurpose: Purpose = { + ...purposeForReceive, + consumerId: tenant.id, + delegationId: generateId(), + }; + + await addOnePurpose(delegatePurpose); + await writeInReadmodel(toReadModelEService(eServiceDeliver), eservices); + await writeInReadmodel(toReadModelTenant(tenant), tenants); + + expect( + purposeService.updateReversePurpose( + delegatePurpose.id, + reversePurposeUpdateContent, + { + authData, + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + } + ) + ).rejects.toThrowError( + organizationIsNotTheConsumer(authData.organizationId) + ); }); }); From ad15dfa654867fc588d4580bdf26b8d733d213ca Mon Sep 17 00:00:00 2001 From: Roberto Gregnanin <128478646+rGregnanin@users.noreply.github.com> Date: Thu, 16 Jan 2025 12:56:30 +0100 Subject: [PATCH 21/23] fixed as suggested --- .../src/services/readModelService.ts | 4 ++-- .../purpose-process/test/updatePurpose.test.ts | 16 +++++++++------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/packages/purpose-process/src/services/readModelService.ts b/packages/purpose-process/src/services/readModelService.ts index c7700a95e6..df3124ed69 100644 --- a/packages/purpose-process/src/services/readModelService.ts +++ b/packages/purpose-process/src/services/readModelService.ts @@ -405,7 +405,7 @@ export function readModelServiceBuilder( "data.eserviceId": eserviceId, "data.state": delegationState.active, "data.kind": delegationKind.delegatedProducer, - }); + } satisfies ReadModelFilter); }, async getActiveConsumerDelegationByEserviceAndConsumerIds({ eserviceId, @@ -419,7 +419,7 @@ export function readModelServiceBuilder( "data.delegatorId": consumerId, "data.state": delegationState.active, "data.kind": delegationKind.delegatedConsumer, - }); + } satisfies ReadModelFilter); }, async getActiveConsumerDelegationByDelegationId( delegationId: DelegationId diff --git a/packages/purpose-process/test/updatePurpose.test.ts b/packages/purpose-process/test/updatePurpose.test.ts index 2e332ac62d..1ace0c0c42 100644 --- a/packages/purpose-process/test/updatePurpose.test.ts +++ b/packages/purpose-process/test/updatePurpose.test.ts @@ -346,8 +346,8 @@ describe("updatePurpose and updateReversePurpose", () => { const delegation = getMockDelegation({ id: delegatePurpose.delegationId, kind: delegationKind.delegatedConsumer, - eserviceId: purposeForReceive.eserviceId, - delegatorId: purposeForReceive.consumerId, + eserviceId: delegatePurpose.eserviceId, + delegatorId: delegatePurpose.consumerId, delegateId: authData.organizationId, state: delegationState.active, }); @@ -431,6 +431,7 @@ describe("updatePurpose and updateReversePurpose", () => { ...purposeForDeliver, consumerId: consumer.id, eserviceId: eservice.id, + delegationId: generateId(), }; const producerDelegation = getMockDelegation({ @@ -442,6 +443,7 @@ describe("updatePurpose and updateReversePurpose", () => { }); const consumerDelegation = getMockDelegation({ + id: delegatePurpose.delegationId, kind: delegationKind.delegatedConsumer, eserviceId: eservice.id, delegatorId: consumer.id, @@ -469,7 +471,7 @@ describe("updatePurpose and updateReversePurpose", () => { delegatePurpose.id, updateContentWithoutTitle, { - authData: getRandomAuthData(consumer.id), + authData: getRandomAuthData(consumerDelegate.id), correlationId: generateId(), logger: genericLogger, serviceName: "", @@ -877,7 +879,7 @@ describe("updatePurpose and updateReversePurpose", () => { riskAnalysisValidationFailed([unexpectedRulesVersionError("0")]) ); }); - it("should throw organizationIsNotTheDelegatedConsumer when the requester is the Consumer but there is a Consumer Delegation in updatePurpose", async () => { + it("should throw organizationIsNotTheDelegatedConsumer when the requester is the Consumer and is updating a purpose created by the delegate in updatePurpose", async () => { const authData = getRandomAuthData(); const delegatePurpose: Purpose = { @@ -913,7 +915,7 @@ describe("updatePurpose and updateReversePurpose", () => { ) ); }); - it("should throw organizationIsNotTheDelegatedConsumer when the requester is the Consumer but there is a Consumer Delegation in updateReversePurpose", async () => { + it("should throw organizationIsNotTheDelegatedConsumer when the requester and is updating a purpose created by the delegate in updateReversePurpose", async () => { const authData = getRandomAuthData(); const delegatePurpose: Purpose = { @@ -954,7 +956,7 @@ describe("updatePurpose and updateReversePurpose", () => { ) ); }); - it("should throw organizationIsNotTheDelegatedConsumer when the requester is the Consumer but there is a Consumer Delegation in updatePurpose", async () => { + it("should throw organizationIsNotTheConsumer when the requester is the Consumer with no delegation and is updating a purpose created by a delegate in updatePurpose", async () => { const authData = getRandomAuthData(); const delegatePurpose: Purpose = { @@ -982,7 +984,7 @@ describe("updatePurpose and updateReversePurpose", () => { organizationIsNotTheConsumer(authData.organizationId) ); }); - it("should throw organizationIsNotTheDelegatedConsumer when the requester is the Consumer but there is a Consumer Delegation in updateReversePurpose", async () => { + it("should throw organizationIsNotTheConsumer when the requester is the Consumer with no delegation and is updating a purpose created by a delegate in updateReversePurpose", async () => { const authData = getRandomAuthData(); const delegatePurpose: Purpose = { From f4f985a199d0bd65dc3fbfb86355b3117e3d179f Mon Sep 17 00:00:00 2001 From: Roberto Gregnanin <128478646+rGregnanin@users.noreply.github.com> Date: Thu, 16 Jan 2025 17:26:25 +0100 Subject: [PATCH 22/23] fix --- packages/purpose-process/test/getPurposes.test.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/purpose-process/test/getPurposes.test.ts b/packages/purpose-process/test/getPurposes.test.ts index e141e58330..5f248eeff5 100644 --- a/packages/purpose-process/test/getPurposes.test.ts +++ b/packages/purpose-process/test/getPurposes.test.ts @@ -1,5 +1,6 @@ import { Agreement, + DelegationId, EService, EServiceId, Purpose, @@ -604,6 +605,7 @@ describe("getPurposes", async () => { consumerId: consumer.id, eserviceId: eservice.id, versions: [getMockPurposeVersion(purposeVersionState.active)], + delegationId: generateId(), }; const producerDelegation = getMockDelegation({ @@ -615,6 +617,7 @@ describe("getPurposes", async () => { }); const consumerDelegation = getMockDelegation({ + id: delegatePurpose.delegationId, kind: delegationKind.delegatedConsumer, eserviceId: eservice.id, delegatorId: consumer.id, From 43f1513bb7e745649387b65b48a8f49ca7a450e9 Mon Sep 17 00:00:00 2001 From: Roberto Gregnanin <128478646+rGregnanin@users.noreply.github.com> Date: Fri, 24 Jan 2025 15:55:02 +0100 Subject: [PATCH 23/23] added tests --- .../purpose-process/test/getPurposes.test.ts | 97 ++++++++++++++++++- 1 file changed, 93 insertions(+), 4 deletions(-) diff --git a/packages/purpose-process/test/getPurposes.test.ts b/packages/purpose-process/test/getPurposes.test.ts index 981e9df14a..52a48c6c3e 100644 --- a/packages/purpose-process/test/getPurposes.test.ts +++ b/packages/purpose-process/test/getPurposes.test.ts @@ -533,14 +533,18 @@ describe("getPurposes", async () => { expect(result.results).toEqual([]); }); it("should get the purposes if the requester is an e-service delegated consumer", async () => { + const delegatedPurpose: Purpose = { + ...getMockPurpose(), + delegationId: generateId(), + }; const delegation = getMockDelegation({ + id: delegatedPurpose.delegationId, kind: delegationKind.delegatedConsumer, - eserviceId: mockPurpose1.eserviceId, - delegatorId: mockPurpose1.consumerId, - delegateId: consumerId1, + eserviceId: delegatedPurpose.eserviceId, + delegatorId: delegatedPurpose.consumerId, + delegateId: generateId(), state: delegationState.active, }); - await addOneDelegation(delegation); const result = await purposeService.getPurposes( @@ -661,6 +665,91 @@ describe("getPurposes", async () => { mockPurpose7, ]); }); + it("Should return an empty list if the requester is a delegate for the eservice and there is no delegationId in the purpose", async () => { + await purposes.deleteMany({}); + await eservices.deleteMany({}); + + const eservice = getMockEService(); + const purpose: Purpose = { + ...getMockPurpose(), + eserviceId: eservice.id, + delegationId: undefined, + }; + + const purposeDelegation = getMockDelegation({ + kind: delegationKind.delegatedConsumer, + eserviceId: purpose.eserviceId, + delegatorId: purpose.consumerId, + delegateId: generateId(), + state: delegationState.active, + }); + await addOnePurpose(purpose); + await addOneEService(eservice); + await addOneDelegation(purposeDelegation); + + const result = await purposeService.getPurposes( + purposeDelegation.delegateId, + { + eservicesIds: [], + consumersIds: [purposeDelegation.delegateId], + producersIds: [], + states: [], + excludeDraft: false, + }, + { offset: 0, limit: 50 }, + genericLogger + ); + expect(result.totalCount).toBe(0); + expect(result.results).toEqual([]); + }); + it("Should return an empty list if exists a purpose delegation but the requester is not the purpose delegate", async () => { + await purposes.deleteMany({}); + await eservices.deleteMany({}); + + const eservice = getMockEService(); + const purpose: Purpose = { + ...getMockPurpose(), + eserviceId: eservice.id, + delegationId: generateId(), + }; + + const delegation = getMockDelegation({ + id: generateId(), + kind: delegationKind.delegatedConsumer, + eserviceId: purpose.eserviceId, + delegatorId: generateId(), + delegateId: generateId(), + state: delegationState.active, + }); + + const purposeDelegation = getMockDelegation({ + id: purpose.delegationId, + kind: delegationKind.delegatedConsumer, + eserviceId: purpose.eserviceId, + delegatorId: purpose.consumerId, + delegateId: generateId(), + state: delegationState.active, + }); + await addOnePurpose(purpose); + await addOneEService(eservice); + await addOneDelegation(delegation); + await addOneDelegation(purposeDelegation); + + const result = await purposeService.getPurposes( + delegation.delegateId, + { + eservicesIds: [], + consumersIds: [delegation.delegateId], + producersIds: [], + states: [], + excludeDraft: false, + }, + { offset: 0, limit: 50 }, + genericLogger + ); + expect(result.totalCount).toBe(0); + expect(result.results).toEqual([]); + }); describe("Producer Delegation active for provided producerIds filter", async () => { it("should get the purposes if they exist (parameters: producersIds with only delegateId)", async () => {