diff --git a/packages/daf-cli/default/config.js b/packages/daf-cli/default/config.js new file mode 100644 index 000000000..e5d33a616 --- /dev/null +++ b/packages/daf-cli/default/config.js @@ -0,0 +1,79 @@ +var fs = require('fs') + +module.exports = { + identityProviders: [ + { + package: 'daf-ethr-did', + network: 'rinkeby', + rpcUrl: 'https://rinkeby.infura.io/v3/' + process.env.DAF_INFURA_ID, + gas: 10001, + ttl: 60 * 60 * 24 * 30 * 12 + 1, + }, + { + package: 'daf-ethr-did', + network: 'ropsten', + rpcUrl: 'https://ropsten.infura.io/v3/' + process.env.DAF_INFURA_ID, + }, + { + package: 'daf-ethr-did', + network: 'mainnet', + rpcUrl: 'https://mainnet.infura.io/v3/' + process.env.DAF_INFURA_ID, + }, + { + package: 'daf-ethr-did', + network: 'private', + rpcUrl: 'http://localhost:8545/', + registry: '0x05cc574b19a3c11308f761b3d7263bd8608bc532' + }, + { + package: 'daf-elem-did', + network: 'ropsten', + apiUrl: 'https://element-did.com/api/v1/sidetree', + }, + ], + ethrDidNetworks: [ + { + name: 'mainnet', + rpcUrl: 'https://mainnet.infura.io/v3/' + process.env.DAF_INFURA_ID + }, + { + name: 'rinkeby', + rpcUrl: 'https://rinkeby.infura.io/v3/' + process.env.DAF_INFURA_ID + }, + { + name: 'ropsten', + rpcUrl: 'https://ropsten.infura.io/v3/' + process.env.DAF_INFURA_ID + }, + { + name: 'kovan', + rpcUrl: 'https://kovan.infura.io/v3/' + process.env.DAF_INFURA_ID + }, + { + name: 'goerli', + rpcUrl: 'https://goerli.infura.io/v3/' + process.env.DAF_INFURA_ID + }, + { + name: 'private', + rpcUrl: 'http://localhost:8545/', + registry: '0x05cc574b19a3c11308f761b3d7263bd8608bc532' + } + ], + // https://typeorm.io/#/connection-options + database: { + type: 'sqlite', + synchronize: !fs.existsSync(process.env.DAF_DATA_STORE), + database: process.env.DAF_DATA_STORE, + logging: process.env.DAF_DEBUG_DB === 'true' ? true : false, + migrationsRun: true, + }, + graphql: { + apiKey: process.env.DAF_GRAPHQL_API_KEY, + resolvers: { + IdentityManager: true, + TrustGraph: false, + DIDComm: true, + W3c: true, + Sdr: true, + } + } +} \ No newline at end of file diff --git a/packages/daf-cli/package.json b/packages/daf-cli/package.json index 180ca92da..bb960152d 100644 --- a/packages/daf-cli/package.json +++ b/packages/daf-cli/package.json @@ -32,6 +32,7 @@ "debug": "^4.1.1", "dotenv": "^8.2.0", "graphql": "^14.5.8", + "graphql-tools": "^5.0.0", "inquirer": "^7.0.0", "lodash.merge": "^4.6.2", "qrcode-terminal": "^0.12.0", @@ -50,6 +51,7 @@ "files": [ "bin/**/*", "build/**/*", + "default/**/*", "src/**/*", "README.md", "LICENSE" diff --git a/packages/daf-cli/src/config.ts b/packages/daf-cli/src/config.ts new file mode 100644 index 000000000..211cd2f7b --- /dev/null +++ b/packages/daf-cli/src/config.ts @@ -0,0 +1,44 @@ +import { ConnectionOptions } from 'typeorm' +const fs = require('fs') + +export interface Configuration { + identityProviders: { + package: 'daf-ethr-did' | 'daf-elem-did' + network: string + rpcUrl?: string + apiUrl?: string + gas?: number + ttl?: number + registry?: string + }[], + ethrDidNetworks: { + name: string + rpcUrl: string + registry?: string + }[], + database: ConnectionOptions, + graphql: { + apiKey?: string + resolvers: { + IdentityManager: boolean + TrustGraph: boolean + DIDComm: boolean + W3c: boolean + Sdr: boolean + } + } +} + +const defaultPath = process.env.HOME + '/.daf/' +const configFile = process.env.DAF_CONFIG || defaultPath + 'config.js' + + +export const getConfiguration = (): Configuration => { + if (!fs.existsSync(configFile)) { + console.log('Config file does not exist. Creating: ' + configFile) + const contents = fs.readFileSync(__dirname + '/../default/config.js') + fs.writeFileSync(configFile, contents) + } + const configuration: Configuration = require(configFile) + return configuration +} \ No newline at end of file diff --git a/packages/daf-cli/src/graphql.ts b/packages/daf-cli/src/graphql.ts index f574a9b0f..cf572bf10 100644 --- a/packages/daf-cli/src/graphql.ts +++ b/packages/daf-cli/src/graphql.ts @@ -8,6 +8,9 @@ import { SdrGql } from 'daf-selective-disclosure' import merge from 'lodash.merge' import { agent } from './setup' import { listen } from './services' +import { makeExecutableSchema, mergeSchemas } from 'graphql-tools' +import { getConfiguration } from './config' + program .command('graphql') .description('GraphQL server') @@ -15,25 +18,74 @@ program .option('-l, --listen', 'Listen for new messages') .option('-i, --interval ', 'Poll for new messages with interval of ') .action(async cmd => { + await agent + const { graphql } = getConfiguration() + const schemas = [ + makeExecutableSchema({ + typeDefs: Gql.baseTypeDefs + Gql.Core.typeDefs, + resolvers: Gql.Core.resolvers + }) + ] + + if (graphql.resolvers.IdentityManager) { + schemas.push( + makeExecutableSchema({ + typeDefs: Gql.baseTypeDefs + Gql.IdentityManager.typeDefs, + resolvers: Gql.IdentityManager.resolvers + }) + ) + } + + if (graphql.resolvers.TrustGraph) { + schemas.push( + makeExecutableSchema({ + typeDefs: Gql.baseTypeDefs + TrustGraphGql.typeDefs, + resolvers: TrustGraphGql.resolvers + }) + ) + } + + if (graphql.resolvers.DIDComm) { + schemas.push( + makeExecutableSchema({ + typeDefs: Gql.baseTypeDefs + DIDCommGql.typeDefs, + resolvers: DIDCommGql.resolvers + }) + ) + } + + if (graphql.resolvers.W3c) { + schemas.push( + makeExecutableSchema({ + typeDefs: Gql.baseTypeDefs + W3cGql.typeDefs, + resolvers: W3cGql.resolvers + }) + ) + } + + if (graphql.resolvers.Sdr) { + schemas.push( + makeExecutableSchema({ + typeDefs: Gql.baseTypeDefs + SdrGql.typeDefs, + resolvers: SdrGql.resolvers + }) + ) + } + + const schema = mergeSchemas({ schemas }) + const server = new ApolloServer({ - typeDefs: [ - Gql.baseTypeDefs, - Gql.Core.typeDefs, - Gql.IdentityManager.typeDefs, - TrustGraphGql.typeDefs, - DIDCommGql.typeDefs, - W3cGql.typeDefs, - SdrGql.typeDefs, - ], - resolvers: merge( - Gql.Core.resolvers, - Gql.IdentityManager.resolvers, - TrustGraphGql.resolvers, - DIDCommGql.resolvers, - W3cGql.resolvers, - SdrGql.resolvers, - ), - context: async () => ({ agent: (await agent) }), + schema, + context: async ({ req }) => { + if (graphql.apiKey) { + const token = req.headers.authorization || '' + if (token !== 'Bearer ' + graphql.apiKey) { + throw Error('Auth error') + } + } + + return { agent: (await agent) } + }, introspection: true, }) // await core.setupServices() diff --git a/packages/daf-cli/src/setup.ts b/packages/daf-cli/src/setup.ts index 01048fe50..0a973b75f 100644 --- a/packages/daf-cli/src/setup.ts +++ b/packages/daf-cli/src/setup.ts @@ -12,22 +12,23 @@ import { SdrActionHandler, SdrMessageHandler } from 'daf-selective-disclosure' import { TrustGraphActionHandler, TrustGraphServiceController } from 'daf-trust-graph' import { DIDCommActionHandler, DIDCommMessageHandler } from 'daf-did-comm' import { UrlMessageHandler } from 'daf-url' -import { createConnection } from 'typeorm' +import { createConnection, ConnectionOptions } from 'typeorm' import { migrations } from './migrations' const fs = require('fs') import ws from 'ws' import { config } from 'dotenv' +import { getConfiguration } from './config' const defaultPath = process.env.HOME + '/.daf/' const envFile = defaultPath + '.env' -const writeDefaultConfig = async () => { +const writeDefaultEnv = async () => { if (!fs.existsSync(defaultPath)) { fs.mkdirSync(defaultPath) } if (!fs.existsSync(envFile)) { - console.log('Configuration file does not exist. Creating: ' + envFile) + console.log('Environment file does not exist. Creating: ' + envFile) let env = 'DAF_DATA_STORE=' + defaultPath + 'database-v2.sqlite' env += '\nDAF_DEBUG_DB=false' env += '\nDAF_SECRET_KEY=' + (await SecretBox.createSecretKey()) @@ -39,14 +40,13 @@ const writeDefaultConfig = async () => { } const setupAgent = async (): Promise => { - await writeDefaultConfig() + await writeDefaultEnv() config({ path: envFile }) - - const infuraProjectId = process.env.DAF_INFURA_ID + const configuration = getConfiguration() // DID Document Resolver let didResolver: Daf.Resolver = new DafResolver({ - infuraProjectId, + networks: configuration.ethrDidNetworks, }) if (process.env.DAF_UNIVERSAL_RESOLVER_URL) { @@ -59,35 +59,38 @@ const setupAgent = async (): Promise => { if (process.env.DAF_TG_WSURI) TrustGraphServiceController.defaultWsUri = process.env.DAF_TG_WSURI TrustGraphServiceController.webSocketImpl = ws - const synchronize = !fs.existsSync(process.env.DAF_DATA_STORE) - const dbConnection = createConnection({ - type: 'sqlite', - migrationsRun: true, - synchronize, - database: process.env.DAF_DATA_STORE, - logging: process.env.DAF_DEBUG_DB === 'true' ? true : false, + ...configuration.database, entities: [...Daf.Entities], migrations: [...Daf.migrations, ...migrations], }) - const identityProviders = [ - new EthrDid.IdentityProvider({ - identityStore: new Daf.IdentityStore('rinkeby-ethr', dbConnection), - kms: new KeyManagementSystem(new Daf.KeyStore(dbConnection, new SecretBox(process.env.DAF_SECRET_KEY))), - network: 'rinkeby', - rpcUrl: 'https://rinkeby.infura.io/v3/' + infuraProjectId, - gas: 10001, - ttl: 60 * 60 * 24 * 30 * 12 + 1, - }), - - new ElemDid.IdentityProvider({ - identityStore: new Daf.IdentityStore('elem-did', dbConnection), - kms: new KeyManagementSystem(new Daf.KeyStore(dbConnection, new SecretBox(process.env.DAF_SECRET_KEY))), - apiUrl: 'https://element-did.com/api/v1/sidetree', - network: 'ropsten' - }), - ] + const identityProviders: Daf.AbstractIdentityProvider[] = [] + + for (const identityProviderConfig of configuration.identityProviders) { + switch(identityProviderConfig.package) { + case 'daf-ethr-did': + identityProviders.push(new EthrDid.IdentityProvider({ + identityStore: new Daf.IdentityStore(identityProviderConfig.package + identityProviderConfig.network, dbConnection), + kms: new KeyManagementSystem(new Daf.KeyStore(dbConnection, new SecretBox(process.env.DAF_SECRET_KEY))), + network: identityProviderConfig.network, + rpcUrl: identityProviderConfig.rpcUrl, + gas: identityProviderConfig.gas, + ttl: identityProviderConfig.ttl, + registry: identityProviderConfig.registry + })) + break + case 'daf-elem-did': + identityProviders.push(new ElemDid.IdentityProvider({ + identityStore: new Daf.IdentityStore(identityProviderConfig.package + identityProviderConfig.network, dbConnection), + kms: new KeyManagementSystem(new Daf.KeyStore(dbConnection, new SecretBox(process.env.DAF_SECRET_KEY))), + apiUrl: identityProviderConfig.apiUrl, + network: identityProviderConfig.network + })) + break + } + } + const serviceControllers = [TrustGraphServiceController] const messageHandler = new UrlMessageHandler() diff --git a/packages/daf-core/src/graphql/graphql-base-type-defs.ts b/packages/daf-core/src/graphql/graphql-base-type-defs.ts index 028967c6b..78c1122c7 100644 --- a/packages/daf-core/src/graphql/graphql-base-type-defs.ts +++ b/packages/daf-core/src/graphql/graphql-base-type-defs.ts @@ -8,4 +8,11 @@ export const baseTypeDefs = ` provider: String } + type Message + type Presentation + type Credential + type Claim + + scalar Object + scalar Date ` diff --git a/packages/daf-core/src/graphql/graphql-core.ts b/packages/daf-core/src/graphql/graphql-core.ts index 204514869..7ed718046 100644 --- a/packages/daf-core/src/graphql/graphql-core.ts +++ b/packages/daf-core/src/graphql/graphql-core.ts @@ -688,10 +688,9 @@ export const typeDefs = ` updateDate: Date! } - scalar Object - scalar Date + - type Message { + extend type Message { id: ID! saveDate: Date! updateDate: Date! @@ -715,7 +714,7 @@ export const typeDefs = ` value: String } - type Presentation { + extend type Presentation { hash: ID! id: String raw: String! @@ -729,7 +728,7 @@ export const typeDefs = ` messages: [Message] } - type Credential { + extend type Credential { hash: ID! id: String raw: String! @@ -745,7 +744,7 @@ export const typeDefs = ` messages: [Message] } - type Claim { + extend type Claim { hash: ID! issuer: Identity! subject: Identity