@sphereon/ssi-sdk.data-store 0.30.1-unstable.4 → 0.30.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -201
- package/README.md +77 -77
- package/dist/contact/ContactStore.d.ts.map +1 -1
- package/dist/digitalCredential/DigitalCredentialStore.d.ts.map +1 -1
- package/dist/eventLogger/EventLoggerStore.d.ts.map +1 -1
- package/dist/issuanceBranding/IssuanceBrandingStore.d.ts.map +1 -1
- package/dist/migrations/internal-migrations-ormconfig.d.ts.map +1 -1
- package/dist/migrations/postgres/1708525189001-CreateDigitalCredential.js +33 -33
- package/dist/migrations/postgres/1708797018115-CreateMachineStateStore.js +16 -16
- package/dist/migrations/postgres/1715761125001-CreateContacts.js +33 -33
- package/dist/migrations/postgres/1716475165345-CreatePresentationDefinitions.js +12 -12
- package/dist/migrations/sqlite/1708525189002-CreateDigitalCredential.js +32 -32
- package/dist/migrations/sqlite/1708796002272-CreateMachineStateStore.js +15 -15
- package/dist/migrations/sqlite/1710438363002-CreateContacts.js +13 -13
- package/dist/migrations/sqlite/1715761125002-CreateContacts.js +32 -32
- package/dist/migrations/sqlite/1716475165344-CreatePresentationDefinitions.js +9 -9
- package/dist/presentationDefinition/PDStore.d.ts.map +1 -1
- package/dist/utils/SortingUtils.d.ts.map +1 -1
- package/dist/utils/contact/MappingUtils.d.ts.map +1 -1
- package/dist/utils/digitalCredential/MappingUtils.d.ts.map +1 -1
- package/dist/utils/digitalCredential/MappingUtils.js +4 -4
- package/dist/utils/digitalCredential/MappingUtils.js.map +1 -1
- package/dist/utils/presentationDefinition/MappingUtils.js +2 -2
- package/dist/utils/presentationDefinition/MappingUtils.js.map +1 -1
- package/package.json +8 -8
- package/src/__tests__/contact.entities.test.ts +2642 -2642
- package/src/__tests__/contact.store.test.ts +2649 -2649
- package/src/__tests__/digitalCredential.entities.test.ts +274 -274
- package/src/__tests__/digitalCredential.store.test.ts +330 -330
- package/src/__tests__/eventLogger.entities.test.ts +76 -76
- package/src/__tests__/eventLogger.store.test.ts +130 -130
- package/src/__tests__/issuanceBranding.entities.test.ts +846 -846
- package/src/__tests__/issuanceBranding.store.test.ts +1886 -1886
- package/src/__tests__/machineState.entities.test.ts +53 -53
- package/src/__tests__/machineState.store.test.ts +176 -176
- package/src/__tests__/pd-manager.entities.test.ts +73 -73
- package/src/__tests__/pd-manager.store.test.ts +193 -193
- package/src/contact/AbstractContactStore.ts +71 -71
- package/src/contact/ContactStore.ts +768 -768
- package/src/digitalCredential/AbstractDigitalCredentialStore.ts +21 -21
- package/src/digitalCredential/DigitalCredentialStore.ts +189 -189
- package/src/entities/contact/BaseContactEntity.ts +51 -51
- package/src/entities/contact/ConnectionEntity.ts +35 -35
- package/src/entities/contact/ContactMetadataItemEntity.ts +51 -51
- package/src/entities/contact/CorrelationIdentifierEntity.ts +43 -43
- package/src/entities/contact/DidAuthConfigEntity.ts +20 -20
- package/src/entities/contact/ElectronicAddressEntity.ts +70 -70
- package/src/entities/contact/IdentityEntity.ts +107 -107
- package/src/entities/contact/IdentityMetadataItemEntity.ts +48 -48
- package/src/entities/contact/NaturalPersonEntity.ts +44 -44
- package/src/entities/contact/OpenIdConfigEntity.ts +32 -32
- package/src/entities/contact/OrganizationEntity.ts +35 -35
- package/src/entities/contact/PartyEntity.ts +117 -117
- package/src/entities/contact/PartyRelationshipEntity.ts +68 -68
- package/src/entities/contact/PartyTypeEntity.ts +63 -63
- package/src/entities/contact/PhysicalAddressEntity.ts +95 -95
- package/src/entities/digitalCredential/DigitalCredentialEntity.ts +98 -98
- package/src/entities/eventLogger/AuditEventEntity.ts +92 -92
- package/src/entities/issuanceBranding/BackgroundAttributesEntity.ts +42 -42
- package/src/entities/issuanceBranding/BaseLocaleBrandingEntity.ts +87 -87
- package/src/entities/issuanceBranding/CredentialBrandingEntity.ts +79 -79
- package/src/entities/issuanceBranding/CredentialLocaleBrandingEntity.ts +33 -33
- package/src/entities/issuanceBranding/ImageAttributesEntity.ts +57 -57
- package/src/entities/issuanceBranding/ImageDimensionsEntity.ts +22 -22
- package/src/entities/issuanceBranding/IssuerBrandingEntity.ts +73 -73
- package/src/entities/issuanceBranding/IssuerLocaleBrandingEntity.ts +33 -33
- package/src/entities/issuanceBranding/TextAttributesEntity.ts +31 -31
- package/src/entities/machineState/MachineStateInfoEntity.ts +59 -59
- package/src/entities/presentationDefinition/PresentationDefinitionItemEntity.ts +44 -44
- package/src/entities/statusList2021/StatusList2021Entity.ts +96 -96
- package/src/entities/statusList2021/StatusList2021EntryEntity.ts +29 -29
- package/src/eventLogger/AbstractEventLoggerStore.ts +7 -7
- package/src/eventLogger/EventLoggerStore.ts +62 -62
- package/src/index.ts +160 -160
- package/src/issuanceBranding/IssuanceBrandingStore.ts +559 -559
- package/src/machineState/IAbstractMachineStateStore.ts +65 -65
- package/src/machineState/MachineStateStore.ts +149 -149
- package/src/migrations/generic/1-CreateContacts.ts +66 -66
- package/src/migrations/generic/10-CreatePresentationDefinitions.ts +66 -66
- package/src/migrations/generic/2-CreateIssuanceBranding.ts +64 -64
- package/src/migrations/generic/3-CreateContacts.ts +66 -66
- package/src/migrations/generic/4-CreateStatusList.ts +54 -54
- package/src/migrations/generic/5-CreateAuditEvents.ts +66 -66
- package/src/migrations/generic/6-CreateDigitalCredential.ts +66 -66
- package/src/migrations/generic/7-CreateMachineStateStore.ts +66 -66
- package/src/migrations/generic/8-CreateContacts.ts +66 -66
- package/src/migrations/generic/9-CreateContacts.ts +66 -66
- package/src/migrations/generic/index.ts +43 -43
- package/src/migrations/index.ts +10 -10
- package/src/migrations/postgres/1659463079428-CreateContacts.ts +63 -63
- package/src/migrations/postgres/1685628974232-CreateIssuanceBranding.ts +85 -85
- package/src/migrations/postgres/1690925872592-CreateContacts.ts +158 -158
- package/src/migrations/postgres/1693866470001-CreateStatusList.ts +24 -24
- package/src/migrations/postgres/1701634812183-CreateAuditEvents.ts +33 -33
- package/src/migrations/postgres/1708525189001-CreateDigitalCredential.ts +61 -61
- package/src/migrations/postgres/1708797018115-CreateMachineStateStore.ts +29 -29
- package/src/migrations/postgres/1710438363001-CreateContacts.ts +63 -63
- package/src/migrations/postgres/1715761125001-CreateContacts.ts +60 -60
- package/src/migrations/postgres/1716475165345-CreatePresentationDefinitions.ts +25 -25
- package/src/migrations/sqlite/1659463069549-CreateContacts.ts +110 -110
- package/src/migrations/sqlite/1685628973231-CreateIssuanceBranding.ts +119 -119
- package/src/migrations/sqlite/1690925872693-CreateContacts.ts +228 -228
- package/src/migrations/sqlite/1693866470000-CreateStatusList.ts +24 -24
- package/src/migrations/sqlite/1701634819487-CreateAuditEvents.ts +15 -15
- package/src/migrations/sqlite/1708525189002-CreateDigitalCredential.ts +46 -46
- package/src/migrations/sqlite/1708796002272-CreateMachineStateStore.ts +28 -28
- package/src/migrations/sqlite/1710438363002-CreateContacts.ts +83 -83
- package/src/migrations/sqlite/1715761125002-CreateContacts.ts +59 -59
- package/src/migrations/sqlite/1716475165344-CreatePresentationDefinitions.ts +24 -24
- package/src/presentationDefinition/AbstractPDStore.ts +20 -20
- package/src/presentationDefinition/PDStore.ts +185 -185
- package/src/statusList/IStatusListStore.ts +44 -44
- package/src/statusList/StatusListStore.ts +236 -236
- package/src/types/contact/IAbstractContactStore.ts +161 -161
- package/src/types/contact/contact.ts +295 -295
- package/src/types/digitalCredential/IAbstractDigitalCredentialStore.ts +42 -42
- package/src/types/digitalCredential/digitalCredential.ts +102 -102
- package/src/types/eventLogger/IAbstractEventLoggerStore.ts +12 -12
- package/src/types/eventLogger/eventLogger.ts +3 -3
- package/src/types/index.ts +14 -14
- package/src/types/machineState/IAbstractMachineStateStore.ts +68 -68
- package/src/types/presentationDefinition/IAbstractPDStore.ts +25 -25
- package/src/types/presentationDefinition/presentationDefinition.ts +17 -17
- package/src/utils/SortingUtils.ts +16 -16
- package/src/utils/contact/MappingUtils.ts +506 -506
- package/src/utils/digitalCredential/MappingUtils.ts +160 -160
- package/src/utils/hasher.ts +19 -19
- package/src/utils/presentationDefinition/MappingUtils.ts +52 -52
- package/dist/entities/contact/IMetadataEntity.d.ts +0 -8
- package/dist/entities/contact/IMetadataEntity.d.ts.map +0 -1
- package/dist/entities/contact/IMetadataEntity.js +0 -2
- package/dist/entities/contact/IMetadataEntity.js.map +0 -1
- package/dist/migrations/generic/8-CreatePresentationDefinitions.d.ts +0 -7
- package/dist/migrations/generic/8-CreatePresentationDefinitions.d.ts.map +0 -1
- package/dist/migrations/generic/8-CreatePresentationDefinitions.js +0 -78
- package/dist/migrations/generic/8-CreatePresentationDefinitions.js.map +0 -1
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
import {
|
|
2
|
-
AddCredentialArgs,
|
|
3
|
-
DigitalCredential,
|
|
4
|
-
GetCredentialArgs,
|
|
5
|
-
GetCredentialsArgs,
|
|
6
|
-
GetCredentialsResponse,
|
|
7
|
-
RemoveCredentialArgs,
|
|
8
|
-
UpdateCredentialStateArgs,
|
|
9
|
-
} from '../types'
|
|
10
|
-
|
|
11
|
-
export abstract class AbstractDigitalCredentialStore {
|
|
12
|
-
abstract getCredential(args: GetCredentialArgs): Promise<DigitalCredential>
|
|
13
|
-
|
|
14
|
-
abstract getCredentials(args?: GetCredentialsArgs): Promise<GetCredentialsResponse>
|
|
15
|
-
|
|
16
|
-
abstract addCredential(args: AddCredentialArgs): Promise<DigitalCredential>
|
|
17
|
-
|
|
18
|
-
abstract updateCredentialState(args: UpdateCredentialStateArgs): Promise<DigitalCredential>
|
|
19
|
-
|
|
20
|
-
abstract removeCredential(args: RemoveCredentialArgs): Promise<boolean>
|
|
21
|
-
}
|
|
1
|
+
import {
|
|
2
|
+
AddCredentialArgs,
|
|
3
|
+
DigitalCredential,
|
|
4
|
+
GetCredentialArgs,
|
|
5
|
+
GetCredentialsArgs,
|
|
6
|
+
GetCredentialsResponse,
|
|
7
|
+
RemoveCredentialArgs,
|
|
8
|
+
UpdateCredentialStateArgs,
|
|
9
|
+
} from '../types'
|
|
10
|
+
|
|
11
|
+
export abstract class AbstractDigitalCredentialStore {
|
|
12
|
+
abstract getCredential(args: GetCredentialArgs): Promise<DigitalCredential>
|
|
13
|
+
|
|
14
|
+
abstract getCredentials(args?: GetCredentialsArgs): Promise<GetCredentialsResponse>
|
|
15
|
+
|
|
16
|
+
abstract addCredential(args: AddCredentialArgs): Promise<DigitalCredential>
|
|
17
|
+
|
|
18
|
+
abstract updateCredentialState(args: UpdateCredentialStateArgs): Promise<DigitalCredential>
|
|
19
|
+
|
|
20
|
+
abstract removeCredential(args: RemoveCredentialArgs): Promise<boolean>
|
|
21
|
+
}
|
|
@@ -1,189 +1,189 @@
|
|
|
1
|
-
import { AbstractDigitalCredentialStore } from './AbstractDigitalCredentialStore'
|
|
2
|
-
import {
|
|
3
|
-
AddCredentialArgs,
|
|
4
|
-
CredentialRole,
|
|
5
|
-
CredentialStateType,
|
|
6
|
-
DigitalCredential,
|
|
7
|
-
GetCredentialArgs,
|
|
8
|
-
GetCredentialsArgs,
|
|
9
|
-
GetCredentialsResponse,
|
|
10
|
-
NonPersistedDigitalCredential,
|
|
11
|
-
RemoveCredentialArgs,
|
|
12
|
-
UpdateCredentialStateArgs,
|
|
13
|
-
} from '../types'
|
|
14
|
-
import { OrPromise } from '@sphereon/ssi-types'
|
|
15
|
-
import { DataSource, FindOptionsOrder, Repository } from 'typeorm'
|
|
16
|
-
import Debug from 'debug'
|
|
17
|
-
import { DigitalCredentialEntity } from '../entities/digitalCredential/DigitalCredentialEntity'
|
|
18
|
-
import {
|
|
19
|
-
digitalCredentialFrom,
|
|
20
|
-
digitalCredentialsFrom,
|
|
21
|
-
nonPersistedDigitalCredentialEntityFromAddArgs,
|
|
22
|
-
} from '../utils/digitalCredential/MappingUtils'
|
|
23
|
-
import { FindOptionsWhere } from 'typeorm/find-options/FindOptionsWhere'
|
|
24
|
-
import { parseAndValidateOrderOptions } from '../utils/SortingUtils'
|
|
25
|
-
|
|
26
|
-
const debug: Debug.Debugger = Debug('sphereon:ssi-sdk:credential-store')
|
|
27
|
-
|
|
28
|
-
export class DigitalCredentialStore extends AbstractDigitalCredentialStore {
|
|
29
|
-
private readonly dbConnection: OrPromise<DataSource>
|
|
30
|
-
private dcRepo: Repository<DigitalCredentialEntity> | undefined
|
|
31
|
-
|
|
32
|
-
constructor(dbConnection: OrPromise<DataSource>) {
|
|
33
|
-
super()
|
|
34
|
-
this.dbConnection = dbConnection
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
addCredential = async (args: AddCredentialArgs): Promise<DigitalCredential> => {
|
|
38
|
-
debug('Adding credential', args)
|
|
39
|
-
const credentialEntity: NonPersistedDigitalCredential = nonPersistedDigitalCredentialEntityFromAddArgs(args)
|
|
40
|
-
const validationError = this.assertValidDigitalCredential(credentialEntity)
|
|
41
|
-
if (validationError) {
|
|
42
|
-
return Promise.reject(validationError)
|
|
43
|
-
}
|
|
44
|
-
const dcRepo = await this.getRepository()
|
|
45
|
-
const createdResult: DigitalCredentialEntity = await dcRepo.save(credentialEntity)
|
|
46
|
-
return Promise.resolve(digitalCredentialFrom(createdResult))
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
getCredential = async (args: GetCredentialArgs): Promise<DigitalCredential> => {
|
|
50
|
-
const dcRepo = await this.getRepository()
|
|
51
|
-
const result: DigitalCredentialEntity | null = await dcRepo.findOne({
|
|
52
|
-
where: args,
|
|
53
|
-
})
|
|
54
|
-
|
|
55
|
-
if (!result) {
|
|
56
|
-
return Promise.reject(Error(`No credential found for arg: ${JSON.stringify(args)}`))
|
|
57
|
-
}
|
|
58
|
-
return digitalCredentialFrom(result)
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
getCredentials = async (args?: GetCredentialsArgs): Promise<GetCredentialsResponse> => {
|
|
62
|
-
const { filter = {}, offset, limit, order = 'createdAt.asc' } = args ?? {}
|
|
63
|
-
const sortOptions: FindOptionsOrder<DigitalCredentialEntity> =
|
|
64
|
-
order && typeof order === 'string'
|
|
65
|
-
? parseAndValidateOrderOptions<DigitalCredentialEntity>(order)
|
|
66
|
-
: <FindOptionsOrder<DigitalCredentialEntity>>order
|
|
67
|
-
const dcRepo = await this.getRepository()
|
|
68
|
-
const [result, total] = await dcRepo.findAndCount({
|
|
69
|
-
where: filter,
|
|
70
|
-
skip: offset,
|
|
71
|
-
take: limit,
|
|
72
|
-
order: sortOptions,
|
|
73
|
-
})
|
|
74
|
-
return {
|
|
75
|
-
data: digitalCredentialsFrom(result),
|
|
76
|
-
total,
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
removeCredential = async (args: RemoveCredentialArgs): Promise<boolean> => {
|
|
81
|
-
if (!args) {
|
|
82
|
-
return false
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
let query: FindOptionsWhere<DigitalCredentialEntity> = {}
|
|
86
|
-
|
|
87
|
-
if ('id' in args) {
|
|
88
|
-
query.id = args.id
|
|
89
|
-
} else if ('hash' in args) {
|
|
90
|
-
query.hash = args.hash
|
|
91
|
-
} else {
|
|
92
|
-
return false
|
|
93
|
-
}
|
|
94
|
-
try {
|
|
95
|
-
const dcRepo = await this.getRepository()
|
|
96
|
-
// TODO create a flag whether we want to delete recursively or return an error when there are child credentials?
|
|
97
|
-
const affected = await this.deleteTree(dcRepo, query)
|
|
98
|
-
return affected > 0
|
|
99
|
-
} catch (error) {
|
|
100
|
-
console.error('Error removing digital credential:', error)
|
|
101
|
-
return false
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
private async deleteTree(dcRepo: Repository<DigitalCredentialEntity>, query: FindOptionsWhere<DigitalCredentialEntity>): Promise<number> {
|
|
106
|
-
let affected: number = 0
|
|
107
|
-
const findResult = await dcRepo.findBy(query)
|
|
108
|
-
for (const dc of findResult) {
|
|
109
|
-
if (dc.parentId !== null && dc.parentId !== undefined) {
|
|
110
|
-
affected += await this.deleteTree(dcRepo, { id: dc.parentId })
|
|
111
|
-
}
|
|
112
|
-
const result = await dcRepo.delete(dc.id)
|
|
113
|
-
if (result.affected) {
|
|
114
|
-
affected += result.affected
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
return affected
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
private async getRepository(): Promise<Repository<DigitalCredentialEntity>> {
|
|
121
|
-
if (this.dcRepo !== undefined) {
|
|
122
|
-
return Promise.resolve(this.dcRepo)
|
|
123
|
-
}
|
|
124
|
-
this.dcRepo = (await this.dbConnection).getRepository(DigitalCredentialEntity)
|
|
125
|
-
if (this.dcRepo === undefined) {
|
|
126
|
-
return Promise.reject(Error('Could not get DigitalCredentialEntity repository'))
|
|
127
|
-
}
|
|
128
|
-
return this.dcRepo
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
updateCredentialState = async (args: UpdateCredentialStateArgs): Promise<DigitalCredential> => {
|
|
132
|
-
const credentialRepository: Repository<DigitalCredentialEntity> = (await this.dbConnection).getRepository(DigitalCredentialEntity)
|
|
133
|
-
const whereClause: Record<string, any> = {}
|
|
134
|
-
if ('id' in args) {
|
|
135
|
-
whereClause.id = args.id
|
|
136
|
-
} else if ('hash' in args) {
|
|
137
|
-
whereClause.hash = args.hash
|
|
138
|
-
} else {
|
|
139
|
-
throw new Error('No id or hash param is provided.')
|
|
140
|
-
}
|
|
141
|
-
if (!args.verifiedState) {
|
|
142
|
-
throw new Error('No verifiedState param is provided.')
|
|
143
|
-
}
|
|
144
|
-
if (args.verifiedState === CredentialStateType.REVOKED && !args.revokedAt) {
|
|
145
|
-
throw new Error('No revokedAt param is provided.')
|
|
146
|
-
}
|
|
147
|
-
if (args.verifiedState !== CredentialStateType.REVOKED && !args.verifiedAt) {
|
|
148
|
-
throw new Error('No verifiedAt param is provided.')
|
|
149
|
-
}
|
|
150
|
-
const credential: DigitalCredentialEntity | null = await credentialRepository.findOne({
|
|
151
|
-
where: whereClause,
|
|
152
|
-
})
|
|
153
|
-
|
|
154
|
-
if (!credential) {
|
|
155
|
-
return Promise.reject(Error(`No credential found for args: ${JSON.stringify(whereClause)}`))
|
|
156
|
-
}
|
|
157
|
-
const updatedCredential: DigitalCredential = {
|
|
158
|
-
...credential,
|
|
159
|
-
...(args.verifiedState !== CredentialStateType.REVOKED && { verifiedAt: args.verifiedAt }),
|
|
160
|
-
...(args.verifiedState === CredentialStateType.REVOKED && { revokedAt: args.revokedAt }),
|
|
161
|
-
identifierMethod: credential.identifierMethod,
|
|
162
|
-
lastUpdatedAt: new Date(),
|
|
163
|
-
verifiedState: args.verifiedState,
|
|
164
|
-
}
|
|
165
|
-
debug('Updating credential', credential)
|
|
166
|
-
const updatedResult: DigitalCredentialEntity = await credentialRepository.save(updatedCredential, { transaction: true })
|
|
167
|
-
return digitalCredentialFrom(updatedResult)
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
private assertValidDigitalCredential(credentialEntity: NonPersistedDigitalCredential): Error | undefined {
|
|
171
|
-
const { kmsKeyRef, identifierMethod, credentialRole, isIssuerSigned } = credentialEntity
|
|
172
|
-
|
|
173
|
-
const isRoleInvalid = credentialRole === CredentialRole.ISSUER || (credentialRole === CredentialRole.HOLDER && !isIssuerSigned)
|
|
174
|
-
|
|
175
|
-
if (isRoleInvalid && (!kmsKeyRef || !identifierMethod)) {
|
|
176
|
-
const missingFields = []
|
|
177
|
-
|
|
178
|
-
if (!kmsKeyRef) missingFields.push('kmsKeyRef')
|
|
179
|
-
if (!identifierMethod) missingFields.push('identifierMethod')
|
|
180
|
-
|
|
181
|
-
const fields = missingFields.join(' and ')
|
|
182
|
-
return new Error(
|
|
183
|
-
`DigitalCredential field(s) ${fields} is/are required for credential role ${credentialRole} with isIssuerSigned=${isIssuerSigned}.`,
|
|
184
|
-
)
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
return undefined
|
|
188
|
-
}
|
|
189
|
-
}
|
|
1
|
+
import { AbstractDigitalCredentialStore } from './AbstractDigitalCredentialStore'
|
|
2
|
+
import {
|
|
3
|
+
AddCredentialArgs,
|
|
4
|
+
CredentialRole,
|
|
5
|
+
CredentialStateType,
|
|
6
|
+
DigitalCredential,
|
|
7
|
+
GetCredentialArgs,
|
|
8
|
+
GetCredentialsArgs,
|
|
9
|
+
GetCredentialsResponse,
|
|
10
|
+
NonPersistedDigitalCredential,
|
|
11
|
+
RemoveCredentialArgs,
|
|
12
|
+
UpdateCredentialStateArgs,
|
|
13
|
+
} from '../types'
|
|
14
|
+
import { OrPromise } from '@sphereon/ssi-types'
|
|
15
|
+
import { DataSource, FindOptionsOrder, Repository } from 'typeorm'
|
|
16
|
+
import Debug from 'debug'
|
|
17
|
+
import { DigitalCredentialEntity } from '../entities/digitalCredential/DigitalCredentialEntity'
|
|
18
|
+
import {
|
|
19
|
+
digitalCredentialFrom,
|
|
20
|
+
digitalCredentialsFrom,
|
|
21
|
+
nonPersistedDigitalCredentialEntityFromAddArgs,
|
|
22
|
+
} from '../utils/digitalCredential/MappingUtils'
|
|
23
|
+
import { FindOptionsWhere } from 'typeorm/find-options/FindOptionsWhere'
|
|
24
|
+
import { parseAndValidateOrderOptions } from '../utils/SortingUtils'
|
|
25
|
+
|
|
26
|
+
const debug: Debug.Debugger = Debug('sphereon:ssi-sdk:credential-store')
|
|
27
|
+
|
|
28
|
+
export class DigitalCredentialStore extends AbstractDigitalCredentialStore {
|
|
29
|
+
private readonly dbConnection: OrPromise<DataSource>
|
|
30
|
+
private dcRepo: Repository<DigitalCredentialEntity> | undefined
|
|
31
|
+
|
|
32
|
+
constructor(dbConnection: OrPromise<DataSource>) {
|
|
33
|
+
super()
|
|
34
|
+
this.dbConnection = dbConnection
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
addCredential = async (args: AddCredentialArgs): Promise<DigitalCredential> => {
|
|
38
|
+
debug('Adding credential', args)
|
|
39
|
+
const credentialEntity: NonPersistedDigitalCredential = nonPersistedDigitalCredentialEntityFromAddArgs(args)
|
|
40
|
+
const validationError = this.assertValidDigitalCredential(credentialEntity)
|
|
41
|
+
if (validationError) {
|
|
42
|
+
return Promise.reject(validationError)
|
|
43
|
+
}
|
|
44
|
+
const dcRepo = await this.getRepository()
|
|
45
|
+
const createdResult: DigitalCredentialEntity = await dcRepo.save(credentialEntity)
|
|
46
|
+
return Promise.resolve(digitalCredentialFrom(createdResult))
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
getCredential = async (args: GetCredentialArgs): Promise<DigitalCredential> => {
|
|
50
|
+
const dcRepo = await this.getRepository()
|
|
51
|
+
const result: DigitalCredentialEntity | null = await dcRepo.findOne({
|
|
52
|
+
where: args,
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
if (!result) {
|
|
56
|
+
return Promise.reject(Error(`No credential found for arg: ${JSON.stringify(args)}`))
|
|
57
|
+
}
|
|
58
|
+
return digitalCredentialFrom(result)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
getCredentials = async (args?: GetCredentialsArgs): Promise<GetCredentialsResponse> => {
|
|
62
|
+
const { filter = {}, offset, limit, order = 'createdAt.asc' } = args ?? {}
|
|
63
|
+
const sortOptions: FindOptionsOrder<DigitalCredentialEntity> =
|
|
64
|
+
order && typeof order === 'string'
|
|
65
|
+
? parseAndValidateOrderOptions<DigitalCredentialEntity>(order)
|
|
66
|
+
: <FindOptionsOrder<DigitalCredentialEntity>>order
|
|
67
|
+
const dcRepo = await this.getRepository()
|
|
68
|
+
const [result, total] = await dcRepo.findAndCount({
|
|
69
|
+
where: filter,
|
|
70
|
+
skip: offset,
|
|
71
|
+
take: limit,
|
|
72
|
+
order: sortOptions,
|
|
73
|
+
})
|
|
74
|
+
return {
|
|
75
|
+
data: digitalCredentialsFrom(result),
|
|
76
|
+
total,
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
removeCredential = async (args: RemoveCredentialArgs): Promise<boolean> => {
|
|
81
|
+
if (!args) {
|
|
82
|
+
return false
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
let query: FindOptionsWhere<DigitalCredentialEntity> = {}
|
|
86
|
+
|
|
87
|
+
if ('id' in args) {
|
|
88
|
+
query.id = args.id
|
|
89
|
+
} else if ('hash' in args) {
|
|
90
|
+
query.hash = args.hash
|
|
91
|
+
} else {
|
|
92
|
+
return false
|
|
93
|
+
}
|
|
94
|
+
try {
|
|
95
|
+
const dcRepo = await this.getRepository()
|
|
96
|
+
// TODO create a flag whether we want to delete recursively or return an error when there are child credentials?
|
|
97
|
+
const affected = await this.deleteTree(dcRepo, query)
|
|
98
|
+
return affected > 0
|
|
99
|
+
} catch (error) {
|
|
100
|
+
console.error('Error removing digital credential:', error)
|
|
101
|
+
return false
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
private async deleteTree(dcRepo: Repository<DigitalCredentialEntity>, query: FindOptionsWhere<DigitalCredentialEntity>): Promise<number> {
|
|
106
|
+
let affected: number = 0
|
|
107
|
+
const findResult = await dcRepo.findBy(query)
|
|
108
|
+
for (const dc of findResult) {
|
|
109
|
+
if (dc.parentId !== null && dc.parentId !== undefined) {
|
|
110
|
+
affected += await this.deleteTree(dcRepo, { id: dc.parentId })
|
|
111
|
+
}
|
|
112
|
+
const result = await dcRepo.delete(dc.id)
|
|
113
|
+
if (result.affected) {
|
|
114
|
+
affected += result.affected
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return affected
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
private async getRepository(): Promise<Repository<DigitalCredentialEntity>> {
|
|
121
|
+
if (this.dcRepo !== undefined) {
|
|
122
|
+
return Promise.resolve(this.dcRepo)
|
|
123
|
+
}
|
|
124
|
+
this.dcRepo = (await this.dbConnection).getRepository(DigitalCredentialEntity)
|
|
125
|
+
if (this.dcRepo === undefined) {
|
|
126
|
+
return Promise.reject(Error('Could not get DigitalCredentialEntity repository'))
|
|
127
|
+
}
|
|
128
|
+
return this.dcRepo
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
updateCredentialState = async (args: UpdateCredentialStateArgs): Promise<DigitalCredential> => {
|
|
132
|
+
const credentialRepository: Repository<DigitalCredentialEntity> = (await this.dbConnection).getRepository(DigitalCredentialEntity)
|
|
133
|
+
const whereClause: Record<string, any> = {}
|
|
134
|
+
if ('id' in args) {
|
|
135
|
+
whereClause.id = args.id
|
|
136
|
+
} else if ('hash' in args) {
|
|
137
|
+
whereClause.hash = args.hash
|
|
138
|
+
} else {
|
|
139
|
+
throw new Error('No id or hash param is provided.')
|
|
140
|
+
}
|
|
141
|
+
if (!args.verifiedState) {
|
|
142
|
+
throw new Error('No verifiedState param is provided.')
|
|
143
|
+
}
|
|
144
|
+
if (args.verifiedState === CredentialStateType.REVOKED && !args.revokedAt) {
|
|
145
|
+
throw new Error('No revokedAt param is provided.')
|
|
146
|
+
}
|
|
147
|
+
if (args.verifiedState !== CredentialStateType.REVOKED && !args.verifiedAt) {
|
|
148
|
+
throw new Error('No verifiedAt param is provided.')
|
|
149
|
+
}
|
|
150
|
+
const credential: DigitalCredentialEntity | null = await credentialRepository.findOne({
|
|
151
|
+
where: whereClause,
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
if (!credential) {
|
|
155
|
+
return Promise.reject(Error(`No credential found for args: ${JSON.stringify(whereClause)}`))
|
|
156
|
+
}
|
|
157
|
+
const updatedCredential: DigitalCredential = {
|
|
158
|
+
...credential,
|
|
159
|
+
...(args.verifiedState !== CredentialStateType.REVOKED && { verifiedAt: args.verifiedAt }),
|
|
160
|
+
...(args.verifiedState === CredentialStateType.REVOKED && { revokedAt: args.revokedAt }),
|
|
161
|
+
identifierMethod: credential.identifierMethod,
|
|
162
|
+
lastUpdatedAt: new Date(),
|
|
163
|
+
verifiedState: args.verifiedState,
|
|
164
|
+
}
|
|
165
|
+
debug('Updating credential', credential)
|
|
166
|
+
const updatedResult: DigitalCredentialEntity = await credentialRepository.save(updatedCredential, { transaction: true })
|
|
167
|
+
return digitalCredentialFrom(updatedResult)
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
private assertValidDigitalCredential(credentialEntity: NonPersistedDigitalCredential): Error | undefined {
|
|
171
|
+
const { kmsKeyRef, identifierMethod, credentialRole, isIssuerSigned } = credentialEntity
|
|
172
|
+
|
|
173
|
+
const isRoleInvalid = credentialRole === CredentialRole.ISSUER || (credentialRole === CredentialRole.HOLDER && !isIssuerSigned)
|
|
174
|
+
|
|
175
|
+
if (isRoleInvalid && (!kmsKeyRef || !identifierMethod)) {
|
|
176
|
+
const missingFields = []
|
|
177
|
+
|
|
178
|
+
if (!kmsKeyRef) missingFields.push('kmsKeyRef')
|
|
179
|
+
if (!identifierMethod) missingFields.push('identifierMethod')
|
|
180
|
+
|
|
181
|
+
const fields = missingFields.join(' and ')
|
|
182
|
+
return new Error(
|
|
183
|
+
`DigitalCredential field(s) ${fields} is/are required for credential role ${credentialRole} with isIssuerSigned=${isIssuerSigned}.`,
|
|
184
|
+
)
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return undefined
|
|
188
|
+
}
|
|
189
|
+
}
|
|
@@ -1,51 +1,51 @@
|
|
|
1
|
-
import {
|
|
2
|
-
BaseEntity,
|
|
3
|
-
BeforeInsert,
|
|
4
|
-
BeforeUpdate,
|
|
5
|
-
CreateDateColumn,
|
|
6
|
-
Entity,
|
|
7
|
-
JoinColumn,
|
|
8
|
-
OneToMany,
|
|
9
|
-
OneToOne,
|
|
10
|
-
PrimaryGeneratedColumn,
|
|
11
|
-
TableInheritance,
|
|
12
|
-
UpdateDateColumn,
|
|
13
|
-
} from 'typeorm'
|
|
14
|
-
import { typeOrmDateTime } from '@sphereon/ssi-sdk.agent-config'
|
|
15
|
-
import { PartyEntity } from './PartyEntity'
|
|
16
|
-
import { ContactMetadataItemEntity } from './ContactMetadataItemEntity'
|
|
17
|
-
|
|
18
|
-
@Entity('BaseContact')
|
|
19
|
-
@TableInheritance({ column: { type: 'varchar', name: 'type' } })
|
|
20
|
-
export abstract class BaseContactEntity extends BaseEntity {
|
|
21
|
-
@PrimaryGeneratedColumn('uuid')
|
|
22
|
-
id!: string
|
|
23
|
-
|
|
24
|
-
@CreateDateColumn({ name: 'created_at', nullable: false, type: typeOrmDateTime() })
|
|
25
|
-
createdAt!: Date
|
|
26
|
-
|
|
27
|
-
@UpdateDateColumn({ name: 'last_updated_at', nullable: false, type: typeOrmDateTime() })
|
|
28
|
-
lastUpdatedAt!: Date
|
|
29
|
-
|
|
30
|
-
@OneToOne(() => PartyEntity, (party: PartyEntity) => party.contact, {
|
|
31
|
-
onDelete: 'CASCADE',
|
|
32
|
-
})
|
|
33
|
-
@JoinColumn({ name: 'party_id' })
|
|
34
|
-
party!: PartyEntity
|
|
35
|
-
|
|
36
|
-
@OneToMany(() => ContactMetadataItemEntity, (metadata: ContactMetadataItemEntity) => metadata.contact, {
|
|
37
|
-
cascade: true,
|
|
38
|
-
onDelete: 'CASCADE',
|
|
39
|
-
eager: true,
|
|
40
|
-
nullable: false,
|
|
41
|
-
})
|
|
42
|
-
@JoinColumn({ name: 'metadata_id' })
|
|
43
|
-
metadata!: Array<ContactMetadataItemEntity>
|
|
44
|
-
|
|
45
|
-
// By default, @UpdateDateColumn in TypeORM updates the timestamp only when the entity's top-level properties change.
|
|
46
|
-
@BeforeInsert()
|
|
47
|
-
@BeforeUpdate()
|
|
48
|
-
updateUpdatedDate(): void {
|
|
49
|
-
this.lastUpdatedAt = new Date()
|
|
50
|
-
}
|
|
51
|
-
}
|
|
1
|
+
import {
|
|
2
|
+
BaseEntity,
|
|
3
|
+
BeforeInsert,
|
|
4
|
+
BeforeUpdate,
|
|
5
|
+
CreateDateColumn,
|
|
6
|
+
Entity,
|
|
7
|
+
JoinColumn,
|
|
8
|
+
OneToMany,
|
|
9
|
+
OneToOne,
|
|
10
|
+
PrimaryGeneratedColumn,
|
|
11
|
+
TableInheritance,
|
|
12
|
+
UpdateDateColumn,
|
|
13
|
+
} from 'typeorm'
|
|
14
|
+
import { typeOrmDateTime } from '@sphereon/ssi-sdk.agent-config'
|
|
15
|
+
import { PartyEntity } from './PartyEntity'
|
|
16
|
+
import { ContactMetadataItemEntity } from './ContactMetadataItemEntity'
|
|
17
|
+
|
|
18
|
+
@Entity('BaseContact')
|
|
19
|
+
@TableInheritance({ column: { type: 'varchar', name: 'type' } })
|
|
20
|
+
export abstract class BaseContactEntity extends BaseEntity {
|
|
21
|
+
@PrimaryGeneratedColumn('uuid')
|
|
22
|
+
id!: string
|
|
23
|
+
|
|
24
|
+
@CreateDateColumn({ name: 'created_at', nullable: false, type: typeOrmDateTime() })
|
|
25
|
+
createdAt!: Date
|
|
26
|
+
|
|
27
|
+
@UpdateDateColumn({ name: 'last_updated_at', nullable: false, type: typeOrmDateTime() })
|
|
28
|
+
lastUpdatedAt!: Date
|
|
29
|
+
|
|
30
|
+
@OneToOne(() => PartyEntity, (party: PartyEntity) => party.contact, {
|
|
31
|
+
onDelete: 'CASCADE',
|
|
32
|
+
})
|
|
33
|
+
@JoinColumn({ name: 'party_id' })
|
|
34
|
+
party!: PartyEntity
|
|
35
|
+
|
|
36
|
+
@OneToMany(() => ContactMetadataItemEntity, (metadata: ContactMetadataItemEntity) => metadata.contact, {
|
|
37
|
+
cascade: true,
|
|
38
|
+
onDelete: 'CASCADE',
|
|
39
|
+
eager: true,
|
|
40
|
+
nullable: false,
|
|
41
|
+
})
|
|
42
|
+
@JoinColumn({ name: 'metadata_id' })
|
|
43
|
+
metadata!: Array<ContactMetadataItemEntity>
|
|
44
|
+
|
|
45
|
+
// By default, @UpdateDateColumn in TypeORM updates the timestamp only when the entity's top-level properties change.
|
|
46
|
+
@BeforeInsert()
|
|
47
|
+
@BeforeUpdate()
|
|
48
|
+
updateUpdatedDate(): void {
|
|
49
|
+
this.lastUpdatedAt = new Date()
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -1,35 +1,35 @@
|
|
|
1
|
-
import { Entity, Column, PrimaryGeneratedColumn, OneToOne, JoinColumn, BaseEntity } from 'typeorm'
|
|
2
|
-
import { BaseConfigEntity } from './BaseConfigEntity'
|
|
3
|
-
import { ConnectionType } from '../../types'
|
|
4
|
-
import { IdentityEntity } from './IdentityEntity'
|
|
5
|
-
import { OpenIdConfigEntity } from './OpenIdConfigEntity'
|
|
6
|
-
import { DidAuthConfigEntity } from './DidAuthConfigEntity'
|
|
7
|
-
|
|
8
|
-
@Entity('Connection')
|
|
9
|
-
export class ConnectionEntity extends BaseEntity {
|
|
10
|
-
@PrimaryGeneratedColumn('uuid')
|
|
11
|
-
id!: string
|
|
12
|
-
|
|
13
|
-
@Column('simple-enum', { name: 'type', enum: ConnectionType, nullable: false })
|
|
14
|
-
type!: ConnectionType
|
|
15
|
-
|
|
16
|
-
@Column('text', { name: 'tenant_id', nullable: true })
|
|
17
|
-
tenantId?: string
|
|
18
|
-
|
|
19
|
-
@Column('text', { name: 'owner_id', nullable: true })
|
|
20
|
-
ownerId?: string
|
|
21
|
-
|
|
22
|
-
@OneToOne(() => BaseConfigEntity, (config: OpenIdConfigEntity | DidAuthConfigEntity) => config.connection, {
|
|
23
|
-
cascade: true,
|
|
24
|
-
onDelete: 'CASCADE',
|
|
25
|
-
eager: true,
|
|
26
|
-
nullable: false,
|
|
27
|
-
})
|
|
28
|
-
config!: BaseConfigEntity
|
|
29
|
-
|
|
30
|
-
@OneToOne(() => IdentityEntity, (identity: IdentityEntity) => identity.connection, {
|
|
31
|
-
onDelete: 'CASCADE',
|
|
32
|
-
})
|
|
33
|
-
@JoinColumn({ name: 'identity_id' })
|
|
34
|
-
identity!: IdentityEntity
|
|
35
|
-
}
|
|
1
|
+
import { Entity, Column, PrimaryGeneratedColumn, OneToOne, JoinColumn, BaseEntity } from 'typeorm'
|
|
2
|
+
import { BaseConfigEntity } from './BaseConfigEntity'
|
|
3
|
+
import { ConnectionType } from '../../types'
|
|
4
|
+
import { IdentityEntity } from './IdentityEntity'
|
|
5
|
+
import { OpenIdConfigEntity } from './OpenIdConfigEntity'
|
|
6
|
+
import { DidAuthConfigEntity } from './DidAuthConfigEntity'
|
|
7
|
+
|
|
8
|
+
@Entity('Connection')
|
|
9
|
+
export class ConnectionEntity extends BaseEntity {
|
|
10
|
+
@PrimaryGeneratedColumn('uuid')
|
|
11
|
+
id!: string
|
|
12
|
+
|
|
13
|
+
@Column('simple-enum', { name: 'type', enum: ConnectionType, nullable: false })
|
|
14
|
+
type!: ConnectionType
|
|
15
|
+
|
|
16
|
+
@Column('text', { name: 'tenant_id', nullable: true })
|
|
17
|
+
tenantId?: string
|
|
18
|
+
|
|
19
|
+
@Column('text', { name: 'owner_id', nullable: true })
|
|
20
|
+
ownerId?: string
|
|
21
|
+
|
|
22
|
+
@OneToOne(() => BaseConfigEntity, (config: OpenIdConfigEntity | DidAuthConfigEntity) => config.connection, {
|
|
23
|
+
cascade: true,
|
|
24
|
+
onDelete: 'CASCADE',
|
|
25
|
+
eager: true,
|
|
26
|
+
nullable: false,
|
|
27
|
+
})
|
|
28
|
+
config!: BaseConfigEntity
|
|
29
|
+
|
|
30
|
+
@OneToOne(() => IdentityEntity, (identity: IdentityEntity) => identity.connection, {
|
|
31
|
+
onDelete: 'CASCADE',
|
|
32
|
+
})
|
|
33
|
+
@JoinColumn({ name: 'identity_id' })
|
|
34
|
+
identity!: IdentityEntity
|
|
35
|
+
}
|