Skip to content
10 changes: 9 additions & 1 deletion apps/api-gateway/src/oid4vc-issuance/dtos/oid4vc-issuer.dto.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* eslint-disable camelcase */
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
import { IsString, IsOptional, IsArray, ValidateNested, IsUrl, IsInt, Min } from 'class-validator';
import { IsString, IsOptional, IsArray, ValidateNested, IsUrl, IsInt, Min, IsBoolean } from 'class-validator';
import { Type } from 'class-transformer';

export class LogoDto {
Expand Down Expand Up @@ -178,4 +178,12 @@ export class IssuerUpdationDto {
@IsOptional()
@IsInt({ message: 'batchCredentialIssuanceSize must be an integer' })
batchCredentialIssuanceSize?: number;

@ApiPropertyOptional({
description: 'Marks whether this issuer is the primary issuer',
example: true
})
@IsOptional()
@IsBoolean()
isPrimary?: boolean;
Comment thread
pranalidhanavade marked this conversation as resolved.
}
4 changes: 4 additions & 0 deletions apps/oid4vc-issuance/interfaces/oid4vc-issuance.interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ export interface IssuerMetadata {
createdById: string;
orgAgentId: string;
batchCredentialIssuanceSize?: number;
isPrimary: boolean;
orgId: string;
}

export interface initialIssuerDetails {
Expand All @@ -95,6 +97,8 @@ export interface IssuerUpdation {
accessTokenSignerKeyType: AccessTokenSignerKeyType;
display;
batchCredentialIssuanceSize?: number;
isPrimary?: boolean;
orgId: string;
}

export interface IAgentNatsPayload {
Expand Down
92 changes: 76 additions & 16 deletions apps/oid4vc-issuance/src/oid4vc-issuance.repository.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
/* eslint-disable camelcase */
import { Injectable, Logger, NotFoundException } from '@nestjs/common';
import { BadRequestException, Injectable, Logger, NotFoundException } from '@nestjs/common';
import { IssuerMetadata, IssuerUpdation, OrgAgent } from '../interfaces/oid4vc-issuance.interfaces';
// eslint-disable-next-line camelcase
import { Prisma, credential_templates, oidc_issuer, org_agents } from '@prisma/client';
import { x5cKeyType, x5cRecordStatus } from '@credebl/enum/enum';

import { PrismaService } from '@credebl/prisma-service';
import { IssuerMetadata, IssuerUpdation, OrgAgent } from '../interfaces/oid4vc-issuance.interfaces';
import { ResponseMessages } from '@credebl/common/response-messages';
import { x5cKeyType, x5cRecordStatus } from '@credebl/enum/enum';
import { X509CertificateRecord } from '@credebl/common/interfaces/x509.interface';

@Injectable()
Expand Down Expand Up @@ -190,16 +191,25 @@ export class Oid4vcIssuanceRepository {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
async addOidcIssuerDetails(issuerMetadata: IssuerMetadata, issuerProfileJson): Promise<oidc_issuer> {
try {
const { publicIssuerId, createdById, orgAgentId, batchCredentialIssuanceSize, authorizationServerUrl } =
issuerMetadata;
const {
publicIssuerId,
createdById,
orgAgentId,
batchCredentialIssuanceSize,
authorizationServerUrl,
isPrimary,
orgId
} = issuerMetadata;
const oidcIssuerDetails = await this.prisma.oidc_issuer.create({
data: {
metadata: issuerProfileJson,
publicIssuerId,
createdBy: createdById,
orgAgentId,
batchCredentialIssuanceSize,
authorizationServerUrl
authorizationServerUrl,
isPrimary,
orgId
}
});

Expand All @@ -210,21 +220,71 @@ export class Oid4vcIssuanceRepository {
}
}

async updateOidcIssuerDetails(createdById: string, issuerConfig: IssuerUpdation): Promise<oidc_issuer> {
async hasPrimaryIssuer(orgId: string): Promise<boolean> {
try {
const { issuerId, display, batchCredentialIssuanceSize } = issuerConfig;
const oidcIssuerDetails = await this.prisma.oidc_issuer.update({
where: { id: issuerId },
data: {
metadata: display as unknown as Prisma.InputJsonValue,
createdBy: createdById,
...(batchCredentialIssuanceSize !== undefined ? { batchCredentialIssuanceSize } : {})
const count = await this.prisma.oidc_issuer.count({
where: {
orgId,
isPrimary: true
}
});
return 0 < count;
} catch (error) {
this.logger.error(`[hasPrimaryIssuer] - error: ${JSON.stringify(error)}`);
throw error;
}
}
Comment thread
pranalidhanavade marked this conversation as resolved.

return oidcIssuerDetails;
async updateOidcIssuerDetails(createdById: string, issuerConfig: IssuerUpdation): Promise<oidc_issuer> {
try {
const { issuerId, display, batchCredentialIssuanceSize, isPrimary, orgId } = issuerConfig;

return await this.prisma.$transaction(async (tx) => {
const issuer = await tx.oidc_issuer.findFirst({
where: {
id: issuerId,
orgId
}
});

if (!issuer) {
throw new NotFoundException(ResponseMessages.oidcIssuer.error.notFound);
}
Comment thread
pranalidhanavade marked this conversation as resolved.

if (isPrimary) {
await tx.oidc_issuer.updateMany({
where: { orgId, isPrimary: true },
data: { isPrimary: false }
});
} else {
const otherPrimaryExists = await tx.oidc_issuer.count({
where: {
orgId,
isPrimary: true,
NOT: { id: issuerId }
}
});

if (0 === otherPrimaryExists) {
throw new BadRequestException(ResponseMessages.oidcIssuer.error.setPrimaryIssuerFailed);
}
}
const updatedIssuer = await tx.oidc_issuer.update({
where: { id: issuerId },
data: {
metadata: display as unknown as Prisma.InputJsonValue,
createdBy: createdById,
...(batchCredentialIssuanceSize !== undefined && {
batchCredentialIssuanceSize
}),
...(isPrimary !== undefined && { isPrimary })
}
});

return updatedIssuer;
});
} catch (error) {
this.logger.error(`[addOidcIssuerDetails] - error: ${JSON.stringify(error)}`);
this.logger.error(`[updateOidcIssuerDetails] - error: ${JSON.stringify(error)}`);
throw error;
}
}
Comment thread
pranalidhanavade marked this conversation as resolved.
Expand Down
15 changes: 11 additions & 4 deletions apps/oid4vc-issuance/src/oid4vc-issuance.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,11 @@ export class Oid4vcIssuanceService {
throw new NotFoundException(ResponseMessages.issuance.error.agentEndPointNotFound);
}
const { agentEndPoint, id: orgAgentId, orgAgentTypeId } = agentDetails;

const hasPrimary = await this.oid4vcIssuanceRepository.hasPrimaryIssuer(orgId);

const isPrimary = !hasPrimary;

const orgAgentType = await this.oid4vcIssuanceRepository.getOrgAgentType(orgAgentTypeId);
if (!orgAgentType) {
throw new NotFoundException(ResponseMessages.issuance.error.orgAgentTypeNotFound);
Expand Down Expand Up @@ -138,7 +143,9 @@ export class Oid4vcIssuanceService {
publicIssuerId: issuerIdFromAgent,
createdById: userDetails.id,
orgAgentId,
batchCredentialIssuanceSize: issuerCreation?.batchCredentialIssuanceSize
batchCredentialIssuanceSize: issuerCreation?.batchCredentialIssuanceSize,
isPrimary,
orgId
};
const addOidcIssuerDetails = await this.oid4vcIssuanceRepository.addOidcIssuerDetails(
issuerMetadata,
Expand All @@ -157,10 +164,10 @@ export class Oid4vcIssuanceService {

async oidcIssuerUpdate(issuerUpdationConfig: IssuerUpdation, orgId: string, userDetails: user): Promise<oidc_issuer> {
try {
const getIssuerDetails = await this.oid4vcIssuanceRepository.getOidcIssuerDetailsById(
const existingIssuer = await this.oid4vcIssuanceRepository.getOidcIssuerDetailsById(
issuerUpdationConfig.issuerId
);
if (!getIssuerDetails) {
if (!existingIssuer) {
throw new NotFoundException(ResponseMessages.oidcIssuer.error.notFound);
}
const agentDetails = await this.oid4vcIssuanceRepository.getAgentEndPoint(orgId);
Expand All @@ -182,7 +189,7 @@ export class Oid4vcIssuanceService {
throw new InternalServerErrorException('Error in updating OID4VC Issuer details in DB');
}

const url = getAgentUrl(agentEndPoint, CommonConstants.OIDC_ISSUER_TEMPLATE, getIssuerDetails.publicIssuerId);
const url = getAgentUrl(agentEndPoint, CommonConstants.OIDC_ISSUER_TEMPLATE, existingIssuer.publicIssuerId);
const issuerConfig = await this.buildOidcIssuerConfig(issuerUpdationConfig.issuerId);
const updatedIssuer = await this._createOIDCTemplate(issuerConfig, url, orgId);
if (updatedIssuer?.response?.statusCode && 200 !== updatedIssuer?.response?.statusCode) {
Expand Down
3 changes: 2 additions & 1 deletion libs/common/src/response-messages/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -671,7 +671,8 @@ export const ResponseMessages = {
invalidId: 'Invalid OID4VC issuer ID.',
createFailed: 'Failed to create OID4VC issuer.',
updateFailed: 'Failed to update OID4VC issuer.',
deleteFailed: 'Failed to delete OID4VC issuer.'
deleteFailed: 'Failed to delete OID4VC issuer.',
setPrimaryIssuerFailed: 'Cannot unset primary. Please assign another issuer as primary first.'
}
},
oidcTemplate: {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
-- DropForeignKey
ALTER TABLE "issued_oid4vc_credentials" DROP CONSTRAINT "issued_oid4vc_credentials_orgId_fkey";

-- DropForeignKey
ALTER TABLE "oid4vc_credentials" DROP CONSTRAINT "oid4vc_credentials_orgId_fkey";

-- AlterTable
ALTER TABLE "oid4vc_credentials" ALTER COLUMN "orgId" DROP NOT NULL;

-- AlterTable
ALTER TABLE "oidc_issuer" ADD COLUMN "isPrimary" BOOLEAN NOT NULL DEFAULT false,
ADD COLUMN "orgId" UUID;

-- AddForeignKey
ALTER TABLE "oidc_issuer" ADD CONSTRAINT "oidc_issuer_orgId_fkey" FOREIGN KEY ("orgId") REFERENCES "organisation"("id") ON DELETE SET NULL ON UPDATE CASCADE;
Comment thread
pranalidhanavade marked this conversation as resolved.

-- AddForeignKey
ALTER TABLE "oid4vc_credentials" ADD CONSTRAINT "oid4vc_credentials_orgId_fkey" FOREIGN KEY ("orgId") REFERENCES "organisation"("id") ON DELETE SET NULL ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "issued_oid4vc_credentials" ADD CONSTRAINT "issued_oid4vc_credentials_orgId_fkey" FOREIGN KEY ("orgId") REFERENCES "organisation"("id") ON DELETE SET NULL ON UPDATE CASCADE;
6 changes: 6 additions & 0 deletions libs/prisma-service/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ model organisation {
ecosystem_invitations ecosystem_invitations[]
status_list_allocations status_list_allocation[]
issued_oid4vc_credentials issued_oid4vc_credentials[]
oidc_issuer oidc_issuer[]
}

model org_invitations {
Expand Down Expand Up @@ -596,6 +597,11 @@ model oidc_issuer {
templates credential_templates[]
batchCredentialIssuanceSize Int @default(0)

orgId String? @db.Uuid
isPrimary Boolean @default(false)

organisation organisation? @relation(fields: [orgId], references: [id])

@@index([orgAgentId])
Comment thread
pranalidhanavade marked this conversation as resolved.
}

Expand Down
Loading