diff --git a/packages/data-store/src/credential/AbstractCredentialStore.ts b/packages/data-store/src/credential/AbstractCredentialStore.ts new file mode 100644 index 000000000..08e17ad00 --- /dev/null +++ b/packages/data-store/src/credential/AbstractCredentialStore.ts @@ -0,0 +1,16 @@ +import { + AddCredentialArgs, + GetCredentialArgs, + GetCredentialsArgs, + RemoveCredentialArgs, + UpdateCredentialStateArgs, +} from '../types/credential/IAbstractCredentialStore' +import { UniformCredential } from '../types/credential/credential' + +export abstract class AbstractCredentialStore { + abstract getCredential(args: GetCredentialArgs): Promise + abstract getCredentials(args?: GetCredentialsArgs): Promise> + abstract addCredential(args: AddCredentialArgs): Promise + abstract updateCredentialState(args: UpdateCredentialStateArgs): Promise + abstract removeCredential(args: RemoveCredentialArgs): Promise +} diff --git a/packages/data-store/src/credential/CredentialStore.ts b/packages/data-store/src/credential/CredentialStore.ts new file mode 100644 index 000000000..3e7192e9f --- /dev/null +++ b/packages/data-store/src/credential/CredentialStore.ts @@ -0,0 +1,48 @@ +import { AbstractCredentialStore } from './AbstractCredentialStore' +import { AddCredentialArgs, GetCredentialArgs, GetCredentialsArgs, RemoveCredentialArgs } from '../types/credential/IAbstractCredentialStore' +import { UniformCredential } from '../types/credential/credential' +import { OrPromise } from '@sphereon/ssi-types' +import { DataSource, Repository } from 'typeorm' +import Debug from 'debug' +import { UniformCredentialEntity } from '../entities/credential/CredentialEntity' +import { PartyEntity } from '../entities/contact/PartyEntity' +import { credentialEntityFrom } from '../utils/credential/MappingUtils' + +const debug: Debug.Debugger = Debug('sphereon:ssi-sdk:credential-store') +export class CredentialStore extends AbstractCredentialStore { + private readonly dbConnection: OrPromise + + constructor(dbConnection: OrPromise) { + super() + this.dbConnection = dbConnection + } + + addCredential = async (args: AddCredentialArgs): Promise => { + debug('Adding credential', args) + const uniformCredentialRepository: Repository = (await this.dbConnection).getRepository(UniformCredentialEntity) + const credentialEntity: UniformCredentialEntity = credentialEntityFrom(args) + const createdResult: UniformCredentialEntity = await uniformCredentialRepository.save(credentialEntity) + return Promise.resolve(undefined) + } + + getCredential = async (args: GetCredentialArgs): Promise => { + const result: UniformCredentialEntity | null = await (await this.dbConnection).getRepository(UniformCredentialEntity).findOne({ + where: args, + }) + + if (!result) { + return Promise.reject(Error(`No party found for arg: ${args.toString()}`)) + } + + //todo: cast to UniformCredential + return result + } + + getCredentials = async (args?: GetCredentialsArgs): Promise> => { + return Promise.resolve(undefined) + } + + removeCredential = async (args: RemoveCredentialArgs): Promise => { + return Promise.resolve(undefined) + } +} diff --git a/packages/data-store/src/entities/credential/CredentialEntity.ts b/packages/data-store/src/entities/credential/CredentialEntity.ts new file mode 100644 index 000000000..5d846ba8d --- /dev/null +++ b/packages/data-store/src/entities/credential/CredentialEntity.ts @@ -0,0 +1,53 @@ +import { BaseEntity, Column, CreateDateColumn, Entity, PrimaryGeneratedColumn, UpdateDateColumn } from 'typeorm' +import { CredentialCorrelationType, CredentialDocumentFormat, CredentialStateType, CredentialTypeEnum } from '../../types/credential/credential' + +@Entity('UniformCredential') +export class UniformCredentialEntity extends BaseEntity { + @PrimaryGeneratedColumn('uuid') + id!: string + + @Column('simple-enum', { name: 'credential_type', enum: CredentialTypeEnum, nullable: false }) + credentialType!: CredentialTypeEnum + + @Column('simple-enum', { name: 'document_format', enum: CredentialDocumentFormat, nullable: false }) + documentFormat!: CredentialDocumentFormat + + @Column('text', { name: 'raw', nullable: false }) + raw!: string + + @Column('text', { name: 'uniform_document', nullable: false }) + uniformDocument!: string + + @Column('text', { name: 'hash', nullable: false, unique: true }) + hash!: string + + @Column('string', { name: 'type', nullable: false }) + type!: string + + @Column('simple-enum', { name: 'issuer_correlation_type', enum: CredentialCorrelationType, nullable: false }) + issuerCorrelationType!: CredentialCorrelationType + + @Column('simple-enum', { name: 'subject_correlation_type', enum: CredentialCorrelationType, nullable: true }) + subjectCorrelationType?: CredentialCorrelationType + + @Column('text', { name: 'issuer_correlation_type', nullable: false }) + issuerCorrelationId!: string + + @Column('text', { name: 'subject_correlation_type', nullable: true }) + subjectCorrelationId?: string + + @Column('simple-enum', { name: 'last_verified_state', enum: CredentialStateType, nullable: true }) + lastVerifiedState?: CredentialStateType + + @Column('text', { name: 'tenant_id', nullable: true }) + tenantId?: string + + @CreateDateColumn({ name: 'created_at', nullable: false }) + createdAt!: Date + + @UpdateDateColumn({ name: 'last_updated_at', nullable: false }) + lastUpdatedAt!: Date + + @Column('date', { name: 'expires_at', nullable: false }) + expiresAt!: Date +} diff --git a/packages/data-store/src/types/credential/IAbstractCredentialStore.ts b/packages/data-store/src/types/credential/IAbstractCredentialStore.ts new file mode 100644 index 000000000..4f4a82bf7 --- /dev/null +++ b/packages/data-store/src/types/credential/IAbstractCredentialStore.ts @@ -0,0 +1,21 @@ +import { CredentialCorrelationType, CredentialDocumentFormat, CredentialStateType, CredentialTypeEnum } from './credential' + +export type GetCredentialArgs = { id: string } | { hash: string } + +// TODO: discuss about what args we want here +export type GetCredentialsArgs = {} + +export type AddCredentialArgs = { + credentialType: CredentialTypeEnum + documentFormat: CredentialDocumentFormat + raw: string + issuerCorrelationType: CredentialCorrelationType + subjectCorrelationType?: CredentialCorrelationType + issuerCorrelationId: string + subjectCorrelationId?: string + tenantId?: string +} + +export type UpdateCredentialStateArgs = GetCredentialArgs & { verified_state: CredentialStateType } + +export type RemoveCredentialArgs = { id: string } | { hash: string } diff --git a/packages/data-store/src/types/credential/credential.ts b/packages/data-store/src/types/credential/credential.ts new file mode 100644 index 000000000..04b18e7b9 --- /dev/null +++ b/packages/data-store/src/types/credential/credential.ts @@ -0,0 +1,39 @@ +export type UniformCredential = { + id: string + credentialType: CredentialTypeEnum + documentFormat: CredentialDocumentFormat + raw: string + hash: string + type: string + issuerCorrelationType: CredentialCorrelationType + subjectCorrelationType?: CredentialCorrelationType + issuerCorrelationId: string + subjectCorrelationId?: string + last_verified_state?: CredentialStateType + tenantId?: string + createdAt: Date + lastUpdatedAt: Date + expiresAt: Date +} + +export enum CredentialTypeEnum { + VC = 'vc', + VP = 'vp', +} + +export enum CredentialDocumentFormat { + JSON_LD = 'JSON-LD', + JWT = 'JWT', + SD_JWT = 'SD-JWT', + MDOC = 'MDOC', +} + +export enum CredentialCorrelationType { + DID = 'did', +} + +export enum CredentialStateType { + REVOKED = 'revoked', + VERIFIED = 'verified', + EXPIRED = 'expired', +} diff --git a/packages/data-store/src/utils/credential/MappingUtils.ts b/packages/data-store/src/utils/credential/MappingUtils.ts new file mode 100644 index 000000000..61d449856 --- /dev/null +++ b/packages/data-store/src/utils/credential/MappingUtils.ts @@ -0,0 +1,30 @@ +import { AddCredentialArgs } from '../../types/credential/IAbstractCredentialStore' +import { UniformCredentialEntity } from '../../entities/credential/CredentialEntity' +import { CredentialMapper, WrappedVerifiableCredential, WrappedVerifiablePresentation } from '@sphereon/ssi-types' +import { CredentialTypeEnum } from '../../types/credential/credential' +import { computeEntryHash } from '@veramo/utils' + +export const credentialEntityFrom = async (addCredentialArgs: AddCredentialArgs): Promise => { + const wrappedCredential: WrappedVerifiableCredential | WrappedVerifiablePresentation = + addCredentialArgs.credentialType === CredentialTypeEnum.VC + ? CredentialMapper.toWrappedVerifiableCredential(JSON.stringify(addCredentialArgs.raw)) + : CredentialMapper.toWrappedVerifiablePresentation(addCredentialArgs.raw) + const uniformCredentialEntity: UniformCredentialEntity = new UniformCredentialEntity() + + uniformCredentialEntity.credentialType = addCredentialArgs.credentialType + uniformCredentialEntity.createdAt = new Date(wrappedCredential.decoded.nbf) + console.log(`We have createdAt:`, uniformCredentialEntity.createdAt) + uniformCredentialEntity.documentFormat = addCredentialArgs.documentFormat + uniformCredentialEntity.lastUpdatedAt = new Date() + uniformCredentialEntity.tenantId = addCredentialArgs.tenantId + uniformCredentialEntity.raw = addCredentialArgs.raw + uniformCredentialEntity.subjectCorrelationId = addCredentialArgs.subjectCorrelationId + uniformCredentialEntity.subjectCorrelationType = addCredentialArgs.subjectCorrelationType + uniformCredentialEntity.type = wrappedCredential.type + uniformCredentialEntity.hash = computeEntryHash(addCredentialArgs.raw) + uniformCredentialEntity.uniformDocument = + addCredentialArgs.credentialType === CredentialTypeEnum.VC + ? JSON.stringify((wrappedCredential as WrappedVerifiableCredential).credential) + : JSON.stringify((wrappedCredential as WrappedVerifiablePresentation).presentation) + return {} as UniformCredentialEntity +}