Skip to content

Commit

Permalink
Merge pull request #128 from Sphereon-Opensource/DPP-42
Browse files Browse the repository at this point in the history
Added identies to contacts
  • Loading branch information
sanderPostma authored Sep 20, 2023
2 parents 5c45dbe + 582fc67 commit 1a69153
Show file tree
Hide file tree
Showing 16 changed files with 404 additions and 280 deletions.
4 changes: 2 additions & 2 deletions packages/contact-manager-rest-api/__tests__/RestAPI.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {ExpressCorsConfigurer} from "../../ssi-express-support/src";
import { ExpressCorsConfigurer } from '../../ssi-express-support/src'
import { ExpressBuilder } from '../../ssi-express-support/src'
import { ContactManagerApiServer } from '../src'
import agent from './agent'
Expand All @@ -9,7 +9,7 @@ const builder = ExpressBuilder.fromServerOpts({
})
.withMorganLogging({ format: 'dev' })
.withPassportAuth(false)
.withCorsConfigurer(new ExpressCorsConfigurer().allowOrigin('*'))
.withCorsConfigurer(new ExpressCorsConfigurer().allowOrigin('*'))

const expressSupport = builder.build({ startListening: true })

Expand Down
226 changes: 171 additions & 55 deletions packages/contact-manager-rest-api/__tests__/agent.ts
Original file line number Diff line number Diff line change
@@ -1,62 +1,178 @@
import { createAgent } from '@veramo/core'
import { DataStore, DataStoreORM } from '@veramo/data-store'
import { IRequiredPlugins } from '../src'
import { DB_CONNECTION_NAME, sqliteConfig } from './database'
import { ContactManager } from '@sphereon/ssi-sdk.contact-manager'
import { ContactStore, PartyTypeEnum } from '@sphereon/ssi-sdk.data-store'
import { DataSources } from '@sphereon/ssi-sdk.agent-config'
import { v4 } from 'uuid'
import {createAgent, IIdentifier} from '@veramo/core'
import {DataStore, DataStoreORM, DIDStore} from '@veramo/data-store'
import {IRequiredPlugins} from '../src'
import {DB_CONNECTION_NAME, sqliteConfig} from './database'
import {AddContactArgs, ContactManager} from '@sphereon/ssi-sdk.contact-manager'
import {
ContactStore,
CorrelationIdentifierEnum,
IdentityRoleEnum,
NonPersistedIdentity,
PartyTypeEnum
} from '@sphereon/ssi-sdk.data-store'
import {DataSources} from '@sphereon/ssi-sdk.agent-config'
import {v4} from 'uuid'
import {DIDManager} from "@veramo/did-manager";
import {KeyManager, MemoryKeyStore, MemoryPrivateKeyStore} from "@veramo/key-manager";
import {KeyManagementSystem} from "@veramo/kms-local";
import {IonDIDProvider} from "@veramo/did-provider-ion";
import {IonPublicKeyPurpose} from "@decentralized-identity/ion-sdk";

export const DID_PREFIX = 'did'

const PRIVATE_RECOVERY_KEY_HEX = 'd39e66e720c00b244923eb861122ed25116555ae771ee9a57b749640173d7cf8'
const PRIVATE_UPDATE_KEY_HEX = '0121009becfa9caf6221dce6f4f7b55dd3376e79c4ca83ce92bd43861c2393ec'
const PRIVATE_DID1_KEY_HEX = 'e0453c226bd4458740c45f0d0590e696da2fe9c5c66f81908aedd43a7b7da252'
const PRIVATE_DID2_KEY_HEX = '74213f5204ea414deb4dc2c470d1700b8cc2076ddd8d3ddb06dae08902dddd0c'
const PRIVATE_DID3_KEY_HEX = '90868704b3bb2bdd27e2e831654c4adb2ea7e4f0e090d03aa3ae38020346aa12'
const PRIVATE_DID4_KEY_HEX = 'f367873323bf0dd701ec972d8a17aee7a9dcad13bd6deb64e8653da113094261'

export enum KeyManagementSystemEnum {
LOCAL = 'local',
}

export enum SupportedDidMethodEnum {
DID_ION = 'ion',
}

export const didProviders = {
[`${DID_PREFIX}:${SupportedDidMethodEnum.DID_ION}`]: new IonDIDProvider({
defaultKms: KeyManagementSystemEnum.LOCAL,
}),
}

const dbConnection = DataSources.singleInstance()
.addConfig(DB_CONNECTION_NAME, sqliteConfig)
.getDbConnection(DB_CONNECTION_NAME)
.addConfig(DB_CONNECTION_NAME, sqliteConfig)
.getDbConnection(DB_CONNECTION_NAME)


const agent = createAgent<IRequiredPlugins>({
plugins: [new DataStore(dbConnection), new DataStoreORM(dbConnection), new ContactManager({ store: new ContactStore(dbConnection) })],
plugins: [
new DataStore(dbConnection),
new DataStoreORM(dbConnection),
new ContactManager({store: new ContactStore(dbConnection)}),
new KeyManager({
store: new MemoryKeyStore(),
kms: {
local: new KeyManagementSystem(new MemoryPrivateKeyStore()),
},
}),
new DIDManager({
store: new DIDStore(dbConnection),
defaultProvider: `${DID_PREFIX}:${SupportedDidMethodEnum.DID_ION}`,
providers: didProviders,
}),
],
})
const generateIdentity = (contact: Record<string, any>, didKeyIdentifier: IIdentifier): NonPersistedIdentity => {
return {
alias: didKeyIdentifier.alias,
roles: [IdentityRoleEnum.ISSUER],
identifier: {
type: CorrelationIdentifierEnum.DID,
correlationId: didKeyIdentifier.did,
},
} as NonPersistedIdentity
};

async function addContacts() {
try {
const ctPeople = await agent.cmAddContactType({
name: "people",
type: PartyTypeEnum.NATURAL_PERSON,
tenantId: v4()
});

const ctOrganizations = await agent.cmAddContactType({
name: "organizations",
type: PartyTypeEnum.ORGANIZATION,
tenantId: v4()
});


const persona1 = {
firstName: "Viola",
middleName: "D.",
lastName: "Kemp",
displayName: "Viola Kemp",
contactType: ctPeople,
uri: "example.com"
} as AddContactArgs;

let didKeyIdentifier = await agent.didManagerCreate(existingDidConfig(false, 'bram', PRIVATE_DID1_KEY_HEX));
persona1.identities = [generateIdentity(persona1, didKeyIdentifier)];
await agent.cmAddContact(persona1);

const persona2 = {
firstName: "Kevin",
middleName: "T.",
lastName: "Bloomer",
displayName: "Kevin Bloomer",
contactType: ctPeople,
uri: "example.com"
} as AddContactArgs;

didKeyIdentifier = await agent.didManagerCreate(existingDidConfig(false, 'kraak', PRIVATE_DID2_KEY_HEX));
persona2.identities = [generateIdentity(persona2, didKeyIdentifier)];
await agent.cmAddContact(persona2);


const organization1 = {
legalName: "Sphereon International",
displayName: "Sphereon B.V.",
contactType: ctOrganizations,
uri: "sphereon.com"
} as AddContactArgs;

didKeyIdentifier = await agent.didManagerCreate(existingDidConfig(false, 'sphereon', PRIVATE_DID3_KEY_HEX));
organization1.identities = [generateIdentity(organization1, didKeyIdentifier)];
await agent.cmAddContact(organization1);

const organization2 = {
legalName: "Kamer van verkoophandel",
displayName: "Kamer van koophandel",
contactType: ctOrganizations,
uri: "kvk.nl"
} as AddContactArgs;

didKeyIdentifier = await agent.didManagerCreate(existingDidConfig(false, 'kvk', PRIVATE_DID4_KEY_HEX));
organization2.identities = [generateIdentity(organization2, didKeyIdentifier)];
await agent.cmAddContact(organization2);
} catch (e) {
console.log(e);
}
}


function existingDidConfig(anchor: boolean = false, kid: string, privateDIDKeyHex: String) {
return {
options: {
anchor,
recoveryKey: {
kid: 'recovery-test2',
key: {
privateKeyHex: PRIVATE_RECOVERY_KEY_HEX,
},
},
updateKey: {
kid: 'update-test2',
key: {
privateKeyHex: PRIVATE_UPDATE_KEY_HEX,
},
},
verificationMethods: [
{
kid,
purposes: [IonPublicKeyPurpose.Authentication, IonPublicKeyPurpose.AssertionMethod],
key: {
privateKeyHex: privateDIDKeyHex,
},
},
],
},
}
}

addContacts()

agent.cmAddContactType({
name: "people",
type: PartyTypeEnum.NATURAL_PERSON,
tenantId: v4()
}).then(ct=> {
agent.cmAddContact({
firstName: "Abraham",
middleName: "Gerrit Jan",
lastName: "ten Cate",
displayName: "Bram ten Cate",
contactType: ct,
uri: "example.com"
})
agent.cmAddContact({
firstName: "Kraak",
middleName: "en",
lastName: "Smaak",
displayName: "Kraak en Smaak",
contactType: ct,
uri: "example.com"
})
}).catch(e => {
console.log(e)
})
agent.cmAddContactType({
name: "orgzanizations",
type: PartyTypeEnum.ORGANIZATION,
tenantId: v4()
}).then(ct=> {
agent.cmAddContact({
legalName: "Sphereon International",
displayName: "Sphereon B.V.",
contactType: ct,
uri: "sphereon.com"
})
agent.cmAddContact({
legalName: "Kamer van verkoophandel",
displayName: "Kamer van koophandel",
contactType: ct,
uri: "kvk.nl"
})
}).catch(e=> {
console.log(e)
})
export default agent
12 changes: 7 additions & 5 deletions packages/contact-manager-rest-api/__tests__/database/config.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
import { DataStoreContactEntities } from '@sphereon/ssi-sdk.data-store'
import { SqliteConnectionOptions } from 'typeorm/driver/sqlite/SqliteConnectionOptions'
import { DataStoreContactMigrations } from '@sphereon/ssi-sdk.data-store/dist/migrations/generic'
import {DataStoreContactEntities} from '@sphereon/ssi-sdk.data-store'
import {SqliteConnectionOptions} from 'typeorm/driver/sqlite/SqliteConnectionOptions'
import {DataStoreContactMigrations} from '@sphereon/ssi-sdk.data-store/dist/migrations/generic'
import {Entities as VeramoDataStoreEntities} from "@veramo/data-store";
import {migrations as VeramoDataStoreMigrations} from "@veramo/data-store/build/migrations";

const DB_CONNECTION_NAME = 'default'
const DB_ENCRYPTION_KEY = '29739248cad1bd1a0fc4d9b75cd4d2990de535baf5caadfdf8d8f86664aa830c'

const sqliteConfig: SqliteConnectionOptions = {
type: 'sqlite',
database: '__tests__/database/test.sqlite',
entities: [...DataStoreContactEntities],
migrations: [...DataStoreContactMigrations],
entities: [...DataStoreContactEntities, ...VeramoDataStoreEntities],
migrations: [...DataStoreContactMigrations, ...VeramoDataStoreMigrations],
migrationsRun: false, // We run migrations from code to ensure proper ordering with Redux
synchronize: false, // We do not enable synchronize, as we use migrations from code
migrationsTransactionMode: 'each', // protect every migration with a separate transaction
Expand Down
3 changes: 3 additions & 0 deletions packages/contact-manager-rest-api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@
"@veramo/key-manager": "4.2.0",
"@veramo/kms-local": "4.2.0",
"@veramo/utils": "4.2.0",
"@veramo/did-manager": "4.2.0",
"@veramo/did-provider-ion": "4.2.0",
"@decentralized-identity/ion-sdk": "^0.6.0",
"morgan": "^1.10.0",
"nock": "^13.2.1",
"passport": "^0.6.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
partiesTypeReadEndpoint,
partyWriteEndpoint,
partyTypeReadEndpoint,
identitiesReadEndpoint
identitiesReadEndpoint,
} from './api-functions'
import { copyGlobalAuthToEndpoints, ExpressSupport } from '@sphereon/ssi-express-support'

Expand All @@ -32,12 +32,7 @@ export class ContactManagerApiServer {
this._express = args.expressSupport.express
this._router = express.Router()
const context = agentContext(agent)
const features = opts?.enableFeatures ?? [
'party_read',
'party_write',
'party_type_read',
'identity_read'
]
const features = opts?.enableFeatures ?? ['party_read', 'party_write', 'party_type_read', 'identity_read']
console.log(`Contact Manager API enabled, with features: ${JSON.stringify(features)}}`)

// endpoints
Expand Down
10 changes: 3 additions & 7 deletions packages/contact-manager-rest-api/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@
import { GenericAuthArgs, ISingleEndpointOpts } from '@sphereon/ssi-express-support'
import { IContactManager } from '@sphereon/ssi-sdk.contact-manager'
import { IAgentContext, IDataStore, IKeyManager } from '@veramo/core'
import {IAgentContext, IDataStore, IDIDManager, IKeyManager} from '@veramo/core'

export type ContactManagerMRestApiFeatures =
'party_read' |
'party_write' |
'party_type_read' |
'identity_read'
export type ContactManagerMRestApiFeatures = 'party_read' | 'party_write' | 'party_type_read' | 'identity_read'

export interface IContactManagerAPIEndpointOpts {
endpointOpts?: {
Expand All @@ -20,5 +16,5 @@ export interface IContactManagerAPIEndpointOpts {
enableFeatures?: ContactManagerMRestApiFeatures[]
}

export type IRequiredPlugins = IContactManager & IDataStore & IKeyManager
export type IRequiredPlugins = IContactManager & IDataStore & IKeyManager & IDIDManager
export type IRequiredContext = IAgentContext<IRequiredPlugins>
1 change: 0 additions & 1 deletion packages/contact-manager/src/agent/ContactManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -184,5 +184,4 @@ export class ContactManager implements IAgentPlugin {

throw new Error('Contact not supported')
}

}
2 changes: 2 additions & 0 deletions packages/data-store/src/__tests__/contact.entities.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ import {
partyTypeEntityFrom,
} from '../utils/contact/MappingUtils'

// TODO write test adding two contacts reusing the same contactType

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

Expand Down
13 changes: 11 additions & 2 deletions packages/data-store/src/entities/contact/BaseContactEntity.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
import { BaseEntity,
import {
BaseEntity,
BeforeInsert,
BeforeUpdate, CreateDateColumn, Entity, JoinColumn, OneToOne, PrimaryGeneratedColumn, TableInheritance, UpdateDateColumn } from 'typeorm'
BeforeUpdate,
CreateDateColumn,
Entity,
JoinColumn,
OneToOne,
PrimaryGeneratedColumn,
TableInheritance,
UpdateDateColumn,
} from 'typeorm'
import { PartyEntity } from './PartyEntity'

@Entity('BaseContact')
Expand Down
18 changes: 0 additions & 18 deletions packages/data-store/src/entities/contact/PartyEntity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,24 +85,6 @@ export class PartyEntity extends BaseEntity {
this.lastUpdatedAt = new Date()
}

@BeforeInsert()
@BeforeUpdate()
async checkUniqueTenantId(): Promise<undefined> {
const result: Array<PartyEntity> = await PartyEntity.find({
where: {
partyType: {
tenantId: this.partyType.tenantId,
},
},
})

if (result?.length > 0) {
return Promise.reject(Error('Tenant id already in use'))
}

return
}

@BeforeInsert()
@BeforeUpdate()
async validate(): Promise<void> {
Expand Down
Loading

0 comments on commit 1a69153

Please sign in to comment.