diff --git a/packages/data-store/src/__tests__/digitalCredential.store.test.ts b/packages/data-store/src/__tests__/digitalCredential.store.test.ts index a3e42dddc..a913bf2f1 100644 --- a/packages/data-store/src/__tests__/digitalCredential.store.test.ts +++ b/packages/data-store/src/__tests__/digitalCredential.store.test.ts @@ -4,13 +4,15 @@ import { DataStoreDigitalCredentialMigrations } from '../migrations' import { CredentialRole, DataStoreDigitalCredentialEntities } from '../index' import { DigitalCredentialStore } from '../digitalCredential/DigitalCredentialStore' import { + AddCredentialArgs, CredentialCorrelationType, CredentialDocumentFormat, CredentialStateType, DocumentType, DigitalCredential, -} from '../types/digitalCredential/digitalCredential' -import { AddCredentialArgs, GetCredentialsArgs, GetCredentialsResponse } from '../types/digitalCredential/IAbstractDigitalCredentialStore' + GetCredentialsArgs, + GetCredentialsResponse, +} from '../types' import { IVerifiablePresentation } from '@sphereon/ssi-types' import { createHash } from 'crypto' @@ -247,6 +249,79 @@ describe('Database entities tests', (): void => { expect(result).toEqual(true) }) + it('should delete stored digital credential with children', async (): Promise => { + const rawCredential: string = + 'eyJraWQiOiJkaWQ6a2V5Ono2TWtyaGt5M3B1c20yNk1laUZhWFUzbjJuZWtyYW13RlVtZ0dyZUdHa0RWNnpRaiN6Nk1rcmhreTNwdXNtMjZNZWlGYVhVM24ybmVrcmFtd0ZVbWdHcmVHR2tEVjZ6UWoiLCJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSIsImh0dHBzOi8vc3BoZXJlb24tb3BlbnNvdXJjZS5naXRodWIuaW8vc3NpLW1vYmlsZS13YWxsZXQvY29udGV4dC9zcGhlcmVvbi13YWxsZXQtaWRlbnRpdHktdjEuanNvbmxkIl0sInR5cGUiOlsiVmVyaWZpYWJsZUNyZWRlbnRpYWwiLCJTcGhlcmVvbldhbGxldElkZW50aXR5Q3JlZGVudGlhbCJdLCJjcmVkZW50aWFsU3ViamVjdCI6eyJmaXJzdE5hbWUiOiJTIiwibGFzdE5hbWUiOiJLIiwiZW1haWxBZGRyZXNzIjoic0BrIn19LCJzdWIiOiJ1cm46dXVpZDpkZGE3YmYyNC04ZTdhLTQxZjgtYjY2Yy1hNDhkYmM1YjEwZmEiLCJqdGkiOiJ1cm46dXVpZDpkZGE3YmYyNC04ZTdhLTQxZjgtYjY2Yy1hNDhkYmM1YjEwZmEiLCJuYmYiOjE3MDg0NDA4MDgsImlzcyI6ImRpZDprZXk6ejZNa3Joa3kzcHVzbTI2TWVpRmFYVTNuMm5la3JhbXdGVW1nR3JlR0drRFY2elFqIn0.G0M84XVAxSmzGY-NQuB9NBofNrINSn6lvxW6761Vlq6ypvYgtc2xNdpiRmw8ryVNfnpzrr4Z5cB1RlrC05rJAw' + const digitalCredential1: AddCredentialArgs = { + rawDocument: rawCredential, + kmsKeyRef: 'testRef', + identifierMethod: 'did', + issuerCorrelationType: CredentialCorrelationType.DID, + subjectCorrelationType: CredentialCorrelationType.DID, + issuerCorrelationId: 'did:key:z6Mkrhky3pusm26MeiFaXU3n2nekramwFUmgGreGGkDV6zQj', + subjectCorrelationId: 'did:key:z6Mkrhky3pusm26MeiFaXU3n2nekramwFUmgGreGGkDV6zQj', + credentialRole: CredentialRole.VERIFIER, + tenantId: 'urn:uuid:nnag4b43-1e7a-98f8-a32c-a48dbc5b10mj', + } + + const parentCredential: DigitalCredential = await digitalCredentialStore.addCredential(digitalCredential1) + + const digitalCredential2: AddCredentialArgs = { + rawDocument: + 'eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NksiLCJraWQiOiJkaWQ6dmVsb2NpdHk6djI6MHhjMTY3MTUxNmMyMTQ1ZDcwYjM0MGY1NjBhYjFjYjU4Y2M0ZDhhMDUyOjE2Mzc4MjY4NTEwMzM4MToxOTg2I2tleS0xIn0.eyJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSJdLCJ0eXBlIjpbIk9wZW5CYWRnZUNyZWRlbnRpYWwiLCJWZXJpZmlhYmxlQ3JlZGVudGlhbCIsIlZlcmlmaWFibGVDcmVkZW50aWFsIl0sImlkIjoiZGlkOnZlbG9jaXR5OnYyOjB4YzE2NzE1MTZjMjE0NWQ3MGIzNDBmNTYwYWIxY2I1OGNjNGQ4YTA1MjoxNjM3ODI2ODUxMDMzODE6MTk4NiIsImNyZWRlbnRpYWxTdGF0dXMiOnsidHlwZSI6IlZlbG9jaXR5UmV2b2NhdGlvbkxpc3RKYW4yMDIxIiwiaWQiOiJldGhlcmV1bToweDFDMjk0NjFDNzQ4MGQxZDg1NzBkZjdjMEE0RjMxNEQwYkU4Y0Q1QmYvZ2V0UmV2b2tlZFN0YXR1cz9hZGRyZXNzPTB4YzE2NzE1MTZDMjE0NUQ3MEIzNDBGNTYwQUIxQ0I1OENjNEQ4YTA1MiZsaXN0SWQ9NTI5NDcyODgxNzIzMTQmaW5kZXg9NTYyNCJ9LCJsaW5rQ29kZUNvbW1pdG1lbnQiOnsidHlwZSI6IlZlbG9jaXR5Q3JlZGVudGlhbExpbmtDb2RlQ29tbWl0bWVudDIwMjIiLCJ2YWx1ZSI6IkVpQ3dJQmRUcmE4MVkyMjEzSVNJSXo0UDh5ejNvNDlXMStYczRmczVIc1BvMXc9PSJ9LCJpc3N1ZXIiOnsiaWQiOiJkaWQ6aW9uOkVpQmFLaWRocEhma2ZzZWpaT1UxY09YVnlhdnE4WUtfaFJfTGgwX1dCNTVQX0EifSwiY29udGVudEhhc2giOnsidHlwZSI6IlZlbG9jaXR5Q29udGVudEhhc2gyMDIwIiwidmFsdWUiOiJkNWUzMGI5Y2FlNDljYjM5MjRjZjVhZjIwMDUwOTE4ZWZjZDQ4ZTk2MzAzZTZhMDQ4NmQzZmE0ODk4NjQ1NDFlIn0sImNyZWRlbnRpYWxTY2hlbWEiOnsiaWQiOiJodHRwczovL3N0YWdpbmdyZWdpc3RyYXIudmVsb2NpdHluZXR3b3JrLmZvdW5kYXRpb24vc2NoZW1hcy9vcGVuLWJhZGdlLWNyZWRlbnRpYWwuc2NoZW1hLmpzb24iLCJ0eXBlIjoiSnNvblNjaGVtYVZhbGlkYXRvcjIwMTgifSwiY29uc2VudGVkQXQiOiIyMDIyLTExLTA3VDIxOjI0OjQ3LjcwM1oiLCJjcmVkZW50aWFsU3ViamVjdCI6eyJpZCI6ImRpZDpqd2s6ZXlKamNuWWlPaUp6WldOd01qVTJhekVpTENKcmRIa2lPaUpGUXlJc0luVnpaU0k2SW5OcFp5SXNJbmdpT2lKRFdVdEdjbmxOVWpOc2RubHpiemQ0UjBKeVN6QnJRMkZZYUdwSGFXdFdMV3MxT0dGSE1GSTBTWGh6SWl3aWVTSTZJakV0TkhoTFowcFBkRlZyYjNablJqVnFjVWMxTm1KeGFtbG5UMEpUTVdGT09FZEJOMUV5YURsUlJtc2lmUSJ9LCJpc3N1YW5jZURhdGUiOiIyMDIyLTExLTA3VDIxOjI5OjI5LjIzNVoifSwibmJmIjoxNjY3ODU2NTY5LCJqdGkiOiJkaWQ6dmVsb2NpdHk6djI6MHhjMTY3MTUxNmMyMTQ1ZDcwYjM0MGY1NjBhYjFjYjU4Y2M0ZDhhMDUyOjE2Mzc4MjY4NTEwMzM4MToxOTg2IiwiaXNzIjoiZGlkOmlvbjpFaUJhS2lkaHBIZmtmc2VqWk9VMWNPWFZ5YXZxOFlLX2hSX0xoMF9XQjU1UF9BIiwic3ViIjoiZGlkOmp3azpleUpqY25ZaU9pSnpaV053TWpVMmF6RWlMQ0pyZEhraU9pSkZReUlzSW5WelpTSTZJbk5wWnlJc0luZ2lPaUpEV1V0R2NubE5Vak5zZG5semJ6ZDRSMEp5U3pCclEyRllhR3BIYVd0V0xXczFPR0ZITUZJMFNYaHpJaXdpZVNJNklqRXROSGhMWjBwUGRGVnJiM1puUmpWcWNVYzFObUp4YW1sblQwSlRNV0ZPT0VkQk4xRXlhRGxSUm1zaWZRIiwiaWF0IjoxNjY3ODU2NTY5fQ.-SiM5d7UrYn1gdj2hU5T5LnLQzhIklOtoexbyebLMeha0K89vkujsbFN4HNFP2TSfRYFt0-jXwDaZ8RNKESwFA', + parentId: parentCredential.id, + kmsKeyRef: 'testRef', + identifierMethod: 'did', + issuerCorrelationType: CredentialCorrelationType.DID, + subjectCorrelationType: CredentialCorrelationType.DID, + issuerCorrelationId: 'did:key:z1Mkrhky3pusm26MeiFaXU3n2nekramwFUmgGreGGkDV6zQj', + subjectCorrelationId: 'did:key:z6Mkrhky3pusm26MeiFaXU3n2nekramwFUmgGreGGkDV6zQj', + credentialRole: CredentialRole.VERIFIER, + tenantId: 'urn:uuid:nnag4b43-1e7a-98f8-a32c-a48dbc5b10mj', + } + const savedDigitalCredential2: DigitalCredential = await digitalCredentialStore.addCredential(digitalCredential2) + let deleteResult: boolean = await digitalCredentialStore.removeCredential({ id: parentCredential.id }) + expect(deleteResult).toEqual(true) + await expect(() => digitalCredentialStore.getCredential({ id: savedDigitalCredential2.id })).rejects.toThrowError('No credential found for arg:') + }) + + it('should not delete stored parent digital credential if a child gets deleted', async (): Promise => { + const rawCredential: string = + 'eyJraWQiOiJkaWQ6a2V5Ono2TWtyaGt5M3B1c20yNk1laUZhWFUzbjJuZWtyYW13RlVtZ0dyZUdHa0RWNnpRaiN6Nk1rcmhreTNwdXNtMjZNZWlGYVhVM24ybmVrcmFtd0ZVbWdHcmVHR2tEVjZ6UWoiLCJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSIsImh0dHBzOi8vc3BoZXJlb24tb3BlbnNvdXJjZS5naXRodWIuaW8vc3NpLW1vYmlsZS13YWxsZXQvY29udGV4dC9zcGhlcmVvbi13YWxsZXQtaWRlbnRpdHktdjEuanNvbmxkIl0sInR5cGUiOlsiVmVyaWZpYWJsZUNyZWRlbnRpYWwiLCJTcGhlcmVvbldhbGxldElkZW50aXR5Q3JlZGVudGlhbCJdLCJjcmVkZW50aWFsU3ViamVjdCI6eyJmaXJzdE5hbWUiOiJTIiwibGFzdE5hbWUiOiJLIiwiZW1haWxBZGRyZXNzIjoic0BrIn19LCJzdWIiOiJ1cm46dXVpZDpkZGE3YmYyNC04ZTdhLTQxZjgtYjY2Yy1hNDhkYmM1YjEwZmEiLCJqdGkiOiJ1cm46dXVpZDpkZGE3YmYyNC04ZTdhLTQxZjgtYjY2Yy1hNDhkYmM1YjEwZmEiLCJuYmYiOjE3MDg0NDA4MDgsImlzcyI6ImRpZDprZXk6ejZNa3Joa3kzcHVzbTI2TWVpRmFYVTNuMm5la3JhbXdGVW1nR3JlR0drRFY2elFqIn0.G0M84XVAxSmzGY-NQuB9NBofNrINSn6lvxW6761Vlq6ypvYgtc2xNdpiRmw8ryVNfnpzrr4Z5cB1RlrC05rJAw' + const digitalCredential1: AddCredentialArgs = { + rawDocument: rawCredential, + kmsKeyRef: 'testRef', + identifierMethod: 'did', + issuerCorrelationType: CredentialCorrelationType.DID, + subjectCorrelationType: CredentialCorrelationType.DID, + issuerCorrelationId: 'did:key:z6Mkrhky3pusm26MeiFaXU3n2nekramwFUmgGreGGkDV6zQj', + subjectCorrelationId: 'did:key:z6Mkrhky3pusm26MeiFaXU3n2nekramwFUmgGreGGkDV6zQj', + credentialRole: CredentialRole.VERIFIER, + tenantId: 'urn:uuid:nnag4b43-1e7a-98f8-a32c-a48dbc5b10mj', + } + + const parentCredential: DigitalCredential = await digitalCredentialStore.addCredential(digitalCredential1) + + const digitalCredential2: AddCredentialArgs = { + rawDocument: + 'eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NksiLCJraWQiOiJkaWQ6dmVsb2NpdHk6djI6MHhjMTY3MTUxNmMyMTQ1ZDcwYjM0MGY1NjBhYjFjYjU4Y2M0ZDhhMDUyOjE2Mzc4MjY4NTEwMzM4MToxOTg2I2tleS0xIn0.eyJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSJdLCJ0eXBlIjpbIk9wZW5CYWRnZUNyZWRlbnRpYWwiLCJWZXJpZmlhYmxlQ3JlZGVudGlhbCIsIlZlcmlmaWFibGVDcmVkZW50aWFsIl0sImlkIjoiZGlkOnZlbG9jaXR5OnYyOjB4YzE2NzE1MTZjMjE0NWQ3MGIzNDBmNTYwYWIxY2I1OGNjNGQ4YTA1MjoxNjM3ODI2ODUxMDMzODE6MTk4NiIsImNyZWRlbnRpYWxTdGF0dXMiOnsidHlwZSI6IlZlbG9jaXR5UmV2b2NhdGlvbkxpc3RKYW4yMDIxIiwiaWQiOiJldGhlcmV1bToweDFDMjk0NjFDNzQ4MGQxZDg1NzBkZjdjMEE0RjMxNEQwYkU4Y0Q1QmYvZ2V0UmV2b2tlZFN0YXR1cz9hZGRyZXNzPTB4YzE2NzE1MTZDMjE0NUQ3MEIzNDBGNTYwQUIxQ0I1OENjNEQ4YTA1MiZsaXN0SWQ9NTI5NDcyODgxNzIzMTQmaW5kZXg9NTYyNCJ9LCJsaW5rQ29kZUNvbW1pdG1lbnQiOnsidHlwZSI6IlZlbG9jaXR5Q3JlZGVudGlhbExpbmtDb2RlQ29tbWl0bWVudDIwMjIiLCJ2YWx1ZSI6IkVpQ3dJQmRUcmE4MVkyMjEzSVNJSXo0UDh5ejNvNDlXMStYczRmczVIc1BvMXc9PSJ9LCJpc3N1ZXIiOnsiaWQiOiJkaWQ6aW9uOkVpQmFLaWRocEhma2ZzZWpaT1UxY09YVnlhdnE4WUtfaFJfTGgwX1dCNTVQX0EifSwiY29udGVudEhhc2giOnsidHlwZSI6IlZlbG9jaXR5Q29udGVudEhhc2gyMDIwIiwidmFsdWUiOiJkNWUzMGI5Y2FlNDljYjM5MjRjZjVhZjIwMDUwOTE4ZWZjZDQ4ZTk2MzAzZTZhMDQ4NmQzZmE0ODk4NjQ1NDFlIn0sImNyZWRlbnRpYWxTY2hlbWEiOnsiaWQiOiJodHRwczovL3N0YWdpbmdyZWdpc3RyYXIudmVsb2NpdHluZXR3b3JrLmZvdW5kYXRpb24vc2NoZW1hcy9vcGVuLWJhZGdlLWNyZWRlbnRpYWwuc2NoZW1hLmpzb24iLCJ0eXBlIjoiSnNvblNjaGVtYVZhbGlkYXRvcjIwMTgifSwiY29uc2VudGVkQXQiOiIyMDIyLTExLTA3VDIxOjI0OjQ3LjcwM1oiLCJjcmVkZW50aWFsU3ViamVjdCI6eyJpZCI6ImRpZDpqd2s6ZXlKamNuWWlPaUp6WldOd01qVTJhekVpTENKcmRIa2lPaUpGUXlJc0luVnpaU0k2SW5OcFp5SXNJbmdpT2lKRFdVdEdjbmxOVWpOc2RubHpiemQ0UjBKeVN6QnJRMkZZYUdwSGFXdFdMV3MxT0dGSE1GSTBTWGh6SWl3aWVTSTZJakV0TkhoTFowcFBkRlZyYjNablJqVnFjVWMxTm1KeGFtbG5UMEpUTVdGT09FZEJOMUV5YURsUlJtc2lmUSJ9LCJpc3N1YW5jZURhdGUiOiIyMDIyLTExLTA3VDIxOjI5OjI5LjIzNVoifSwibmJmIjoxNjY3ODU2NTY5LCJqdGkiOiJkaWQ6dmVsb2NpdHk6djI6MHhjMTY3MTUxNmMyMTQ1ZDcwYjM0MGY1NjBhYjFjYjU4Y2M0ZDhhMDUyOjE2Mzc4MjY4NTEwMzM4MToxOTg2IiwiaXNzIjoiZGlkOmlvbjpFaUJhS2lkaHBIZmtmc2VqWk9VMWNPWFZ5YXZxOFlLX2hSX0xoMF9XQjU1UF9BIiwic3ViIjoiZGlkOmp3azpleUpqY25ZaU9pSnpaV053TWpVMmF6RWlMQ0pyZEhraU9pSkZReUlzSW5WelpTSTZJbk5wWnlJc0luZ2lPaUpEV1V0R2NubE5Vak5zZG5semJ6ZDRSMEp5U3pCclEyRllhR3BIYVd0V0xXczFPR0ZITUZJMFNYaHpJaXdpZVNJNklqRXROSGhMWjBwUGRGVnJiM1puUmpWcWNVYzFObUp4YW1sblQwSlRNV0ZPT0VkQk4xRXlhRGxSUm1zaWZRIiwiaWF0IjoxNjY3ODU2NTY5fQ.-SiM5d7UrYn1gdj2hU5T5LnLQzhIklOtoexbyebLMeha0K89vkujsbFN4HNFP2TSfRYFt0-jXwDaZ8RNKESwFA', + parentId: parentCredential.id, + kmsKeyRef: 'testRef', + identifierMethod: 'did', + issuerCorrelationType: CredentialCorrelationType.DID, + subjectCorrelationType: CredentialCorrelationType.DID, + issuerCorrelationId: 'did:key:z1Mkrhky3pusm26MeiFaXU3n2nekramwFUmgGreGGkDV6zQj', + subjectCorrelationId: 'did:key:z6Mkrhky3pusm26MeiFaXU3n2nekramwFUmgGreGGkDV6zQj', + credentialRole: CredentialRole.VERIFIER, + tenantId: 'urn:uuid:nnag4b43-1e7a-98f8-a32c-a48dbc5b10mj', + } + const savedDigitalCredential2: DigitalCredential = await digitalCredentialStore.addCredential(digitalCredential2) + let deleteResult: boolean = await digitalCredentialStore.removeCredential({ id: savedDigitalCredential2.id }) + expect(deleteResult).toEqual(true) + const fetchedCredential: DigitalCredential = await digitalCredentialStore.getCredential({ id: parentCredential.id }) + expect(fetchedCredential.id).toEqual(parentCredential.id) + }) + it('should not delete stored digital credential if id not found', async (): Promise => { const result = await digitalCredentialStore.removeCredential({ id: 'unknown_id' }) expect(result).toEqual(false) diff --git a/packages/data-store/src/__tests__/eventLogger.entities.test.ts b/packages/data-store/src/__tests__/eventLogger.entities.test.ts index ceb979d77..e671ca9f3 100644 --- a/packages/data-store/src/__tests__/eventLogger.entities.test.ts +++ b/packages/data-store/src/__tests__/eventLogger.entities.test.ts @@ -1,10 +1,10 @@ import { DataSources } from '@sphereon/ssi-sdk.agent-config' import { PartyCorrelationType } from '@sphereon/ssi-sdk.core' -import { ActionType, InitiatorType, LogLevel, SubSystem, System, SystemCorrelationIdType } from '@sphereon/ssi-types' +import { ActionType, InitiatorType, LoggingEventType, LogLevel, SubSystem, System, SystemCorrelationIdType } from '@sphereon/ssi-types' import { DataSource } from 'typeorm' import { DataStoreEventLoggerEntities } from '../index' import { DataStoreEventLoggerMigrations } from '../migrations/generic' -import { auditEventEntityFrom, AuditEventEntity } from '../entities/eventLogger/AuditEventEntity' +import { AuditEventEntity, auditEventEntityFrom } from '../entities/eventLogger/AuditEventEntity' import { NonPersistedAuditLoggingEvent } from '../types' describe('Database entities tests', (): void => { @@ -31,6 +31,7 @@ describe('Database entities tests', (): void => { it('should save audit event to database', async (): Promise => { const auditEvent: NonPersistedAuditLoggingEvent = { + type: LoggingEventType.AUDIT, timestamp: new Date(), level: LogLevel.DEBUG, correlationId: 'b40b8474-58a2-4b23-9fde-bd6ee1902cdb', diff --git a/packages/data-store/src/__tests__/eventLogger.store.test.ts b/packages/data-store/src/__tests__/eventLogger.store.test.ts index 5abaa0db9..12d3fb89c 100644 --- a/packages/data-store/src/__tests__/eventLogger.store.test.ts +++ b/packages/data-store/src/__tests__/eventLogger.store.test.ts @@ -1,5 +1,5 @@ import { DataSources } from '@sphereon/ssi-sdk.agent-config' -import { ActionType, InitiatorType, LogLevel, SubSystem, System, SystemCorrelationIdType } from '@sphereon/ssi-types' +import { ActionType, InitiatorType, LoggingEventType, LogLevel, SubSystem, System, SystemCorrelationIdType } from '@sphereon/ssi-types' import { DataSource } from 'typeorm' import { DataStoreEventLoggerMigrations } from '../migrations' import { DataStoreEventLoggerEntities } from '../index' @@ -33,6 +33,7 @@ describe('Database entities tests', (): void => { it('should store audit event', async (): Promise => { const auditEvent: NonPersistedAuditLoggingEvent = { + type: LoggingEventType.AUDIT, timestamp: new Date(), level: LogLevel.DEBUG, correlationId: 'b40b8474-58a2-4b23-9fde-bd6ee1902cdb', @@ -58,6 +59,7 @@ describe('Database entities tests', (): void => { it('should get all audit events', async (): Promise => { const auditEvent: NonPersistedAuditLoggingEvent = { + type: LoggingEventType.AUDIT, timestamp: new Date(), level: LogLevel.DEBUG, correlationId: 'b40b8474-58a2-4b23-9fde-bd6ee1902cdb', @@ -89,6 +91,7 @@ describe('Database entities tests', (): void => { it('should get audit events by filter', async (): Promise => { const auditEvent: NonPersistedAuditLoggingEvent = { + type: LoggingEventType.AUDIT, timestamp: new Date(), level: LogLevel.DEBUG, correlationId: 'b40b8474-58a2-4b23-9fde-bd6ee1902cdb', diff --git a/packages/data-store/src/digitalCredential/DigitalCredentialStore.ts b/packages/data-store/src/digitalCredential/DigitalCredentialStore.ts index 840ececeb..d55f98d5c 100644 --- a/packages/data-store/src/digitalCredential/DigitalCredentialStore.ts +++ b/packages/data-store/src/digitalCredential/DigitalCredentialStore.ts @@ -106,9 +106,7 @@ export class DigitalCredentialStore extends AbstractDigitalCredentialStore { let affected: number = 0 const findResult = await dcRepo.findBy(query) for (const dc of findResult) { - if (dc.parentId !== null && dc.parentId !== undefined) { - affected += await this.deleteTree(dcRepo, { id: dc.parentId }) - } + affected += await this.deleteTree(dcRepo, { parentId: dc.id }) const result = await dcRepo.delete(dc.id) if (result.affected) { affected += result.affected diff --git a/packages/data-store/src/entities/eventLogger/AuditEventEntity.ts b/packages/data-store/src/entities/eventLogger/AuditEventEntity.ts index 6d562b893..007bd2af2 100644 --- a/packages/data-store/src/entities/eventLogger/AuditEventEntity.ts +++ b/packages/data-store/src/entities/eventLogger/AuditEventEntity.ts @@ -1,9 +1,11 @@ -import { ActionSubType, ActionType, InitiatorType, LogLevel, SubSystem, System, SystemCorrelationIdType } from '@sphereon/ssi-types' +import { ActionSubType, ActionType, InitiatorType, LoggingEventType, LogLevel, SubSystem, System, SystemCorrelationIdType } from '@sphereon/ssi-types' import { BaseEntity, Column, CreateDateColumn, Entity, PrimaryGeneratedColumn, UpdateDateColumn } from 'typeorm' -import { PartyCorrelationType } from '@sphereon/ssi-sdk.core' -import { NonPersistedAuditLoggingEvent } from '../../types' +import { CredentialType, PartyCorrelationType } from '@sphereon/ssi-sdk.core' +import { NonPersistedAuditLoggingEvent, NonPersistedActivityLoggingEvent } from '../../types' import { typeOrmDateTime } from '@sphereon/ssi-sdk.agent-config' +//TODO this entity, also contains some optional fields that are related to another event type (Activity) later we might want to refactor and reorganize this. +// For now I've added a discriminator value called eventType that can be one of the three types of events: 1. General, 2. Audit, and 3. Activity @Entity('AuditEvents') export class AuditEventEntity extends BaseEntity { @PrimaryGeneratedColumn('uuid') @@ -12,6 +14,9 @@ export class AuditEventEntity extends BaseEntity { @Column({ name: 'timestamp', nullable: false, unique: false, type: typeOrmDateTime() }) timestamp!: Date + @Column('simple-enum', { name: 'eventType', enum: LoggingEventType, nullable: false, unique: false }) + type!: LoggingEventType + @Column('simple-enum', { name: 'level', enum: LogLevel, nullable: false, unique: false }) level!: LogLevel @@ -54,6 +59,18 @@ export class AuditEventEntity extends BaseEntity { @Column('text', { name: 'description', nullable: false, unique: false }) description!: string + @Column('simple-enum', { name: 'credentialType', enum: CredentialType, nullable: true, unique: false }) + credentialType?: CredentialType + + @Column('text', { name: 'credentialHash', nullable: true, unique: false }) + credentialHash?: string + + @Column('text', { name: 'originalCredential', nullable: true, unique: false }) + originalCredential?: string + + @Column('text', { name: 'sharePurpose', nullable: true, unique: false }) + sharePurpose?: string + @Column('text', { name: 'data', nullable: true, unique: false }) data?: string @@ -69,6 +86,7 @@ export class AuditEventEntity extends BaseEntity { export const auditEventEntityFrom = (args: NonPersistedAuditLoggingEvent): AuditEventEntity => { const auditEventEntity: AuditEventEntity = new AuditEventEntity() + auditEventEntity.type = LoggingEventType.AUDIT auditEventEntity.timestamp = args.timestamp auditEventEntity.level = args.level auditEventEntity.correlationId = args.correlationId @@ -90,3 +108,32 @@ export const auditEventEntityFrom = (args: NonPersistedAuditLoggingEvent): Audit return auditEventEntity } + +export const activityEventEntityFrom = (args: NonPersistedActivityLoggingEvent): AuditEventEntity => { + const activityEventEntity: AuditEventEntity = new AuditEventEntity() + activityEventEntity.type = LoggingEventType.ACTIVITY + activityEventEntity.timestamp = args.timestamp + activityEventEntity.level = args.level + activityEventEntity.correlationId = args.correlationId + activityEventEntity.system = args.system + activityEventEntity.subSystemType = args.subSystemType + activityEventEntity.actionType = args.actionType + activityEventEntity.actionSubType = args.actionSubType + activityEventEntity.initiatorType = args.initiatorType + activityEventEntity.systemCorrelationIdType = args.systemCorrelationIdType + activityEventEntity.systemCorrelationId = args.systemCorrelationId + activityEventEntity.systemAlias = args.systemAlias + activityEventEntity.partyCorrelationType = args.partyCorrelationType + activityEventEntity.partyCorrelationId = args.partyCorrelationId + activityEventEntity.partyAlias = args.partyAlias + activityEventEntity.description = args.description + activityEventEntity.partyCorrelationType = args.partyCorrelationType + activityEventEntity.data = JSON.stringify(args.data) + activityEventEntity.sharePurpose = args.sharePurpose + activityEventEntity.credentialType = args.credentialType + activityEventEntity.originalCredential = args.originalCredential + activityEventEntity.credentialHash = args.credentialHash + activityEventEntity.diagnosticData = JSON.stringify(args.diagnosticData) + + return activityEventEntity +} diff --git a/packages/data-store/src/eventLogger/AbstractEventLoggerStore.ts b/packages/data-store/src/eventLogger/AbstractEventLoggerStore.ts index 42d324f2e..209da438b 100644 --- a/packages/data-store/src/eventLogger/AbstractEventLoggerStore.ts +++ b/packages/data-store/src/eventLogger/AbstractEventLoggerStore.ts @@ -1,7 +1,9 @@ -import { GetAuditEventsArgs, StoreAuditEventArgs } from '../types' -import { AuditLoggingEvent } from '@sphereon/ssi-sdk.core' +import { GetAuditEventsArgs, StoreActivityEventArgs, StoreAuditEventArgs } from '../types' +import { ActivityLoggingEvent, AuditLoggingEvent } from '@sphereon/ssi-sdk.core' export abstract class AbstractEventLoggerStore { abstract getAuditEvents(args: GetAuditEventsArgs): Promise> + abstract getActivityEvents(args: GetAuditEventsArgs): Promise> abstract storeAuditEvent(args: StoreAuditEventArgs): Promise + abstract storeActivityEvent(args: StoreActivityEventArgs): Promise } diff --git a/packages/data-store/src/eventLogger/EventLoggerStore.ts b/packages/data-store/src/eventLogger/EventLoggerStore.ts index 5aed175bd..a8c2d0fe0 100644 --- a/packages/data-store/src/eventLogger/EventLoggerStore.ts +++ b/packages/data-store/src/eventLogger/EventLoggerStore.ts @@ -1,10 +1,11 @@ import Debug, { Debugger } from 'debug' import { DataSource } from 'typeorm' -import { AuditLoggingEvent } from '@sphereon/ssi-sdk.core' +import { ActivityLoggingEvent, AuditLoggingEvent } from '@sphereon/ssi-sdk.core' import { OrPromise } from '@sphereon/ssi-types' import { AbstractEventLoggerStore } from './AbstractEventLoggerStore' -import { AuditEventEntity, auditEventEntityFrom } from '../entities/eventLogger/AuditEventEntity' -import { GetAuditEventsArgs, StoreAuditEventArgs } from '../types' +import { activityEventEntityFrom, AuditEventEntity, auditEventEntityFrom } from '../entities/eventLogger/AuditEventEntity' +import { GetActivityEventsArgs, GetAuditEventsArgs, StoreActivityEventArgs, StoreAuditEventArgs } from '../types' +import { LoggingEventType } from '@sphereon/ssi-types/dist' const debug: Debugger = Debug('sphereon:ssi-sdk:event-store') @@ -19,13 +20,35 @@ export class EventLoggerStore extends AbstractEventLoggerStore { getAuditEvents = async (args?: GetAuditEventsArgs): Promise> => { const connection: DataSource = await this.dbConnection // TODO apply everywhere debug('Getting audit events', args) + const where: any = {} + if (args?.filter) { + args.filter.forEach((filterCondition) => { + Object.assign(where, filterCondition) + }) + } const result: Array = await connection.getRepository(AuditEventEntity).find({ - ...(args?.filter && { where: args?.filter }), + where, }) return result.map((event: AuditEventEntity) => this.auditEventFrom(event)) } + getActivityEvents = async (args?: GetActivityEventsArgs): Promise> => { + const connection: DataSource = await this.dbConnection + debug('Getting activity events', args) + const where: any = {} + if (args?.filter) { + args.filter.forEach((filterCondition) => { + Object.assign(where, filterCondition) + }) + } + const result: Array = await connection.getRepository(AuditEventEntity).find({ + where, + }) + + return result.map((event: AuditEventEntity) => this.activityEventFrom(event)) + } + storeAuditEvent = async (args: StoreAuditEventArgs): Promise => { const { event } = args @@ -37,9 +60,47 @@ export class EventLoggerStore extends AbstractEventLoggerStore { return this.auditEventFrom(createdResult) } + storeActivityEvent = async (args: StoreActivityEventArgs): Promise => { + const { event } = args + + const activityEventEntity: AuditEventEntity = activityEventEntityFrom(event) + const connection: DataSource = await this.dbConnection + debug('Storing activity event', activityEventEntity) + const createdResult: AuditEventEntity = await connection.getRepository(AuditEventEntity).save(activityEventEntity) + + return this.activityEventFrom(createdResult) + } + private auditEventFrom = (event: AuditEventEntity): AuditLoggingEvent => { return { id: event.id, + type: LoggingEventType.AUDIT, + description: event.description, + timestamp: event.timestamp, + level: event.level, + correlationId: event.correlationId, + actionType: event.actionType, + actionSubType: event.actionSubType, + initiatorType: event.initiatorType, + partyAlias: event.partyAlias, + partyCorrelationId: event.partyCorrelationId, + partyCorrelationType: event.partyCorrelationType, + subSystemType: event.subSystemType, + system: event.system, + systemAlias: event.systemAlias, + systemCorrelationId: event.systemCorrelationId, + systemCorrelationIdType: event.systemCorrelationIdType, + ...(event.data && { data: JSON.parse(event.data) }), + ...(event.diagnosticData && { diagnosticData: JSON.parse(event.diagnosticData) }), + } + } + + private activityEventFrom = (event: AuditEventEntity): ActivityLoggingEvent => { + return { + id: event.id, + type: LoggingEventType.ACTIVITY, + credentialType: event.credentialType, + sharePurpose: event.sharePurpose, description: event.description, timestamp: event.timestamp, level: event.level, diff --git a/packages/data-store/src/migrations/postgres/1701634812183-CreateAuditEvents.ts b/packages/data-store/src/migrations/postgres/1701634812183-CreateAuditEvents.ts index 014593b62..5c8f4ac82 100644 --- a/packages/data-store/src/migrations/postgres/1701634812183-CreateAuditEvents.ts +++ b/packages/data-store/src/migrations/postgres/1701634812183-CreateAuditEvents.ts @@ -15,8 +15,36 @@ export class CreateAuditEvents1701634812183 implements MigrationInterface { await queryRunner.query(`CREATE TYPE "public"."Initiator_type_enum" AS ENUM('user', 'system', 'external')`) await queryRunner.query(`CREATE TYPE "public"."System_correlation_id_type_enum" AS ENUM('did', 'email', 'hostname', 'phone', 'user')`) await queryRunner.query(`CREATE TYPE "public"."Party_correlation_type_enum" AS ENUM('did', 'email', 'hostname', 'phone')`) + await queryRunner.query(`CREATE TYPE "public"."Event_type_enum" AS ENUM('audit', 'activity', 'general')`) + await queryRunner.query(`CREATE TYPE "public"."Event_credential_type_enum" AS ENUM('JSON_LD', 'JWT', 'SD_JWT', 'MSO_MDOC')`) await queryRunner.query( - `CREATE TABLE "AuditEvents" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "timestamp" TIMESTAMP NOT NULL, "level" "public"."Level_enum" NOT NULL, "correlationId" TEXT NOT NULL, "system" "public"."System_enum" NOT NULL, "subSystemType" "public"."Subsystem_type_enum" NOT NULL, "actionType" "public"."Action_type_enum" NOT NULL, "actionSubType" TEXT NOT NULL, "initiatorType" "public"."Initiator_type_enum" NOT NULL, "systemCorrelationIdType" "public"."System_correlation_id_type_enum", "systemCorrelationId" TEXT, "systemAlias" TEXT, "partyCorrelationType" "public"."Party_correlation_type_enum", "partyCorrelationId" TEXT, "partyAlias" TEXT, "description" TEXT NOT NULL, "data" TEXT, "diagnosticData" TEXT, "created_at" TIMESTAMP NOT NULL DEFAULT now(), "last_updated_at" TIMESTAMP NOT NULL DEFAULT now(), CONSTRAINT "PK_AuditEvents_id" PRIMARY KEY ("id"))`, + `CREATE TABLE "AuditEvents" ( + "id" uuid NOT NULL DEFAULT uuid_generate_v4(), + "eventType" "public"."Event_type_enum" NOT NULL, + "timestamp" TIMESTAMP NOT NULL, + "level" "public"."Level_enum" NOT NULL, + "correlationId" TEXT NOT NULL, + "system" "public"."System_enum" NOT NULL, + "subSystemType" "public"."Subsystem_type_enum" NOT NULL, + "actionType" "public"."Action_type_enum" NOT NULL, + "actionSubType" TEXT NOT NULL, + "initiatorType" "public"."Initiator_type_enum" NOT NULL, + "systemCorrelationIdType" "public"."System_correlation_id_type_enum", + "systemCorrelationId" TEXT, + "systemAlias" TEXT, + "partyCorrelationType" "public"."Party_correlation_type_enum", + "partyCorrelationId" TEXT, + "partyAlias" TEXT, + "credentialType" "public"."Event_credential_type_enum", + "credentialHash" TEXT, + "originalCredential" TEXT, + "sharePurpose" TEXT, + "description" TEXT NOT NULL, + "data" TEXT, + "diagnosticData" TEXT, + "created_at" TIMESTAMP NOT NULL DEFAULT now(), + "last_updated_at" TIMESTAMP NOT NULL DEFAULT now(), + CONSTRAINT "PK_AuditEvents_id" PRIMARY KEY ("id"))`, ) } diff --git a/packages/data-store/src/migrations/sqlite/1701634819487-CreateAuditEvents.ts b/packages/data-store/src/migrations/sqlite/1701634819487-CreateAuditEvents.ts index af504bb6a..1844dfbd3 100644 --- a/packages/data-store/src/migrations/sqlite/1701634819487-CreateAuditEvents.ts +++ b/packages/data-store/src/migrations/sqlite/1701634819487-CreateAuditEvents.ts @@ -5,7 +5,31 @@ export class CreateAuditEvents1701634819487 implements MigrationInterface { public async up(queryRunner: QueryRunner): Promise { await queryRunner.query( - `CREATE TABLE "AuditEvents" ("id" varchar PRIMARY KEY NOT NULL, "timestamp" datetime NOT NULL, "level" varchar CHECK( "level" IN ('0','1','2','3') ) NOT NULL, "correlationId" varchar NOT NULL, "system" varchar CHECK( "system" IN ('general','kms','identity','oid4vci','credentials','web3','profile','contact') ) NOT NULL, "subSystemType" varchar CHECK( "subSystemType" IN ('key','did_provider','did_resolver','oid4vp_op','oid4vci_client','siopv2_op','contact_manager','vc_issuer','vc_verifier','vc_persistence','transport','profile') ) NOT NULL, "actionType" varchar CHECK( "actionType" IN ('create','read','update','delete','execute') ) NOT NULL, "actionSubType" varchar NOT NULL, "initiatorType" varchar CHECK( "initiatorType" IN ('user','system','external') ) NOT NULL, "systemCorrelationIdType" varchar CHECK( "systemCorrelationIdType" IN ('did','email','hostname','phone','user') ), "systemCorrelationId" varchar, "systemAlias" varchar, "partyCorrelationType" varchar CHECK( "partyCorrelationType" IN ('did','email','hostname','phone') ), "partyCorrelationId" varchar, "partyAlias" varchar, "description" varchar NOT NULL, "data" varchar, "diagnosticData" varchar, "created_at" datetime NOT NULL DEFAULT (datetime('now')), "last_updated_at" datetime NOT NULL DEFAULT (datetime('now')))`, + `CREATE TABLE "AuditEvents" ( + "id" varchar PRIMARY KEY NOT NULL, + "eventType" varchar CHECK( "eventType" IN ('audit', 'activity', 'general') ) NOT NULL, + "timestamp" datetime NOT NULL, + "level" varchar CHECK( "level" IN ('0','1','2','3') ) NOT NULL, + "correlationId" varchar NOT NULL, + "system" varchar CHECK( "system" IN ('general','kms','identity','oid4vci','credentials','web3','profile','contact') ) NOT NULL, + "subSystemType" varchar CHECK( "subSystemType" IN ('key','did_provider','did_resolver','oid4vp_op','oid4vci_client','siopv2_op','contact_manager','vc_issuer','vc_verifier','vc_persistence','transport','profile') ) NOT NULL, + "actionType" varchar CHECK( "actionType" IN ('create','read','update','delete','execute') ) NOT NULL, + "actionSubType" varchar NOT NULL, "initiatorType" varchar CHECK( "initiatorType" IN ('user','system','external') ) NOT NULL, + "systemCorrelationIdType" varchar CHECK( "systemCorrelationIdType" IN ('did','email','hostname','phone','user') ), + "systemCorrelationId" varchar, + "systemAlias" varchar, + "partyCorrelationType" varchar CHECK( "partyCorrelationType" IN ('did','email','hostname','phone') ), + "partyCorrelationId" varchar, + "partyAlias" varchar, + "credentialType" varchar CHECK( "credentialType" IN ('JSON_LD', 'JWT', 'SD_JWT', 'MSO_MDOC') ), + "credentialHash" varchar, + "originalCredential" varchar, + "sharePurpose" varchar, + "description" varchar NOT NULL, + "data" varchar, + "diagnosticData" varchar, + "created_at" datetime NOT NULL DEFAULT (datetime('now')), + "last_updated_at" datetime NOT NULL DEFAULT (datetime('now')))`, ) } diff --git a/packages/data-store/src/types/eventLogger/IAbstractEventLoggerStore.ts b/packages/data-store/src/types/eventLogger/IAbstractEventLoggerStore.ts index 09522e3b5..cc80b2086 100644 --- a/packages/data-store/src/types/eventLogger/IAbstractEventLoggerStore.ts +++ b/packages/data-store/src/types/eventLogger/IAbstractEventLoggerStore.ts @@ -1,12 +1,22 @@ -import { PartialAuditLoggingEvent } from '@sphereon/ssi-sdk.core' -import { NonPersistedAuditLoggingEvent } from './eventLogger' +import { PartialActivityLoggingEvent, PartialAuditLoggingEvent } from '@sphereon/ssi-sdk.core' +import { NonPersistedActivityLoggingEvent, NonPersistedAuditLoggingEvent } from './eventLogger' export type FindAuditLoggingEventArgs = Array +export type FindActivityLoggingEventArgs = Array + export type StoreAuditEventArgs = { event: NonPersistedAuditLoggingEvent } +export type StoreActivityEventArgs = { + event: NonPersistedActivityLoggingEvent +} + export type GetAuditEventsArgs = { filter?: FindAuditLoggingEventArgs } + +export type GetActivityEventsArgs = { + filter?: FindActivityLoggingEventArgs +} diff --git a/packages/data-store/src/types/eventLogger/eventLogger.ts b/packages/data-store/src/types/eventLogger/eventLogger.ts index 0a9029227..c292ad3f1 100644 --- a/packages/data-store/src/types/eventLogger/eventLogger.ts +++ b/packages/data-store/src/types/eventLogger/eventLogger.ts @@ -1,3 +1,4 @@ -import { AuditLoggingEvent } from '@sphereon/ssi-sdk.core' +import { ActivityLoggingEvent, AuditLoggingEvent } from '@sphereon/ssi-sdk.core' export type NonPersistedAuditLoggingEvent = Omit +export type NonPersistedActivityLoggingEvent = Omit diff --git a/packages/event-logger/__tests__/shared/eventLoggerAgentLogic.ts b/packages/event-logger/__tests__/shared/eventLoggerAgentLogic.ts index 04f2eb5a1..baedd5218 100644 --- a/packages/event-logger/__tests__/shared/eventLoggerAgentLogic.ts +++ b/packages/event-logger/__tests__/shared/eventLoggerAgentLogic.ts @@ -1,7 +1,7 @@ -import { ActionType, InitiatorType, LogLevel, SubSystem, System, SystemCorrelationIdType } from '@sphereon/ssi-types' +import { ActionType, InitiatorType, LoggingEventType, LogLevel, SubSystem, System, SystemCorrelationIdType } from '@sphereon/ssi-types' import { TAgent } from '@veramo/core' -import { AuditLoggingEvent, PartyCorrelationType } from '@sphereon/ssi-sdk.core' -import { GetAuditEventsArgs, IEventLogger, NonPersistedAuditLoggingEvent } from '../../src' +import { ActivityLoggingEvent, AuditLoggingEvent, CredentialType, PartyCorrelationType } from '@sphereon/ssi-sdk.core' +import { GetActivityEventsArgs, GetAuditEventsArgs, IEventLogger, NonPersistedActivityLoggingEvent, NonPersistedAuditLoggingEvent } from '../../src' type ConfiguredAgent = TAgent @@ -18,6 +18,7 @@ export default (testContext: { getAgent: () => ConfiguredAgent; setup: () => Pro it('should store audit event', async (): Promise => { const auditEvent: NonPersistedAuditLoggingEvent = { + type: LoggingEventType.AUDIT, level: LogLevel.DEBUG, correlationId: 'b40b8474-58a2-4b23-9fde-bd6ee1902cdb', system: System.GENERAL, @@ -61,6 +62,7 @@ export default (testContext: { getAgent: () => ConfiguredAgent; setup: () => Pro it('should get audit events without filter', async (): Promise => { const auditEvent: NonPersistedAuditLoggingEvent = { + type: LoggingEventType.AUDIT, level: LogLevel.DEBUG, correlationId: 'b40b8474-58a2-4b23-9fde-bd6ee1902cdb', system: System.GENERAL, @@ -88,15 +90,16 @@ export default (testContext: { getAgent: () => ConfiguredAgent; setup: () => Pro it('should get audit events with filter', async (): Promise => { const auditEvent: NonPersistedAuditLoggingEvent = { + type: LoggingEventType.AUDIT, level: LogLevel.DEBUG, - correlationId: 'filter_test_correlation_id', + correlationId: 'filter_test_correlation_id_filter', system: System.GENERAL, subSystemType: SubSystem.DID_PROVIDER, actionType: ActionType.CREATE, actionSubType: 'Key generation', initiatorType: InitiatorType.EXTERNAL, systemCorrelationIdType: SystemCorrelationIdType.DID, - systemCorrelationId: 'did:example:123456789abcdefghi', + systemCorrelationId: 'did:example:223456789abcdefghi', systemAlias: 'test_alias', partyCorrelationType: PartyCorrelationType.DID, partyCorrelationId: 'did:example:123456789abcdefghi', @@ -115,5 +118,112 @@ export default (testContext: { getAgent: () => ConfiguredAgent; setup: () => Pro expect(result).toBeDefined() expect(result?.length).toEqual(1) }) + + it('should store activity event', async (): Promise => { + const activityEvent: NonPersistedActivityLoggingEvent = { + type: LoggingEventType.ACTIVITY, + level: LogLevel.DEBUG, + correlationId: 'b40b8474-58a2-4b23-9fde-bd6ee1902cdb', + system: System.GENERAL, + subSystemType: SubSystem.OID4VCI_CLIENT, + actionType: ActionType.CREATE, + actionSubType: 'Key generation', + initiatorType: InitiatorType.USER, + systemCorrelationIdType: SystemCorrelationIdType.DID, + systemCorrelationId: 'did:example:123456789abcdefghi', + systemAlias: 'test_alias', + partyCorrelationType: PartyCorrelationType.DID, + partyCorrelationId: 'did:example:123456789abcdefghi', + originalCredential: + 'eyJraWQiOiJFeEhrQk1XOWZtYmt2VjI2Nm1ScHVQMnNVWV9OX0VXSU4xbGFwVXpPOHJvIiwiYWxnIjoiRVMyNTYifQ .eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvbnMvY3JlZGVudGlhbHMvdjIiLCJodHRwczovL3d3dy53My5vcmcvbnMvY3JlZGVudGlhbHMvZXhhbXBsZXMvdjIiXSwiaWQiOiJodHRwOi8vdW5pdmVyc2l0eS5leGFtcGxlL2NyZWRlbnRpYWxzLzM3MzIiLCJ0eXBlIjpbIlZlcmlmaWFibGVDcmVkZW50aWFsIiwiRXhhbXBsZURlZ3JlZUNyZWRlbnRpYWwiXSwiaXNzdWVyIjoiaHR0cHM6Ly91bml2ZXJzaXR5LmV4YW1wbGUvaXNzdWVycy81NjUwNDkiLCJ2YWxpZEZyb20iOiIyMDEwLTAxLTAxVDAwOjAwOjAwWiIsImNyZWRlbnRpYWxTdWJqZWN0Ijp7ImlkIjoiZGlkOmV4YW1wbGU6ZWJmZWIxZjcxMmViYzZmMWMyNzZlMTJlYzIxIiwiZGVncmVlIjp7InR5cGUiOiJFeGFtcGxlQmFjaGVsb3JEZWdyZWUiLCJuYW1lIjoiQmFjaGVsb3Igb2YgU2NpZW5jZSBhbmQgQXJ0cyJ9fX0 .vtw4cyIRP7YDVPpsLMmD_5ibDOjFrYr1fUJ7S74VoLHO_FIxueM1Wv6_zP8dEeR8jGw3t9vVLVl5CTf_i1KoVQ', + credentialHash: '57575757', + credentialType: CredentialType.JWT, + partyAlias: 'test_alias', + description: 'test_description', + data: 'test_data_string', + diagnosticData: { data: 'test_data_string' }, + } + + const result: ActivityLoggingEvent = await agent.loggerLogActivityEvent({ event: activityEvent }) + + expect(result).toBeDefined() + expect(result?.id).toBeDefined() + expect(result?.timestamp).toBeDefined() + expect(result?.level).toEqual(activityEvent.level) + expect(result?.correlationId).toEqual(activityEvent.correlationId) + expect(result?.system).toEqual(activityEvent.system) + expect(result?.subSystemType).toEqual(activityEvent.subSystemType) + expect(result?.actionType).toEqual(activityEvent.actionType) + expect(result?.actionSubType).toEqual(activityEvent.actionSubType) + expect(result?.initiatorType).toEqual(activityEvent.initiatorType) + expect(result?.systemCorrelationIdType).toEqual(activityEvent.systemCorrelationIdType) + expect(result?.systemCorrelationId).toEqual(activityEvent.systemCorrelationId) + expect(result?.systemAlias).toEqual(activityEvent.systemAlias) + expect(result?.partyCorrelationType).toEqual(activityEvent.partyCorrelationType) + expect(result?.partyCorrelationId).toEqual(activityEvent.partyCorrelationId) + expect(result?.partyAlias).toEqual(activityEvent.partyAlias) + expect(result?.description).toEqual(activityEvent.description) + expect(result?.data).toEqual(activityEvent.data) + expect(result?.diagnosticData).toEqual(activityEvent.diagnosticData) + }) + + it('should get activity events without filter', async (): Promise => { + const activityEvent: NonPersistedActivityLoggingEvent = { + type: LoggingEventType.ACTIVITY, + level: LogLevel.DEBUG, + correlationId: 'b40b8474-58a2-4b23-9fde-bd6ee1902cdb', + system: System.GENERAL, + subSystemType: SubSystem.DID_PROVIDER, + actionType: ActionType.CREATE, + actionSubType: 'Key generation', + initiatorType: InitiatorType.EXTERNAL, + systemCorrelationIdType: SystemCorrelationIdType.DID, + systemCorrelationId: 'did:example:123456789abcdefghi', + systemAlias: 'test_alias', + partyCorrelationType: PartyCorrelationType.DID, + partyCorrelationId: 'did:example:123456789abcdefghi', + partyAlias: 'test_alias', + description: 'test_description', + data: 'test_data_string', + diagnosticData: { data: 'test_data_string' }, + } + + await agent.loggerLogAuditEvent({ event: activityEvent }) + const result: Array = await agent.loggerGetAuditEvents() + + expect(result).toBeDefined() + expect(result?.length).toBeGreaterThan(0) + }) + + it('should get activity events with filter', async (): Promise => { + const activityEvent: NonPersistedActivityLoggingEvent = { + type: LoggingEventType.ACTIVITY, + level: LogLevel.DEBUG, + correlationId: 'filter_test_correlation_id_activity', + system: System.GENERAL, + subSystemType: SubSystem.DID_PROVIDER, + actionType: ActionType.CREATE, + actionSubType: 'Key generation', + initiatorType: InitiatorType.EXTERNAL, + systemCorrelationIdType: SystemCorrelationIdType.DID, + systemCorrelationId: 'did:example:123456789abcdefghi', + systemAlias: 'test_alias', + partyCorrelationType: PartyCorrelationType.DID, + partyCorrelationId: 'did:example:123456789abcdefghi', + partyAlias: 'test_alias', + description: 'test_description', + data: 'test_data_string', + diagnosticData: { data: 'test_data_string' }, + } + + await agent.loggerLogActivityEvent({ event: activityEvent }) + const args: GetActivityEventsArgs = { + filter: [{ correlationId: activityEvent.correlationId }], + } + const result: Array = await agent.loggerGetActivityEvents(args) + + expect(result).toBeDefined() + expect(result?.length).toEqual(1) + }) }) } diff --git a/packages/event-logger/src/agent/EventLogger.ts b/packages/event-logger/src/agent/EventLogger.ts index d14612c7e..f8d3c7366 100644 --- a/packages/event-logger/src/agent/EventLogger.ts +++ b/packages/event-logger/src/agent/EventLogger.ts @@ -1,17 +1,34 @@ import { AbstractEventLoggerStore } from '@sphereon/ssi-sdk.data-store' import { IAgentPlugin } from '@veramo/core' import { Loggers, LoggingEventType, LogLevel, LogMethod } from '@sphereon/ssi-types' -import { AuditLoggingEvent } from '@sphereon/ssi-sdk.core' +import { ActivityLoggingEvent, AuditLoggingEvent } from '@sphereon/ssi-sdk.core' import { v4 as uuidv4 } from 'uuid' -import { NonPersistedAuditLoggingEvent, schema } from '../index' -import { EventLoggerOptions, GetAuditEventsArgs, IEventLogger, RequiredContext, LogAuditEventArgs, LoggingEvent } from '../types/IEventLogger' +import { + EventLoggerOptions, + GetActivityEventsArgs, + GetAuditEventsArgs, + IEventLogger, + LogActivityEventArgs, + LogEventArgs, + LoggingEvent, + NonPersistedActivityLoggingEvent, + NonPersistedAuditLoggingEvent, + RequiredContext, + schema, +} from '../index' /** * {@inheritDoc IEventLogger} */ // Exposing the methods here for any REST implementation -export const eventLoggerAuditMethods: Array = ['loggerGetAuditEvents', 'loggerLogAuditEvent', 'loggerLogGeneralEvent'] +export const eventLoggerAuditMethods: Array = [ + 'loggerGetAuditEvents', + 'loggerLogAuditEvent', + 'loggerLogGeneralEvent', + 'loggerLogActivityEvent', + 'loggerGetActivityEvents', +] export const eventLoggerMethods: Array = [...eventLoggerAuditMethods] export class EventLogger implements IAgentPlugin { @@ -24,6 +41,8 @@ export class EventLogger implements IAgentPlugin { loggerGetAuditEvents: this.loggerGetAuditEvents.bind(this), loggerLogAuditEvent: this.loggerLogAuditEvent.bind(this), loggerLogGeneralEvent: this.loggerLogGeneralEvent.bind(this), + loggerLogActivityEvent: this.loggerLogActivityEvent.bind(this), + loggerGetActivityEvents: this.loggerGetActivityEvents.bind(this), } constructor(options: EventLoggerOptions) { @@ -60,28 +79,39 @@ export class EventLogger implements IAgentPlugin { // TODO: We might also want to do this locally though, as these logs are not persisted typically await context.agent.loggerLogGeneralEvent({ event: event.data }) break + case LoggingEventType.ACTIVITY: + // Calling the context of the agent to make sure the REST client is called when configured + await context.agent.loggerLogActivityEvent({ event: event.data as NonPersistedActivityLoggingEvent }) + break default: return Promise.reject(Error(`Event type ${event.type} not supported`)) } } private async loggerGetAuditEvents(args?: GetAuditEventsArgs): Promise> { - const { filter } = args ?? {} - + const { filter = [] } = args ?? {} if (!this.store) { return Promise.reject(Error('No store available in options')) } + return this.store.getAuditEvents({ filter: [...filter, { type: LoggingEventType.AUDIT }] }) + } - return this.store.getAuditEvents({ filter }) + private async loggerGetActivityEvents(args?: GetActivityEventsArgs): Promise> { + const { filter = [] } = args ?? {} + + if (!this.store) { + return Promise.reject(Error('No store available in options')) + } + return this.store.getActivityEvents({ filter: [...filter, { type: LoggingEventType.ACTIVITY }] }) } - private async loggerLogGeneralEvent(args: LogAuditEventArgs): Promise { + private async loggerLogGeneralEvent(args: LogEventArgs): Promise { const { event } = args this.simpleLoggers.get(event.data.system).logl(event.data.level ?? LogLevel.INFO, event.data.data, event.data) return args.event } - private async loggerLogAuditEvent(args: LogAuditEventArgs): Promise { + private async loggerLogAuditEvent(args: LogEventArgs): Promise { const { event } = args if (!this.store) { @@ -91,6 +121,28 @@ export class EventLogger implements IAgentPlugin { return this.store.storeAuditEvent({ event: { ...event, + type: LoggingEventType.AUDIT, + system: event.system, + subSystemType: event.subSystemType, + initiatorType: event.initiatorType, + level: event.level ?? LogLevel.INFO, + correlationId: event.correlationId ?? uuidv4(), + timestamp: new Date(), + }, + }) + } + + private async loggerLogActivityEvent(args: LogActivityEventArgs): Promise { + const { event } = args + + if (!this.store) { + return Promise.reject(Error('No store available in options')) + } + + return this.store.storeActivityEvent({ + event: { + ...event, + type: LoggingEventType.ACTIVITY, system: event.system, subSystemType: event.subSystemType, initiatorType: event.initiatorType, diff --git a/packages/event-logger/src/types/IEventLogger.ts b/packages/event-logger/src/types/IEventLogger.ts index 7d39a0187..6c961a366 100644 --- a/packages/event-logger/src/types/IEventLogger.ts +++ b/packages/event-logger/src/types/IEventLogger.ts @@ -1,12 +1,20 @@ import { InitiatorType, LoggingEventType, LogLevel, SubSystem, System } from '@sphereon/ssi-types' import { IAgentContext, IPluginMethodMap } from '@veramo/core' -import { AuditLoggingEvent, NonPersistedAuditLoggingEvent as NPAuditLoggingEvent } from '@sphereon/ssi-sdk.core' -import { AbstractEventLoggerStore, FindAuditLoggingEventArgs } from '@sphereon/ssi-sdk.data-store' +import { + AuditLoggingEvent, + CredentialType, + NonPersistedAuditLoggingEvent as NPAuditLoggingEvent, + NonPersistedActivityLoggingEvent as NPActivityLoggingEvent, + ActivityLoggingEvent, +} from '@sphereon/ssi-sdk.core' +import { AbstractEventLoggerStore, FindActivityLoggingEventArgs, FindAuditLoggingEventArgs } from '@sphereon/ssi-sdk.data-store' export interface IEventLogger extends IPluginMethodMap { loggerGetAuditEvents(args?: GetAuditEventsArgs): Promise> - loggerLogAuditEvent(args: LogAuditEventArgs, context: RequiredContext): Promise - loggerLogGeneralEvent(args: LogAuditEventArgs, context: RequiredContext): Promise + loggerLogAuditEvent(args: LogEventArgs, context: RequiredContext): Promise + loggerLogGeneralEvent(args: LogEventArgs, context: RequiredContext): Promise + loggerLogActivityEvent(args: LogActivityEventArgs, context: RequiredContext): Promise + loggerGetActivityEvents(args?: GetActivityEventsArgs): Promise> } export interface EventLoggerGeneralLogOpts { @@ -27,8 +35,16 @@ export type GetAuditEventsArgs = { filter?: FindAuditLoggingEventArgs } -export type LogAuditEventArgs = { - event: NonPersistedAuditLoggingEvent +export type GetActivityEventsArgs = { + filter?: FindActivityLoggingEventArgs +} + +export type LogEventArgs = { + event: NonPersistedAuditLoggingEvent | NonPersistedActivityLoggingEvent +} + +export type LogActivityEventArgs = { + event: NonPersistedActivityLoggingEvent } export type NonPersistedAuditLoggingEvent = Omit & { @@ -37,9 +53,20 @@ export type NonPersistedAuditLoggingEvent = Omit & { + system: System + subSystemType: SubSystem + initiatorType: InitiatorType + originalCredential?: string + credentialHash?: string + credentialType?: CredentialType + sharePurpose?: string + data?: any +} + export type LoggingEvent = { type: LoggingEventType - data: NonPersistedAuditLoggingEvent + data: NonPersistedAuditLoggingEvent | NonPersistedActivityLoggingEvent } export type RequiredContext = IAgentContext diff --git a/packages/ssi-sdk-core/src/types/events.ts b/packages/ssi-sdk-core/src/types/events.ts index 574dc97d0..962882ec9 100644 --- a/packages/ssi-sdk-core/src/types/events.ts +++ b/packages/ssi-sdk-core/src/types/events.ts @@ -19,7 +19,7 @@ export enum PartyCorrelationType { PHONE = 'phone', } -export type AuditLoggingEvent = Omit & { +export type AuditLoggingEvent = Omit & { id: string // timestamp: Date // level: LogLevel @@ -39,8 +39,27 @@ export type AuditLoggingEvent = Omit & { data?: any // diagnosticData?: any } + +export enum CredentialType { + JSON_LD = 'JSON_LD', + JWT = 'JWT', + SD_JWT = 'SD_JWT', + MSO_MDOC = 'MSO_MDOC', +} + +//todo the fields credentialType, data, originalCredential and credentialHash should be required in this type +export type ActivityLoggingEvent = Omit & { + originalCredential?: string + credentialHash?: string + credentialType?: CredentialType + sharePurpose?: string + data?: any +} + export type PartialAuditLoggingEvent = Partial +export type PartialActivityLoggingEvent = Partial + export type NonPersistedAuditLoggingEvent = Omit< AuditLoggingEvent, 'id' | 'timestamp' | 'level' | 'correlationId' | 'system' | 'subSystemType' | 'initiatorType' @@ -52,6 +71,19 @@ export type NonPersistedAuditLoggingEvent = Omit< initiatorType?: InitiatorType } +export type NonPersistedActivityLoggingEvent = Omit< + ActivityLoggingEvent, + 'id' | 'timestamp' | 'level' | 'correlationId' | 'system' | 'subSystemType' | 'initiatorType' +> & { + level?: LogLevel + correlationId?: string + system?: System + subSystemType?: SubSystem + initiatorType?: InitiatorType + credentialType?: CredentialType + sharePurpose?: string +} + export type LoggingEvent = { type: LoggingEventType data: NonPersistedAuditLoggingEvent diff --git a/packages/ssi-types/src/logging/index.ts b/packages/ssi-types/src/logging/index.ts index 526571cfa..a96a2dde4 100644 --- a/packages/ssi-types/src/logging/index.ts +++ b/packages/ssi-types/src/logging/index.ts @@ -11,11 +11,12 @@ export enum LogLevel { export enum LoggingEventType { AUDIT = 'audit', + ACTIVITY = 'activity', GENERAL = 'general', } export interface SimpleLogEvent { - type: LoggingEventType.GENERAL + type: LoggingEventType level: LogLevel correlationId?: string timestamp: Date