Skip to content

Commit

Permalink
feat: added tenant aware credential store
Browse files Browse the repository at this point in the history
  • Loading branch information
sksadjad committed Feb 22, 2024
1 parent c17db4b commit 312698e
Show file tree
Hide file tree
Showing 20 changed files with 671 additions and 126 deletions.
1 change: 1 addition & 0 deletions packages/data-store/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"@sphereon/ssi-sdk.core": "workspace:*",
"@sphereon/ssi-types": "workspace:*",
"@veramo/core": "4.2.0",
"@veramo/utils": "4.2.0",
"class-validator": "^0.14.0",
"debug": "^4.3.4",
"typeorm": "^0.3.12"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { DataSource } from 'typeorm'
import { DataStoreUniformCredentialEntities, uniformCredentialEntityFromAddArgs } from '../index'
import { DataStoreUniformCredentialMigrations } from '../migrations'
import { UniformCredentialEntity } from '../entities/uniformCredential/UniformCredentialEntity'
import { CredentialCorrelationType, CredentialDocumentFormat, CredentialTypeEnum } from '../types/uniformCredential/uniformCredential'
import { computeEntryHash } from '@veramo/utils'
import { AddUniformCredentialArgs } from '../../dist/types/credential/IAbstractCredentialStore'

describe('Database entities tests', (): void => {
let dbConnection: DataSource

beforeEach(async (): Promise<void> => {
dbConnection = await new DataSource({
type: 'sqlite',
database: ':memory:',
//logging: 'all',
migrationsRun: false,
migrations: DataStoreUniformCredentialMigrations,
synchronize: false,
entities: [...DataStoreUniformCredentialEntities],
}).initialize()
await dbConnection.runMigrations()
expect(await dbConnection.showMigrations()).toBeFalsy()
})

afterEach(async (): Promise<void> => {
await (await dbConnection).destroy()
})

it('should save uniform credential to database', async (): Promise<void> => {
console.log(`going to save the credential...`)
const rawCredential: string =
'eyJraWQiOiJkaWQ6a2V5Ono2TWtyaGt5M3B1c20yNk1laUZhWFUzbjJuZWtyYW13RlVtZ0dyZUdHa0RWNnpRaiN6Nk1rcmhreTNwdXNtMjZNZWlGYVhVM24ybmVrcmFtd0ZVbWdHcmVHR2tEVjZ6UWoiLCJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSIsImh0dHBzOi8vc3BoZXJlb24tb3BlbnNvdXJjZS5naXRodWIuaW8vc3NpLW1vYmlsZS13YWxsZXQvY29udGV4dC9zcGhlcmVvbi13YWxsZXQtaWRlbnRpdHktdjEuanNvbmxkIl0sInR5cGUiOlsiVmVyaWZpYWJsZUNyZWRlbnRpYWwiLCJTcGhlcmVvbldhbGxldElkZW50aXR5Q3JlZGVudGlhbCJdLCJjcmVkZW50aWFsU3ViamVjdCI6eyJmaXJzdE5hbWUiOiJTIiwibGFzdE5hbWUiOiJLIiwiZW1haWxBZGRyZXNzIjoic0BrIn19LCJzdWIiOiJ1cm46dXVpZDpkZGE3YmYyNC04ZTdhLTQxZjgtYjY2Yy1hNDhkYmM1YjEwZmEiLCJqdGkiOiJ1cm46dXVpZDpkZGE3YmYyNC04ZTdhLTQxZjgtYjY2Yy1hNDhkYmM1YjEwZmEiLCJuYmYiOjE3MDg0NDA4MDgsImlzcyI6ImRpZDprZXk6ejZNa3Joa3kzcHVzbTI2TWVpRmFYVTNuMm5la3JhbXdGVW1nR3JlR0drRFY2elFqIn0.G0M84XVAxSmzGY-NQuB9NBofNrINSn6lvxW6761Vlq6ypvYgtc2xNdpiRmw8ryVNfnpzrr4Z5cB1RlrC05rJAw'
const uniformCredential: AddUniformCredentialArgs = {
credentialType: CredentialTypeEnum.VC,
documentFormat: CredentialDocumentFormat.JWT,
raw: rawCredential,
issuerCorrelationType: CredentialCorrelationType.DID,
subjectCorrelationType: CredentialCorrelationType.DID,
issuerCorrelationId: 'did:key:z6Mkrhky3pusm26MeiFaXU3n2nekramwFUmgGreGGkDV6zQj',
subjectCorrelationId: 'did:key:z6Mkrhky3pusm26MeiFaXU3n2nekramwFUmgGreGGkDV6zQj',
tenantId: 'urn:uuid:nnag4b43-1e7a-98f8-a32c-a48dbc5b10mj',
}

const uniformCredentialEntity: UniformCredentialEntity = uniformCredentialEntityFromAddArgs(uniformCredential)
const fromDb: UniformCredentialEntity = await dbConnection.getRepository(UniformCredentialEntity).save(uniformCredentialEntity)
console.log(`saved uniformCredential: ${JSON.stringify(fromDb, null, 2)}`)
expect(fromDb).toBeDefined()
expect(fromDb?.id).not.toBeNull()
expect(fromDb?.credentialType).toEqual(CredentialTypeEnum.VC)
expect(fromDb?.documentFormat).toEqual(CredentialDocumentFormat.JWT)
expect(fromDb?.raw).toEqual(rawCredential)
expect(fromDb?.hash).toEqual(computeEntryHash(rawCredential))
expect(fromDb?.issuerCorrelationType).toEqual(CredentialCorrelationType.DID)
expect(fromDb?.subjectCorrelationType).toEqual(CredentialCorrelationType.DID)
expect(fromDb?.issuerCorrelationId).toEqual('did:key:z6Mkrhky3pusm26MeiFaXU3n2nekramwFUmgGreGGkDV6zQj')
expect(fromDb?.subjectCorrelationId).toEqual('did:key:z6Mkrhky3pusm26MeiFaXU3n2nekramwFUmgGreGGkDV6zQj')
expect(fromDb?.tenantId).toEqual('urn:uuid:nnag4b43-1e7a-98f8-a32c-a48dbc5b10mj')
})
})
214 changes: 214 additions & 0 deletions packages/data-store/src/__tests__/uniformCredential.store.test.ts

Large diffs are not rendered by default.

16 changes: 0 additions & 16 deletions packages/data-store/src/credential/AbstractCredentialStore.ts

This file was deleted.

48 changes: 0 additions & 48 deletions packages/data-store/src/credential/CredentialStore.ts

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { BaseEntity, Column, CreateDateColumn, Entity, PrimaryGeneratedColumn, UpdateDateColumn } from 'typeorm'
import { CredentialCorrelationType, CredentialDocumentFormat, CredentialStateType, CredentialTypeEnum } from '../../types/credential/credential'
import {
CredentialCorrelationType,
CredentialDocumentFormat,
CredentialStateType,
CredentialTypeEnum,
} from '../../types/uniformCredential/uniformCredential'

@Entity('UniformCredential')
export class UniformCredentialEntity extends BaseEntity {
Expand All @@ -21,19 +26,16 @@ export class UniformCredentialEntity extends BaseEntity {
@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 })
@Column('text', { name: 'issuer_correlation_id', nullable: false })
issuerCorrelationId!: string

@Column('text', { name: 'subject_correlation_type', nullable: true })
@Column('text', { name: 'subject_correlation_id', nullable: true })
subjectCorrelationId?: string

@Column('simple-enum', { name: 'last_verified_state', enum: CredentialStateType, nullable: true })
Expand All @@ -48,6 +50,12 @@ export class UniformCredentialEntity extends BaseEntity {
@UpdateDateColumn({ name: 'last_updated_at', nullable: false })
lastUpdatedAt!: Date

@Column('date', { name: 'expires_at', nullable: false })
expiresAt!: Date
@Column('date', { name: 'expires_at', nullable: true })
expiresAt?: Date

@Column('date', { name: 'verification_date', nullable: true })
verificationDate?: Date

@Column('date', { name: 'revocation_date', nullable: true })
revocationDate?: Date
}
9 changes: 9 additions & 0 deletions packages/data-store/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ export { AbstractIssuanceBrandingStore } from './issuanceBranding/AbstractIssuan
export { IssuanceBrandingStore } from './issuanceBranding/IssuanceBrandingStore'
export { StatusListStore } from './statusList/StatusListStore'
import { AuditEventEntity, auditEventEntityFrom } from './entities/eventLogger/AuditEventEntity'
import { UniformCredentialEntity } from './entities/uniformCredential/UniformCredentialEntity'
import { uniformCredentialFrom, uniformCredentialsFrom, uniformCredentialEntityFromAddArgs } from './utils/uniformCredential/MappingUtils'
export { AbstractEventLoggerStore } from './eventLogger/AbstractEventLoggerStore'
export { EventLoggerStore } from './eventLogger/EventLoggerStore'
export {
Expand Down Expand Up @@ -77,12 +79,15 @@ export const DataStoreStatusListEntities = [StatusListEntity, StatusListEntryEnt

export const DataStoreEventLoggerEntities = [AuditEventEntity]

export const DataStoreUniformCredentialEntities = [UniformCredentialEntity]

// All entities combined if a party wants to enable them all at once
export const DataStoreEntities = [
...DataStoreContactEntities,
...DataStoreIssuanceBrandingEntities,
...DataStoreStatusListEntities,
...DataStoreEventLoggerEntities,
...DataStoreUniformCredentialEntities,
]

export {
Expand Down Expand Up @@ -119,4 +124,8 @@ export {
StatusListEntryEntity,
AuditEventEntity,
auditEventEntityFrom,
UniformCredentialEntity,
uniformCredentialFrom,
uniformCredentialsFrom,
uniformCredentialEntityFromAddArgs,
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { DatabaseType, MigrationInterface, QueryRunner } from 'typeorm'
import Debug, { Debugger } from 'debug'
import { CreateUniformCredential1708525189001 } from '../postgres/1708525189001-CreateUniformCredential'
import { CreateUniformCredential1708525189002 } from '../sqlite/1708525189002-CreateUniformCredential'

const debug: Debugger = Debug('sphereon:ssi-sdk:migrations')

export class CreateUniformCredential1708525189000 implements MigrationInterface {
name: string = 'CreateUniformCredential1708525189000'

public async up(queryRunner: QueryRunner): Promise<void> {
debug('migration: creating UniformCredential tables')
const dbType: DatabaseType = queryRunner.connection.driver.options.type

switch (dbType) {
case 'postgres': {
debug('using postgres migration file for UniformCredential')
const mig: CreateUniformCredential1708525189001 = new CreateUniformCredential1708525189001()
await mig.up(queryRunner)
debug('Postgres Migration statements for UniformCredential executed')
return
}
case 'sqlite':
case 'expo':
case 'react-native': {
debug('using sqlite/react-native migration file for UniformCredential')
const mig: CreateUniformCredential1708525189002 = new CreateUniformCredential1708525189002()
await mig.up(queryRunner)
debug('SQLite Migration statements for UniformCredential executed')
return
}
default:
return Promise.reject(
`Migrations are currently only supported for sqlite, react-native, expo, and postgres for UniformCredential. Was ${dbType}. Please run your database without migrations and with 'migrationsRun: false' and 'synchronize: true' for now`
)
}
}

public async down(queryRunner: QueryRunner): Promise<void> {
debug('migration: reverting UniformCredential tables')
const dbType: DatabaseType = queryRunner.connection.driver.options.type

switch (dbType) {
case 'postgres': {
debug('using postgres migration file for UniformCredential')
const mig: CreateUniformCredential1708525189001 = new CreateUniformCredential1708525189001()
await mig.down(queryRunner)
debug('Postgres Migration statements for UniformCredential reverted')
return
}
case 'sqlite':
case 'expo':
case 'react-native': {
debug('using sqlite/react-native migration file for UniformCredential')
const mig: CreateUniformCredential1708525189002 = new CreateUniformCredential1708525189002()
await mig.down(queryRunner)
debug('SQLite Migration statements for UniformCredential reverted')
return
}
default:
return Promise.reject(
`Migrations are currently only supported for sqlite, react-native, expo, and postgres for UniformCredential. Was ${dbType}. Please run your database without migrations and with 'migrationsRun: false' and 'synchronize: true' for now`
)
}
}
}
3 changes: 3 additions & 0 deletions packages/data-store/src/migrations/generic/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { CreateIssuanceBranding1659463079429 } from './2-CreateIssuanceBranding'
import { CreateContacts1690925872318 } from './3-CreateContacts'
import { CreateStatusList1693866470000 } from './4-CreateStatusList'
import { CreateAuditEvents1701635835330 } from './5-CreateAuditEvents'
import { CreateUniformCredential1708525189000 } from './6-CreateUniformCredential'

/**
* The migrations array that SHOULD be used when initializing a TypeORM database connection.
Expand All @@ -17,11 +18,13 @@ export const DataStoreContactMigrations = [CreateContacts1659463079429, CreateCo
export const DataStoreIssuanceBrandingMigrations = [CreateIssuanceBranding1659463079429]
export const DataStoreStatusListMigrations = [CreateStatusList1693866470000]
export const DataStoreEventLoggerMigrations = [CreateAuditEvents1701635835330]
export const DataStoreUniformCredentialMigrations = [CreateUniformCredential1708525189000]

// All migrations together
export const DataStoreMigrations = [
...DataStoreContactMigrations,
...DataStoreIssuanceBrandingMigrations,
...DataStoreStatusListMigrations,
...DataStoreEventLoggerMigrations,
...DataStoreUniformCredentialMigrations,
]
1 change: 1 addition & 0 deletions packages/data-store/src/migrations/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ export {
DataStoreContactMigrations,
DataStoreIssuanceBrandingMigrations,
DataStoreStatusListMigrations,
DataStoreUniformCredentialMigrations,
} from './generic'
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { MigrationInterface, QueryRunner } from 'typeorm'

export class CreateUniformCredential1708525189001 implements MigrationInterface {
name = 'CreateUniformCredential1708525189001'

public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`CREATE TYPE "uniform_credential_credential_type_enum" AS ENUM('vc', 'vp')`)
await queryRunner.query(`CREATE TYPE "uniform_credential_document_format_enum" AS ENUM('JSON-LD', 'JWT', 'SD-JWT', 'MDOC')`)
await queryRunner.query(`CREATE TYPE "uniform_credential_correlation_type_enum" AS ENUM('did')`)
await queryRunner.query(`CREATE TYPE "uniform_credential_state_type_enum" AS ENUM('revoked', 'verified', 'expired')`)

await queryRunner.query(`
CREATE TABLE "UniformCredential" (
"id" uuid NOT NULL DEFAULT uuid_generate_v4(),
"credential_type" "uniform_credential_credential_type_enum" NOT NULL,
"document_format" "uniform_credential_document_format_enum" NOT NULL,
"raw" text NOT NULL,
"uniform_document" text NOT NULL,
"hash" text NOT NULL UNIQUE,
"issuer_correlation_type" "uniform_credential_correlation_type_enum" NOT NULL,
"subject_correlation_type" "uniform_credential_correlation_type_enum",
"issuer_correlation_id" text NOT NULL,
"subject_correlation_id" text,
"last_verified_state" "uniform_credential_state_type_enum",
"tenant_id" text,
"created_at" TIMESTAMP NOT NULL DEFAULT now(),
"last_updated_at" TIMESTAMP NOT NULL DEFAULT now(),
"expires_at" DATE,
"verification_date" DATE,
"revocation_date" DATE,
PRIMARY KEY ("id")
)
`)
}

public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`DROP TABLE "UniformCredential"`)
await queryRunner.query(`DROP TYPE "uniform_credential_state_type_enum"`)
await queryRunner.query(`DROP TYPE "uniform_credential_correlation_type_enum"`)
await queryRunner.query(`DROP TYPE "uniform_credential_document_format_enum"`)
await queryRunner.query(`DROP TYPE "uniform_credential_credential_type_enum"`)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { MigrationInterface, QueryRunner } from 'typeorm';

export class CreateUniformCredential1708525189002 implements MigrationInterface {
name = 'CreateUniformCredential1708525189002';

public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`
CREATE TABLE "UniformCredential" (
"id" varchar PRIMARY KEY NOT NULL,
"credential_type" varchar CHECK( "credential_type" IN ('vc', 'vp') ) NOT NULL,
"document_format" varchar CHECK( "document_format" IN ('JSON-LD', 'JWT', 'SD-JWT', 'MDOC') ) NOT NULL,
"raw" text NOT NULL,
"uniform_document" text NOT NULL,
"hash" text NOT NULL UNIQUE,
"issuer_correlation_type" varchar CHECK( "issuer_correlation_type" IN ('did') ) NOT NULL,
"subject_correlation_type" varchar CHECK( "subject_correlation_type" IN ('did') ),
"issuer_correlation_id" text NOT NULL,
"subject_correlation_id" text,
"last_verified_state" varchar CHECK( "last_verified_state" IN ('revoked', 'verified', 'expired') ),
"tenant_id" text,
"created_at" datetime NOT NULL DEFAULT (datetime('now')),
"last_updated_at" datetime NOT NULL DEFAULT (datetime('now')),
"expires_at" datetime,
"verification_date" datetime,
"revocation_date" datetime
)
`);
}

public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`DROP TABLE "UniformCredential"`);
}
}
Loading

0 comments on commit 312698e

Please sign in to comment.