@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.
Files changed (100) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +67 -0
  3. package/dist/contact/AbstractContactStore.d.ts +15 -0
  4. package/dist/contact/AbstractContactStore.d.ts.map +1 -0
  5. package/dist/contact/AbstractContactStore.js +7 -0
  6. package/dist/contact/AbstractContactStore.js.map +1 -0
  7. package/dist/contact/ContactStore.d.ts +30 -0
  8. package/dist/contact/ContactStore.d.ts.map +1 -0
  9. package/dist/contact/ContactStore.js +281 -0
  10. package/dist/contact/ContactStore.js.map +1 -0
  11. package/dist/entities/contact/BaseConfigEntity.d.ts +5 -0
  12. package/dist/entities/contact/BaseConfigEntity.d.ts.map +1 -0
  13. package/dist/entities/contact/BaseConfigEntity.js +26 -0
  14. package/dist/entities/contact/BaseConfigEntity.js.map +1 -0
  15. package/dist/entities/contact/ConnectionEntity.d.ts +12 -0
  16. package/dist/entities/contact/ConnectionEntity.d.ts.map +1 -0
  17. package/dist/entities/contact/ConnectionEntity.js +66 -0
  18. package/dist/entities/contact/ConnectionEntity.js.map +1 -0
  19. package/dist/entities/contact/ContactEntity.d.ts +16 -0
  20. package/dist/entities/contact/ContactEntity.d.ts.map +1 -0
  21. package/dist/entities/contact/ContactEntity.js +108 -0
  22. package/dist/entities/contact/ContactEntity.js.map +1 -0
  23. package/dist/entities/contact/CorrelationIdentifierEntity.d.ts +12 -0
  24. package/dist/entities/contact/CorrelationIdentifierEntity.d.ts.map +1 -0
  25. package/dist/entities/contact/CorrelationIdentifierEntity.js +76 -0
  26. package/dist/entities/contact/CorrelationIdentifierEntity.js.map +1 -0
  27. package/dist/entities/contact/DidAuthConfigEntity.d.ts +11 -0
  28. package/dist/entities/contact/DidAuthConfigEntity.d.ts.map +1 -0
  29. package/dist/entities/contact/DidAuthConfigEntity.js +49 -0
  30. package/dist/entities/contact/DidAuthConfigEntity.js.map +1 -0
  31. package/dist/entities/contact/IdentityEntity.d.ts +22 -0
  32. package/dist/entities/contact/IdentityEntity.d.ts.map +1 -0
  33. package/dist/entities/contact/IdentityEntity.js +137 -0
  34. package/dist/entities/contact/IdentityEntity.js.map +1 -0
  35. package/dist/entities/contact/IdentityMetadataItemEntity.d.ts +12 -0
  36. package/dist/entities/contact/IdentityMetadataItemEntity.d.ts.map +1 -0
  37. package/dist/entities/contact/IdentityMetadataItemEntity.js +73 -0
  38. package/dist/entities/contact/IdentityMetadataItemEntity.js.map +1 -0
  39. package/dist/entities/contact/OpenIdConfigEntity.d.ts +15 -0
  40. package/dist/entities/contact/OpenIdConfigEntity.d.ts.map +1 -0
  41. package/dist/entities/contact/OpenIdConfigEntity.js +70 -0
  42. package/dist/entities/contact/OpenIdConfigEntity.js.map +1 -0
  43. package/dist/index.d.ts +16 -0
  44. package/dist/index.d.ts.map +1 -0
  45. package/dist/index.js +59 -0
  46. package/dist/index.js.map +1 -0
  47. package/dist/migrations/generic/1-CreateContacts.d.ts +7 -0
  48. package/dist/migrations/generic/1-CreateContacts.d.ts.map +1 -0
  49. package/dist/migrations/generic/1-CreateContacts.js +72 -0
  50. package/dist/migrations/generic/1-CreateContacts.js.map +1 -0
  51. package/dist/migrations/generic/index.d.ts +10 -0
  52. package/dist/migrations/generic/index.d.ts.map +1 -0
  53. package/dist/migrations/generic/index.js +13 -0
  54. package/dist/migrations/generic/index.js.map +1 -0
  55. package/dist/migrations/index.d.ts +2 -0
  56. package/dist/migrations/index.d.ts.map +1 -0
  57. package/dist/migrations/index.js +6 -0
  58. package/dist/migrations/index.js.map +1 -0
  59. package/dist/migrations/internal-migrations-ormconfig.d.ts +6 -0
  60. package/dist/migrations/internal-migrations-ormconfig.d.ts.map +1 -0
  61. package/dist/migrations/internal-migrations-ormconfig.js +29 -0
  62. package/dist/migrations/internal-migrations-ormconfig.js.map +1 -0
  63. package/dist/migrations/postgres/1659463079428-CreateContacts.d.ts +7 -0
  64. package/dist/migrations/postgres/1659463079428-CreateContacts.d.ts.map +1 -0
  65. package/dist/migrations/postgres/1659463079428-CreateContacts.js +55 -0
  66. package/dist/migrations/postgres/1659463079428-CreateContacts.js.map +1 -0
  67. package/dist/migrations/sqlite/1659463069549-CreateContacts.d.ts +7 -0
  68. package/dist/migrations/sqlite/1659463069549-CreateContacts.d.ts.map +1 -0
  69. package/dist/migrations/sqlite/1659463069549-CreateContacts.js +79 -0
  70. package/dist/migrations/sqlite/1659463069549-CreateContacts.js.map +1 -0
  71. package/dist/types/IAbstractContactStore.d.ts +41 -0
  72. package/dist/types/IAbstractContactStore.d.ts.map +1 -0
  73. package/dist/types/IAbstractContactStore.js +3 -0
  74. package/dist/types/IAbstractContactStore.js.map +1 -0
  75. package/dist/types/contact.d.ts +91 -0
  76. package/dist/types/contact.d.ts.map +1 -0
  77. package/dist/types/contact.js +21 -0
  78. package/dist/types/contact.js.map +1 -0
  79. package/package.json +45 -0
  80. package/src/__tests__/contact.entities.test.ts +911 -0
  81. package/src/__tests__/contact.store.test.ts +743 -0
  82. package/src/contact/AbstractContactStore.ts +26 -0
  83. package/src/contact/ContactStore.ts +373 -0
  84. package/src/entities/contact/BaseConfigEntity.ts +8 -0
  85. package/src/entities/contact/ConnectionEntity.ts +48 -0
  86. package/src/entities/contact/ContactEntity.ts +76 -0
  87. package/src/entities/contact/CorrelationIdentifierEntity.ts +41 -0
  88. package/src/entities/contact/DidAuthConfigEntity.ts +31 -0
  89. package/src/entities/contact/IdentityEntity.ts +106 -0
  90. package/src/entities/contact/IdentityMetadataItemEntity.ts +39 -0
  91. package/src/entities/contact/OpenIdConfigEntity.ts +48 -0
  92. package/src/index.ts +45 -0
  93. package/src/migrations/generic/1-CreateContacts.ts +54 -0
  94. package/src/migrations/generic/index.ts +10 -0
  95. package/src/migrations/index.ts +1 -0
  96. package/src/migrations/internal-migrations-ormconfig.ts +28 -0
  97. package/src/migrations/postgres/1659463079428-CreateContacts.ts +61 -0
  98. package/src/migrations/sqlite/1659463069549-CreateContacts.ts +111 -0
  99. package/src/types/IAbstractContactStore.ts +51 -0
  100. 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
+ }