diff --git a/apps/edr-freight-api/.env.example b/apps/edr-freight-api/.env.example index da0b1ceb9..086923df5 100644 --- a/apps/edr-freight-api/.env.example +++ b/apps/edr-freight-api/.env.example @@ -76,6 +76,12 @@ SEED_EDR_ORG=true SEED_FREIGHT_STAFF=true SEED_EXPORT_DJIBOUTI_INTERCHANGE_DEMO=false +# Limits GET /staff/users to employees of this IAM organization (iam.organizations.key). +# Unset = every employee. A key matching no organization returns no users. +# Dev seed key: edr_freight +# Production: ETHIO_DJIBOUTI_STANDARD_GAUGE_RAILWAY_SHARE_COMPANY_001 +FREIGHT_ORG_KEY=edr_freight + # MinIO (used by @tria-plc/iamapi-common for file storage) MINIO_ENDPOINT=localhost MINIO_PORT=9000 diff --git a/apps/edr-freight-api/src/modules/auth/list-users.service.ts b/apps/edr-freight-api/src/modules/auth/list-users.service.ts index cf7e22be2..c598e105a 100644 --- a/apps/edr-freight-api/src/modules/auth/list-users.service.ts +++ b/apps/edr-freight-api/src/modules/auth/list-users.service.ts @@ -1,4 +1,5 @@ import { Injectable } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; import { InjectRepository } from '@nestjs/typeorm'; import { PaginatedResponse } from '@edr/types'; import { User } from '@tria-plc/iamapi-common/entities/iam/user/user.entity'; @@ -19,6 +20,7 @@ import { paginateQuery } from '../../common/utils/pagination.util'; export class ListUsersService { constructor( @InjectRepository(User) private readonly users: Repository, + private readonly config: ConfigService, ) {} findAll(query: ListUsersQueryDto): Promise> { @@ -40,6 +42,29 @@ export class ListUsersService { ]) .orderBy(`user.${sortBy}`, query.sortOrder ?? 'ASC'); + // Restrict to one IAM organization when configured. The org key differs per + // environment (dev seeds `edr_freight`, production uses the registered + // company key), so this is config rather than a constant. An unset key + // means no restriction; a key matching no organization matches no user — + // failing closed rather than silently widening to every org. + const orgKey = this.config.get('FREIGHT_ORG_KEY'); + if (orgKey) { + // EXISTS, not a join: a user with several employee rows would otherwise + // be returned once per row, duplicating them in the list and inflating + // `getManyAndCount`'s total. + qb.andWhere( + `EXISTS ( + SELECT 1 + FROM iam.employees emp + JOIN iam.organizations org ON org.id = emp.organization_id + WHERE emp.user_id = "user".id + AND org.key = :orgKey + AND org.deleted_at IS NULL + )`, + { orgKey }, + ); + } + if (query.userType) { qb.andWhere('user.userType = :userType', { userType: query.userType }); } diff --git a/apps/edr-freight-web/backoffice/src/components/ruleEngine/RuleEngineCardGrid.tsx b/apps/edr-freight-web/backoffice/src/components/ruleEngine/RuleEngineCardGrid.tsx index 762f5bc84..e3fe9cb37 100644 --- a/apps/edr-freight-web/backoffice/src/components/ruleEngine/RuleEngineCardGrid.tsx +++ b/apps/edr-freight-web/backoffice/src/components/ruleEngine/RuleEngineCardGrid.tsx @@ -253,7 +253,7 @@ const RuleEngineCardGrid = ({ config={config} layout="compact" readOnly={readOnly} - onEdit={onEdit ?? (() => { })} + onEdit={onEdit} onDelete={onDelete ?? (() => { })} onViewChain={onViewChain} onSubmitRate={onSubmitRate} diff --git a/apps/edr-freight-web/backoffice/src/components/ruleEngine/RuleEngineRecordActions.tsx b/apps/edr-freight-web/backoffice/src/components/ruleEngine/RuleEngineRecordActions.tsx index 96440c001..f724d5b60 100644 --- a/apps/edr-freight-web/backoffice/src/components/ruleEngine/RuleEngineRecordActions.tsx +++ b/apps/edr-freight-web/backoffice/src/components/ruleEngine/RuleEngineRecordActions.tsx @@ -14,7 +14,7 @@ import type { RuleEngineRecord } from "@/types/rule-engine"; export interface RuleEngineRecordActionsProps { record: RuleEngineRecord; config: RuleEngineResourceConfig; - onEdit: (record: RuleEngineRecord) => void; + onEdit?: (record: RuleEngineRecord) => void; onDelete: (record: RuleEngineRecord) => void; onViewChain?: () => void; onSubmitRate?: (id: string) => void; @@ -93,17 +93,19 @@ const RuleEngineRecordActions = ({ ) : null}
- + {onEdit ? ( + + ) : null} - {canRequest ? ( - - ) : null} } /> diff --git a/apps/edr-payment-api/src/modules/cbe-bill/mappers/cbe-payment.mapper.ts b/apps/edr-payment-api/src/modules/cbe-bill/mappers/cbe-payment.mapper.ts index 8e6259930..5b479a8cc 100644 --- a/apps/edr-payment-api/src/modules/cbe-bill/mappers/cbe-payment.mapper.ts +++ b/apps/edr-payment-api/src/modules/cbe-bill/mappers/cbe-payment.mapper.ts @@ -10,7 +10,7 @@ export function mapPaymentSuccess( End_To_End_Txn_Id: request.End_To_End_Txn_Id, Cbe_Txn_Ref: request.Cbe_Txn_Ref, Destination_Txn_Ref: destinationTxnRef, - Status: "SUCCESS", + Status: "Success", Response_Code: "0", Response_Description: "Success", Additional_Fields: [], diff --git a/apps/edr-payment-api/src/modules/cbe-bill/mappers/cbe-query.mapper.ts b/apps/edr-payment-api/src/modules/cbe-bill/mappers/cbe-query.mapper.ts index d2153b6a5..ac96c3ce1 100644 --- a/apps/edr-payment-api/src/modules/cbe-bill/mappers/cbe-query.mapper.ts +++ b/apps/edr-payment-api/src/modules/cbe-bill/mappers/cbe-query.mapper.ts @@ -21,7 +21,7 @@ export function mapQuerySuccess( Credit_Acct_Number: "", Transaction_Type: "", Timestamp: new Date().toISOString(), - Status: "SUCCESS", + Status: "Success", Response_Code: "0", Response_Description: "Success", Additional_Fields: [],