@sphereon/ssi-sdk.data-store 0.11.1-next.65
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 -0
- package/README.md +67 -0
- package/dist/contact/AbstractContactStore.d.ts +15 -0
- package/dist/contact/AbstractContactStore.d.ts.map +1 -0
- package/dist/contact/AbstractContactStore.js +7 -0
- package/dist/contact/AbstractContactStore.js.map +1 -0
- package/dist/contact/ContactStore.d.ts +30 -0
- package/dist/contact/ContactStore.d.ts.map +1 -0
- package/dist/contact/ContactStore.js +281 -0
- package/dist/contact/ContactStore.js.map +1 -0
- package/dist/entities/contact/BaseConfigEntity.d.ts +5 -0
- package/dist/entities/contact/BaseConfigEntity.d.ts.map +1 -0
- package/dist/entities/contact/BaseConfigEntity.js +26 -0
- package/dist/entities/contact/BaseConfigEntity.js.map +1 -0
- package/dist/entities/contact/ConnectionEntity.d.ts +12 -0
- package/dist/entities/contact/ConnectionEntity.d.ts.map +1 -0
- package/dist/entities/contact/ConnectionEntity.js +66 -0
- package/dist/entities/contact/ConnectionEntity.js.map +1 -0
- package/dist/entities/contact/ContactEntity.d.ts +16 -0
- package/dist/entities/contact/ContactEntity.d.ts.map +1 -0
- package/dist/entities/contact/ContactEntity.js +108 -0
- package/dist/entities/contact/ContactEntity.js.map +1 -0
- package/dist/entities/contact/CorrelationIdentifierEntity.d.ts +12 -0
- package/dist/entities/contact/CorrelationIdentifierEntity.d.ts.map +1 -0
- package/dist/entities/contact/CorrelationIdentifierEntity.js +76 -0
- package/dist/entities/contact/CorrelationIdentifierEntity.js.map +1 -0
- package/dist/entities/contact/DidAuthConfigEntity.d.ts +11 -0
- package/dist/entities/contact/DidAuthConfigEntity.d.ts.map +1 -0
- package/dist/entities/contact/DidAuthConfigEntity.js +49 -0
- package/dist/entities/contact/DidAuthConfigEntity.js.map +1 -0
- package/dist/entities/contact/IdentityEntity.d.ts +22 -0
- package/dist/entities/contact/IdentityEntity.d.ts.map +1 -0
- package/dist/entities/contact/IdentityEntity.js +137 -0
- package/dist/entities/contact/IdentityEntity.js.map +1 -0
- package/dist/entities/contact/IdentityMetadataItemEntity.d.ts +12 -0
- package/dist/entities/contact/IdentityMetadataItemEntity.d.ts.map +1 -0
- package/dist/entities/contact/IdentityMetadataItemEntity.js +73 -0
- package/dist/entities/contact/IdentityMetadataItemEntity.js.map +1 -0
- package/dist/entities/contact/OpenIdConfigEntity.d.ts +15 -0
- package/dist/entities/contact/OpenIdConfigEntity.d.ts.map +1 -0
- package/dist/entities/contact/OpenIdConfigEntity.js +70 -0
- package/dist/entities/contact/OpenIdConfigEntity.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +59 -0
- package/dist/index.js.map +1 -0
- package/dist/migrations/generic/1-CreateContacts.d.ts +7 -0
- package/dist/migrations/generic/1-CreateContacts.d.ts.map +1 -0
- package/dist/migrations/generic/1-CreateContacts.js +72 -0
- package/dist/migrations/generic/1-CreateContacts.js.map +1 -0
- package/dist/migrations/generic/index.d.ts +10 -0
- package/dist/migrations/generic/index.d.ts.map +1 -0
- package/dist/migrations/generic/index.js +13 -0
- package/dist/migrations/generic/index.js.map +1 -0
- package/dist/migrations/index.d.ts +2 -0
- package/dist/migrations/index.d.ts.map +1 -0
- package/dist/migrations/index.js +6 -0
- package/dist/migrations/index.js.map +1 -0
- package/dist/migrations/internal-migrations-ormconfig.d.ts +6 -0
- package/dist/migrations/internal-migrations-ormconfig.d.ts.map +1 -0
- package/dist/migrations/internal-migrations-ormconfig.js +29 -0
- package/dist/migrations/internal-migrations-ormconfig.js.map +1 -0
- package/dist/migrations/postgres/1659463079428-CreateContacts.d.ts +7 -0
- package/dist/migrations/postgres/1659463079428-CreateContacts.d.ts.map +1 -0
- package/dist/migrations/postgres/1659463079428-CreateContacts.js +55 -0
- package/dist/migrations/postgres/1659463079428-CreateContacts.js.map +1 -0
- package/dist/migrations/sqlite/1659463069549-CreateContacts.d.ts +7 -0
- package/dist/migrations/sqlite/1659463069549-CreateContacts.d.ts.map +1 -0
- package/dist/migrations/sqlite/1659463069549-CreateContacts.js +79 -0
- package/dist/migrations/sqlite/1659463069549-CreateContacts.js.map +1 -0
- package/dist/types/IAbstractContactStore.d.ts +41 -0
- package/dist/types/IAbstractContactStore.d.ts.map +1 -0
- package/dist/types/IAbstractContactStore.js +3 -0
- package/dist/types/IAbstractContactStore.js.map +1 -0
- package/dist/types/contact.d.ts +91 -0
- package/dist/types/contact.d.ts.map +1 -0
- package/dist/types/contact.js +21 -0
- package/dist/types/contact.js.map +1 -0
- package/package.json +45 -0
- package/src/__tests__/contact.entities.test.ts +911 -0
- package/src/__tests__/contact.store.test.ts +743 -0
- package/src/contact/AbstractContactStore.ts +26 -0
- package/src/contact/ContactStore.ts +373 -0
- package/src/entities/contact/BaseConfigEntity.ts +8 -0
- package/src/entities/contact/ConnectionEntity.ts +48 -0
- package/src/entities/contact/ContactEntity.ts +76 -0
- package/src/entities/contact/CorrelationIdentifierEntity.ts +41 -0
- package/src/entities/contact/DidAuthConfigEntity.ts +31 -0
- package/src/entities/contact/IdentityEntity.ts +106 -0
- package/src/entities/contact/IdentityMetadataItemEntity.ts +39 -0
- package/src/entities/contact/OpenIdConfigEntity.ts +48 -0
- package/src/index.ts +45 -0
- package/src/migrations/generic/1-CreateContacts.ts +54 -0
- package/src/migrations/generic/index.ts +10 -0
- package/src/migrations/index.ts +1 -0
- package/src/migrations/internal-migrations-ormconfig.ts +28 -0
- package/src/migrations/postgres/1659463079428-CreateContacts.ts +61 -0
- package/src/migrations/sqlite/1659463069549-CreateContacts.ts +111 -0
- package/src/types/IAbstractContactStore.ts +51 -0
- package/src/types/contact.ts +101 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { IContact, IIdentity } from '../types/contact'
|
|
2
|
+
import {
|
|
3
|
+
IAddIdentityArgs,
|
|
4
|
+
IGetIdentityArgs,
|
|
5
|
+
IGetIdentitiesArgs,
|
|
6
|
+
IGetContactArgs,
|
|
7
|
+
IRemoveIdentityArgs,
|
|
8
|
+
IUpdateIdentityArgs,
|
|
9
|
+
IAddContactArgs,
|
|
10
|
+
IGetContactsArgs,
|
|
11
|
+
IRemoveContactArgs,
|
|
12
|
+
IUpdateContactArgs,
|
|
13
|
+
} from '../types/IAbstractContactStore'
|
|
14
|
+
|
|
15
|
+
export abstract class AbstractContactStore {
|
|
16
|
+
abstract getContact(args: IGetContactArgs): Promise<IContact>
|
|
17
|
+
abstract getContacts(args?: IGetContactsArgs): Promise<Array<IContact>>
|
|
18
|
+
abstract addContact(args: IAddContactArgs): Promise<IContact>
|
|
19
|
+
abstract updateContact(args: IUpdateContactArgs): Promise<IContact>
|
|
20
|
+
abstract removeContact(args: IRemoveContactArgs): Promise<void>
|
|
21
|
+
abstract getIdentity(args: IGetIdentityArgs): Promise<IIdentity>
|
|
22
|
+
abstract getIdentities(args: IGetIdentitiesArgs): Promise<Array<IIdentity>>
|
|
23
|
+
abstract addIdentity(args: IAddIdentityArgs): Promise<IIdentity>
|
|
24
|
+
abstract updateIdentity(args: IUpdateIdentityArgs): Promise<IIdentity>
|
|
25
|
+
abstract removeIdentity(args: IRemoveIdentityArgs): Promise<void>
|
|
26
|
+
}
|
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
import Debug from 'debug'
|
|
2
|
+
import { OrPromise } from '@sphereon/ssi-types'
|
|
3
|
+
import {
|
|
4
|
+
IAddIdentityArgs,
|
|
5
|
+
IGetIdentityArgs,
|
|
6
|
+
IGetIdentitiesArgs,
|
|
7
|
+
IGetContactArgs,
|
|
8
|
+
IRemoveIdentityArgs,
|
|
9
|
+
IUpdateIdentityArgs,
|
|
10
|
+
IGetContactsArgs,
|
|
11
|
+
IAddContactArgs,
|
|
12
|
+
IUpdateContactArgs,
|
|
13
|
+
IRemoveContactArgs,
|
|
14
|
+
} from '../types/IAbstractContactStore'
|
|
15
|
+
import { AbstractContactStore } from './AbstractContactStore'
|
|
16
|
+
import {
|
|
17
|
+
BasicConnectionConfig,
|
|
18
|
+
ConnectionConfig,
|
|
19
|
+
ConnectionTypeEnum,
|
|
20
|
+
CorrelationIdentifierEnum,
|
|
21
|
+
IConnection,
|
|
22
|
+
IContact,
|
|
23
|
+
ICorrelationIdentifier,
|
|
24
|
+
IDidAuthConfig,
|
|
25
|
+
IIdentity,
|
|
26
|
+
IMetadataItem,
|
|
27
|
+
IOpenIdConfig,
|
|
28
|
+
} from '../types/contact'
|
|
29
|
+
import { ContactEntity, contactEntityFrom } from '../entities/contact/ContactEntity'
|
|
30
|
+
import { IdentityEntity, identityEntityFrom } from '../entities/contact/IdentityEntity'
|
|
31
|
+
import { IdentityMetadataItemEntity } from '../entities/contact/IdentityMetadataItemEntity'
|
|
32
|
+
import { CorrelationIdentifierEntity } from '../entities/contact/CorrelationIdentifierEntity'
|
|
33
|
+
import { ConnectionEntity } from '../entities/contact/ConnectionEntity'
|
|
34
|
+
import { BaseConfigEntity } from '../entities/contact/BaseConfigEntity'
|
|
35
|
+
import { OpenIdConfigEntity } from '../entities/contact/OpenIdConfigEntity'
|
|
36
|
+
import { DidAuthConfigEntity } from '../entities/contact/DidAuthConfigEntity'
|
|
37
|
+
import { DataSource, In } from 'typeorm'
|
|
38
|
+
|
|
39
|
+
const debug = Debug('sphereon:ssi-sdk:contact-store')
|
|
40
|
+
|
|
41
|
+
export class ContactStore extends AbstractContactStore {
|
|
42
|
+
private readonly dbConnection: OrPromise<DataSource>
|
|
43
|
+
|
|
44
|
+
constructor(dbConnection: OrPromise<DataSource>) {
|
|
45
|
+
super()
|
|
46
|
+
this.dbConnection = dbConnection
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
getContact = async ({ contactId }: IGetContactArgs): Promise<IContact> => {
|
|
50
|
+
const result = await (await this.dbConnection).getRepository(ContactEntity).findOne({
|
|
51
|
+
where: { id: contactId },
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
if (!result) {
|
|
55
|
+
return Promise.reject(Error(`No contact found for id: ${contactId}`))
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return this.contactFrom(result)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
getContacts = async (args?: IGetContactsArgs): Promise<Array<IContact>> => {
|
|
62
|
+
const initialResult = await (await this.dbConnection).getRepository(ContactEntity).find({
|
|
63
|
+
...(args?.filter && { where: args?.filter }),
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
const result = await (await this.dbConnection).getRepository(ContactEntity).find({
|
|
67
|
+
where: {
|
|
68
|
+
id: In(initialResult.map((contact: ContactEntity) => contact.id)),
|
|
69
|
+
},
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
return result.map((contact: ContactEntity) => this.contactFrom(contact))
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
addContact = async (args: IAddContactArgs): Promise<IContact> => {
|
|
76
|
+
const { name, alias, uri, identities } = args
|
|
77
|
+
|
|
78
|
+
const result = await (await this.dbConnection).getRepository(ContactEntity).findOne({
|
|
79
|
+
where: [{ name }, { alias }],
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
if (result) {
|
|
83
|
+
return Promise.reject(Error(`Duplicate names or aliases are not allowed. Name: ${name}, Alias: ${alias}`))
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
for (const identity of identities ?? []) {
|
|
87
|
+
if (identity.identifier.type === CorrelationIdentifierEnum.URL) {
|
|
88
|
+
if (!identity.connection) {
|
|
89
|
+
return Promise.reject(Error(`Identity with correlation type ${CorrelationIdentifierEnum.URL} should contain a connection`))
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (!this.hasCorrectConfig(identity.connection.type, identity.connection.config)) {
|
|
93
|
+
return Promise.reject(Error(`Connection type ${identity.connection.type}, does not match for provided config`))
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const contactEntity = contactEntityFrom({ name, alias, uri, identities })
|
|
99
|
+
debug('Adding contact', name)
|
|
100
|
+
const createdResult = await (await this.dbConnection).getRepository(ContactEntity).save(contactEntity)
|
|
101
|
+
|
|
102
|
+
return this.contactFrom(createdResult)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
updateContact = async ({ contact }: IUpdateContactArgs): Promise<IContact> => {
|
|
106
|
+
const result = await (await this.dbConnection).getRepository(ContactEntity).findOne({
|
|
107
|
+
where: { id: contact.id },
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
if (!result) {
|
|
111
|
+
return Promise.reject(Error(`No contact found for id: ${contact.id}`))
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const updatedContact = {
|
|
115
|
+
...contact,
|
|
116
|
+
identities: result.identities,
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
debug('Updating contact', contact)
|
|
120
|
+
const updatedResult = await (await this.dbConnection).getRepository(ContactEntity).save(updatedContact, { transaction: true })
|
|
121
|
+
|
|
122
|
+
return this.contactFrom(updatedResult)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
removeContact = async ({ contactId }: IRemoveContactArgs): Promise<void> => {
|
|
126
|
+
debug('Removing contact', contactId)
|
|
127
|
+
;(await this.dbConnection)
|
|
128
|
+
.getRepository(ContactEntity)
|
|
129
|
+
.findOneById(contactId)
|
|
130
|
+
.then(async (contact: ContactEntity | null) => {
|
|
131
|
+
if (!contact) {
|
|
132
|
+
await Promise.reject(Error(`Unable to find the contact with id to remove: ${contactId}`))
|
|
133
|
+
} else {
|
|
134
|
+
await this.deleteIdentities(contact.identities)
|
|
135
|
+
|
|
136
|
+
await (
|
|
137
|
+
await this.dbConnection
|
|
138
|
+
)
|
|
139
|
+
.getRepository(ContactEntity)
|
|
140
|
+
.delete({ id: contactId })
|
|
141
|
+
.catch((error) => Promise.reject(Error(`Unable to remove contact with id: ${contactId}. ${error}`)))
|
|
142
|
+
}
|
|
143
|
+
})
|
|
144
|
+
.catch((error) => Promise.reject(Error(`Unable to remove contact with id: ${contactId}. ${error}`)))
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
private async deleteIdentities(identities: Array<IdentityEntity>): Promise<void> {
|
|
148
|
+
debug('Removing identities', identities)
|
|
149
|
+
|
|
150
|
+
identities.map(async (identity: IdentityEntity) => {
|
|
151
|
+
await (
|
|
152
|
+
await this.dbConnection
|
|
153
|
+
)
|
|
154
|
+
.getRepository(CorrelationIdentifierEntity)
|
|
155
|
+
.delete(identity.identifier.id)
|
|
156
|
+
.catch((error) => Promise.reject(Error(`Unable to remove identity.identifier with id: ${identity.identifier.id}. ${error}`)))
|
|
157
|
+
|
|
158
|
+
if (identity.connection) {
|
|
159
|
+
await (await this.dbConnection).getRepository(BaseConfigEntity).delete(identity.connection.config.id)
|
|
160
|
+
|
|
161
|
+
await (
|
|
162
|
+
await this.dbConnection
|
|
163
|
+
)
|
|
164
|
+
.getRepository(ConnectionEntity)
|
|
165
|
+
.delete(identity.connection.id)
|
|
166
|
+
.catch((error) => Promise.reject(Error(`Unable to remove identity.connection with id. ${error}`)))
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (identity.metadata) {
|
|
170
|
+
identity.metadata.map(async (metadataItem: IdentityMetadataItemEntity) => {
|
|
171
|
+
await (
|
|
172
|
+
await this.dbConnection
|
|
173
|
+
)
|
|
174
|
+
.getRepository(IdentityMetadataItemEntity)
|
|
175
|
+
.delete(metadataItem.id)
|
|
176
|
+
.catch((error) => Promise.reject(Error(`Unable to remove metadataItem.id with id ${metadataItem.id}. ${error}`)))
|
|
177
|
+
})
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
;(await this.dbConnection)
|
|
181
|
+
.getRepository(IdentityEntity)
|
|
182
|
+
.delete(identity.id)
|
|
183
|
+
.catch((error) => Promise.reject(Error(`Unable to remove metadataItem.id with id ${identity.id}. ${error}`)))
|
|
184
|
+
})
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
getIdentity = async ({ identityId }: IGetIdentityArgs): Promise<IIdentity> => {
|
|
188
|
+
const result = await (await this.dbConnection).getRepository(IdentityEntity).findOne({
|
|
189
|
+
where: { id: identityId },
|
|
190
|
+
})
|
|
191
|
+
|
|
192
|
+
if (!result) {
|
|
193
|
+
return Promise.reject(Error(`No identity found for id: ${identityId}`))
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
return this.identityFrom(result)
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
getIdentities = async (args?: IGetIdentitiesArgs): Promise<Array<IIdentity>> => {
|
|
200
|
+
const initialResult = await (await this.dbConnection).getRepository(IdentityEntity).find({
|
|
201
|
+
...(args?.filter && { where: args?.filter }),
|
|
202
|
+
})
|
|
203
|
+
|
|
204
|
+
const result = await (await this.dbConnection).getRepository(IdentityEntity).find({
|
|
205
|
+
where: {
|
|
206
|
+
id: In(initialResult.map((identity: IdentityEntity) => identity.id)),
|
|
207
|
+
},
|
|
208
|
+
})
|
|
209
|
+
|
|
210
|
+
return result.map((identity: IdentityEntity) => this.identityFrom(identity))
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
addIdentity = async ({ identity, contactId }: IAddIdentityArgs): Promise<IIdentity> => {
|
|
214
|
+
const contact = await (await this.dbConnection).getRepository(ContactEntity).findOne({
|
|
215
|
+
where: { id: contactId },
|
|
216
|
+
})
|
|
217
|
+
|
|
218
|
+
if (!contact) {
|
|
219
|
+
return Promise.reject(Error(`No contact found for id: ${contactId}`))
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
if (identity.identifier.type === CorrelationIdentifierEnum.URL) {
|
|
223
|
+
if (!identity.connection) {
|
|
224
|
+
return Promise.reject(Error(`Identity with correlation type ${CorrelationIdentifierEnum.URL} should contain a connection`))
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
if (!this.hasCorrectConfig(identity.connection.type, identity.connection.config)) {
|
|
228
|
+
return Promise.reject(Error(`Connection type ${identity.connection.type}, does not match for provided config`))
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const identityEntity = identityEntityFrom(identity)
|
|
233
|
+
identityEntity.contact = contact
|
|
234
|
+
debug('Adding identity', identity)
|
|
235
|
+
const result = await (await this.dbConnection).getRepository(IdentityEntity).save(identityEntity, {
|
|
236
|
+
transaction: true,
|
|
237
|
+
})
|
|
238
|
+
|
|
239
|
+
return this.identityFrom(result)
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
updateIdentity = async ({ identity }: IUpdateIdentityArgs): Promise<IIdentity> => {
|
|
243
|
+
const result = await (await this.dbConnection).getRepository(IdentityEntity).findOne({
|
|
244
|
+
where: { id: identity.id },
|
|
245
|
+
})
|
|
246
|
+
|
|
247
|
+
if (!result) {
|
|
248
|
+
return Promise.reject(Error(`No identity found for id: ${identity.id}`))
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
if (identity.identifier.type === CorrelationIdentifierEnum.URL) {
|
|
252
|
+
if (!identity.connection) {
|
|
253
|
+
return Promise.reject(Error(`Identity with correlation type ${CorrelationIdentifierEnum.URL} should contain a connection`))
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
if (!this.hasCorrectConfig(identity.connection.type, identity.connection.config)) {
|
|
257
|
+
return Promise.reject(Error(`Connection type ${identity.connection.type}, does not match for provided config`))
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
debug('Updating identity', identity)
|
|
262
|
+
const updatedResult = await (await this.dbConnection).getRepository(IdentityEntity).save(identity, { transaction: true })
|
|
263
|
+
|
|
264
|
+
return this.identityFrom(updatedResult)
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
removeIdentity = async ({ identityId }: IRemoveIdentityArgs): Promise<void> => {
|
|
268
|
+
const identity = await (await this.dbConnection).getRepository(IdentityEntity).findOne({
|
|
269
|
+
where: { id: identityId },
|
|
270
|
+
})
|
|
271
|
+
|
|
272
|
+
if (!identity) {
|
|
273
|
+
return Promise.reject(Error(`No identity found for id: ${identityId}`))
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
debug('Removing identity', identityId)
|
|
277
|
+
|
|
278
|
+
await this.deleteIdentities([identity])
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
private contactFrom = (contact: ContactEntity): IContact => {
|
|
282
|
+
return {
|
|
283
|
+
id: contact.id,
|
|
284
|
+
name: contact.name,
|
|
285
|
+
alias: contact.alias,
|
|
286
|
+
uri: contact.uri,
|
|
287
|
+
roles: [...new Set(contact.identities?.flatMap((identity) => identity.roles))] ?? [],
|
|
288
|
+
identities: contact.identities ? contact.identities.map((identity: IdentityEntity) => this.identityFrom(identity)) : [],
|
|
289
|
+
createdAt: contact.createdAt,
|
|
290
|
+
lastUpdatedAt: contact.lastUpdatedAt,
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
private identityFrom = (identity: IdentityEntity): IIdentity => {
|
|
295
|
+
return {
|
|
296
|
+
id: identity.id,
|
|
297
|
+
alias: identity.alias,
|
|
298
|
+
roles: identity.roles,
|
|
299
|
+
identifier: this.correlationIdentifierFrom(identity.identifier),
|
|
300
|
+
...(identity.connection && { connection: this.connectionFrom(identity.connection) }),
|
|
301
|
+
metadata: identity.metadata ? identity.metadata.map((item: IdentityMetadataItemEntity) => this.metadataItemFrom(item)) : [],
|
|
302
|
+
createdAt: identity.createdAt,
|
|
303
|
+
lastUpdatedAt: identity.createdAt,
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
private correlationIdentifierFrom = (identifier: CorrelationIdentifierEntity): ICorrelationIdentifier => {
|
|
308
|
+
return {
|
|
309
|
+
id: identifier.id,
|
|
310
|
+
type: identifier.type,
|
|
311
|
+
correlationId: identifier.correlationId,
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
private metadataItemFrom = (item: IdentityMetadataItemEntity): IMetadataItem => {
|
|
316
|
+
return {
|
|
317
|
+
id: item.id,
|
|
318
|
+
label: item.label,
|
|
319
|
+
value: item.value,
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
private connectionFrom = (connection: ConnectionEntity): IConnection => {
|
|
324
|
+
return {
|
|
325
|
+
id: connection.id,
|
|
326
|
+
type: connection.type,
|
|
327
|
+
config: this.configFrom(connection.type, connection.config),
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
private configFrom = (type: ConnectionTypeEnum, config: BaseConfigEntity): ConnectionConfig => {
|
|
332
|
+
switch (type) {
|
|
333
|
+
case ConnectionTypeEnum.OPENID_CONNECT:
|
|
334
|
+
return {
|
|
335
|
+
id: (config as OpenIdConfigEntity).id,
|
|
336
|
+
clientId: (config as OpenIdConfigEntity).clientId,
|
|
337
|
+
clientSecret: (config as OpenIdConfigEntity).clientSecret,
|
|
338
|
+
scopes: (config as OpenIdConfigEntity).scopes,
|
|
339
|
+
issuer: (config as OpenIdConfigEntity).issuer!, // TODO fixme
|
|
340
|
+
redirectUrl: (config as OpenIdConfigEntity).redirectUrl,
|
|
341
|
+
dangerouslyAllowInsecureHttpRequests: (config as OpenIdConfigEntity).dangerouslyAllowInsecureHttpRequests,
|
|
342
|
+
clientAuthMethod: (config as OpenIdConfigEntity).clientAuthMethod,
|
|
343
|
+
}
|
|
344
|
+
case ConnectionTypeEnum.SIOPv2:
|
|
345
|
+
return {
|
|
346
|
+
id: (config as DidAuthConfigEntity).id,
|
|
347
|
+
identifier: { did: (config as DidAuthConfigEntity).identifier, provider: '', keys: [], services: [] },
|
|
348
|
+
stateId: '',
|
|
349
|
+
redirectUrl: (config as DidAuthConfigEntity).redirectUrl,
|
|
350
|
+
sessionId: (config as DidAuthConfigEntity).sessionId,
|
|
351
|
+
}
|
|
352
|
+
default:
|
|
353
|
+
throw new Error('Connection type not supported')
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
private hasCorrectConfig(type: ConnectionTypeEnum, config: BasicConnectionConfig): boolean {
|
|
358
|
+
switch (type) {
|
|
359
|
+
case ConnectionTypeEnum.OPENID_CONNECT:
|
|
360
|
+
return this.isOpenIdConfig(config)
|
|
361
|
+
case ConnectionTypeEnum.SIOPv2:
|
|
362
|
+
return this.isDidAuthConfig(config)
|
|
363
|
+
default:
|
|
364
|
+
throw new Error('Connection type not supported')
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
private isOpenIdConfig = (config: BasicConnectionConfig): config is IOpenIdConfig =>
|
|
369
|
+
'clientSecret' in config && 'issuer' in config && 'redirectUrl' in config
|
|
370
|
+
|
|
371
|
+
private isDidAuthConfig = (config: BasicConnectionConfig): config is IDidAuthConfig =>
|
|
372
|
+
'identifier' in config && 'redirectUrl' in config && 'sessionId' in config
|
|
373
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { BaseEntity, Entity, PrimaryGeneratedColumn, TableInheritance } from 'typeorm'
|
|
2
|
+
|
|
3
|
+
@Entity('BaseConfigEntity') // FIXME rename it to 'BaseConfig'
|
|
4
|
+
@TableInheritance({ column: { type: 'varchar', name: 'type' } })
|
|
5
|
+
export abstract class BaseConfigEntity extends BaseEntity {
|
|
6
|
+
@PrimaryGeneratedColumn('uuid')
|
|
7
|
+
id!: string
|
|
8
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { Entity, Column, PrimaryGeneratedColumn, OneToOne, JoinColumn, BaseEntity } from 'typeorm'
|
|
2
|
+
import { BaseConfigEntity } from './BaseConfigEntity'
|
|
3
|
+
import { BasicConnectionConfig, ConnectionTypeEnum, IBasicConnection, IDidAuthConfig, IOpenIdConfig } from '../../types/contact'
|
|
4
|
+
import { IdentityEntity } from './IdentityEntity'
|
|
5
|
+
import { OpenIdConfigEntity, openIdConfigEntityFrom } from './OpenIdConfigEntity'
|
|
6
|
+
import { DidAuthConfigEntity, didAuthConfigEntityFrom } 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: ConnectionTypeEnum, nullable: false })
|
|
14
|
+
type!: ConnectionTypeEnum
|
|
15
|
+
|
|
16
|
+
@OneToOne(() => BaseConfigEntity, (config: OpenIdConfigEntity | DidAuthConfigEntity) => config.connection, {
|
|
17
|
+
cascade: true,
|
|
18
|
+
onDelete: 'CASCADE',
|
|
19
|
+
eager: true,
|
|
20
|
+
nullable: false,
|
|
21
|
+
})
|
|
22
|
+
config!: BaseConfigEntity
|
|
23
|
+
|
|
24
|
+
@OneToOne(() => IdentityEntity, (identity: IdentityEntity) => identity.connection, {
|
|
25
|
+
onDelete: 'CASCADE',
|
|
26
|
+
})
|
|
27
|
+
@JoinColumn({ name: 'identityId' })
|
|
28
|
+
identity!: IdentityEntity
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export const connectionEntityFrom = (connection: IBasicConnection): ConnectionEntity => {
|
|
32
|
+
const connectionEntity = new ConnectionEntity()
|
|
33
|
+
connectionEntity.type = connection.type
|
|
34
|
+
connectionEntity.config = configEntityFrom(connection.type, connection.config)
|
|
35
|
+
|
|
36
|
+
return connectionEntity
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const configEntityFrom = (type: ConnectionTypeEnum, config: BasicConnectionConfig): BaseConfigEntity => {
|
|
40
|
+
switch (type) {
|
|
41
|
+
case ConnectionTypeEnum.OPENID_CONNECT:
|
|
42
|
+
return openIdConfigEntityFrom(config as IOpenIdConfig)
|
|
43
|
+
case ConnectionTypeEnum.SIOPv2:
|
|
44
|
+
return didAuthConfigEntityFrom(config as IDidAuthConfig)
|
|
45
|
+
default:
|
|
46
|
+
throw new Error('Connection type not supported')
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import {
|
|
2
|
+
BaseEntity,
|
|
3
|
+
BeforeInsert,
|
|
4
|
+
BeforeUpdate,
|
|
5
|
+
Column,
|
|
6
|
+
CreateDateColumn,
|
|
7
|
+
Entity,
|
|
8
|
+
JoinColumn,
|
|
9
|
+
OneToMany,
|
|
10
|
+
PrimaryGeneratedColumn,
|
|
11
|
+
UpdateDateColumn,
|
|
12
|
+
} from 'typeorm'
|
|
13
|
+
import { IBasicContact, IBasicIdentity } from '../../types/contact'
|
|
14
|
+
import { IdentityEntity, identityEntityFrom } from './IdentityEntity'
|
|
15
|
+
import { IsNotEmpty, validate } from 'class-validator'
|
|
16
|
+
|
|
17
|
+
@Entity('Contact')
|
|
18
|
+
export class ContactEntity extends BaseEntity {
|
|
19
|
+
@PrimaryGeneratedColumn('uuid')
|
|
20
|
+
id!: string
|
|
21
|
+
|
|
22
|
+
@Column({ name: 'name', length: 255, nullable: false, unique: true })
|
|
23
|
+
@IsNotEmpty({ message: 'Blank names are not allowed' })
|
|
24
|
+
name!: string
|
|
25
|
+
|
|
26
|
+
@Column({ name: 'alias', length: 255, nullable: false, unique: true })
|
|
27
|
+
@IsNotEmpty({ message: 'Blank aliases are not allowed' })
|
|
28
|
+
alias!: string
|
|
29
|
+
|
|
30
|
+
@Column({ name: 'uri', length: 255 })
|
|
31
|
+
uri?: string
|
|
32
|
+
|
|
33
|
+
@OneToMany(() => IdentityEntity, (identity: IdentityEntity) => identity.contact, {
|
|
34
|
+
cascade: true,
|
|
35
|
+
onDelete: 'CASCADE',
|
|
36
|
+
eager: true,
|
|
37
|
+
nullable: false,
|
|
38
|
+
})
|
|
39
|
+
@JoinColumn({ name: 'identityId' })
|
|
40
|
+
identities!: Array<IdentityEntity>
|
|
41
|
+
|
|
42
|
+
@CreateDateColumn({ name: 'created_at', nullable: false })
|
|
43
|
+
createdAt!: Date
|
|
44
|
+
|
|
45
|
+
@UpdateDateColumn({ name: 'last_updated_at', nullable: false })
|
|
46
|
+
lastUpdatedAt!: Date
|
|
47
|
+
|
|
48
|
+
// By default, @UpdateDateColumn in TypeORM updates the timestamp only when the entity's top-level properties change.
|
|
49
|
+
@BeforeInsert()
|
|
50
|
+
@BeforeUpdate()
|
|
51
|
+
updateUpdatedDate() {
|
|
52
|
+
this.lastUpdatedAt = new Date()
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
@BeforeInsert()
|
|
56
|
+
@BeforeUpdate()
|
|
57
|
+
async validate() {
|
|
58
|
+
const validation = await validate(this)
|
|
59
|
+
if (validation.length > 0) {
|
|
60
|
+
return Promise.reject(Error(validation[0].constraints?.isNotEmpty))
|
|
61
|
+
}
|
|
62
|
+
return
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export const contactEntityFrom = (args: IBasicContact): ContactEntity => {
|
|
67
|
+
const contactEntity = new ContactEntity()
|
|
68
|
+
contactEntity.name = args.name
|
|
69
|
+
contactEntity.alias = args.alias
|
|
70
|
+
contactEntity.uri = args.uri
|
|
71
|
+
if (args.identities) {
|
|
72
|
+
contactEntity.identities = args.identities.map((identity: IBasicIdentity) => identityEntityFrom(identity))
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return contactEntity
|
|
76
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { Entity, Column, PrimaryGeneratedColumn, BaseEntity, OneToOne, JoinColumn, BeforeInsert, BeforeUpdate } from 'typeorm'
|
|
2
|
+
import { CorrelationIdentifierEnum, BasicCorrelationIdentifier } from '../../types/contact'
|
|
3
|
+
import { IdentityEntity } from './IdentityEntity'
|
|
4
|
+
import { IsNotEmpty, validate } from 'class-validator'
|
|
5
|
+
|
|
6
|
+
@Entity('CorrelationIdentifier')
|
|
7
|
+
export class CorrelationIdentifierEntity extends BaseEntity {
|
|
8
|
+
@PrimaryGeneratedColumn('uuid')
|
|
9
|
+
id!: string
|
|
10
|
+
|
|
11
|
+
@Column('simple-enum', { name: 'type', enum: CorrelationIdentifierEnum, nullable: false })
|
|
12
|
+
type!: CorrelationIdentifierEnum
|
|
13
|
+
|
|
14
|
+
@Column('text', { name: 'correlation_id', nullable: false, unique: true })
|
|
15
|
+
@IsNotEmpty({ message: 'Blank correlation ids are not allowed' })
|
|
16
|
+
correlationId!: string
|
|
17
|
+
|
|
18
|
+
@OneToOne(() => IdentityEntity, (identity: IdentityEntity) => identity.identifier, {
|
|
19
|
+
onDelete: 'CASCADE',
|
|
20
|
+
})
|
|
21
|
+
@JoinColumn({ name: 'identityId' })
|
|
22
|
+
identity!: IdentityEntity
|
|
23
|
+
|
|
24
|
+
@BeforeInsert()
|
|
25
|
+
@BeforeUpdate()
|
|
26
|
+
async validate() {
|
|
27
|
+
const validation = await validate(this)
|
|
28
|
+
if (validation.length > 0) {
|
|
29
|
+
return Promise.reject(Error(validation[0].constraints?.isNotEmpty))
|
|
30
|
+
}
|
|
31
|
+
return
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export const correlationIdentifierEntityFrom = (identifier: BasicCorrelationIdentifier): CorrelationIdentifierEntity => {
|
|
36
|
+
const identifierEntity = new CorrelationIdentifierEntity()
|
|
37
|
+
identifierEntity.type = identifier.type
|
|
38
|
+
identifierEntity.correlationId = identifier.correlationId
|
|
39
|
+
|
|
40
|
+
return identifierEntity
|
|
41
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { ChildEntity, Column, JoinColumn, OneToOne } from 'typeorm'
|
|
2
|
+
import { BaseConfigEntity } from './BaseConfigEntity'
|
|
3
|
+
import { BasicDidAuthConfig } from '../../types/contact'
|
|
4
|
+
import { ConnectionEntity } from './ConnectionEntity'
|
|
5
|
+
|
|
6
|
+
@ChildEntity('DidAuthConfig')
|
|
7
|
+
export class DidAuthConfigEntity extends BaseConfigEntity {
|
|
8
|
+
@Column({ name: 'identifier', length: 255, nullable: false })
|
|
9
|
+
identifier!: string
|
|
10
|
+
|
|
11
|
+
@Column({ name: 'redirect_url', length: 255, nullable: false })
|
|
12
|
+
redirectUrl!: string
|
|
13
|
+
|
|
14
|
+
@Column({ name: 'session_id', length: 255, nullable: false })
|
|
15
|
+
sessionId!: string
|
|
16
|
+
|
|
17
|
+
@OneToOne(() => ConnectionEntity, (connection: ConnectionEntity) => connection.config, {
|
|
18
|
+
onDelete: 'CASCADE',
|
|
19
|
+
})
|
|
20
|
+
@JoinColumn({ name: 'connectionId' })
|
|
21
|
+
connection?: ConnectionEntity
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const didAuthConfigEntityFrom = (config: BasicDidAuthConfig): DidAuthConfigEntity => {
|
|
25
|
+
const didAuthConfig = new DidAuthConfigEntity()
|
|
26
|
+
didAuthConfig.identifier = config.identifier.did
|
|
27
|
+
didAuthConfig.redirectUrl = config.redirectUrl
|
|
28
|
+
didAuthConfig.sessionId = config.sessionId
|
|
29
|
+
|
|
30
|
+
return didAuthConfig
|
|
31
|
+
}
|