Skip to content

Commit

Permalink
feat: Selective Disclosure Request
Browse files Browse the repository at this point in the history
  • Loading branch information
simonas-notcat committed Nov 29, 2019
1 parent 1c45a8f commit 9afe0c5
Show file tree
Hide file tree
Showing 9 changed files with 247 additions and 40 deletions.
1 change: 1 addition & 0 deletions packages/daf-cli/src/cli.ts
Expand Up @@ -5,6 +5,7 @@ import './credential'
import './services'
import './data-explorer'
import './graphql'
import './sdr'

program.parse(process.argv)
if (!process.argv.slice(2).length) {
Expand Down
3 changes: 3 additions & 0 deletions packages/daf-cli/src/graphql.ts
Expand Up @@ -3,6 +3,7 @@ import program from 'commander'
import * as Daf from 'daf-core'
import * as W3c from 'daf-w3c'
import * as TG from 'daf-trust-graph'
import * as SRD from 'daf-selective-disclosure'
import { Gql as DataGql } from 'daf-data-store'
import merge from 'lodash.merge'
import { core, dataStore } from './setup'
Expand All @@ -22,13 +23,15 @@ program
DataGql.typeDefs,
TG.Gql.typeDefs,
W3c.Gql.typeDefs,
SRD.Gql.typeDefs,
],
resolvers: merge(
Daf.Gql.Core.resolvers,
Daf.Gql.IdentityManager.resolvers,
DataGql.resolvers,
TG.Gql.resolvers,
W3c.Gql.resolvers,
SRD.Gql.resolvers,
),
context: () => ({ dataStore, core }),
introspection: true,
Expand Down
120 changes: 120 additions & 0 deletions packages/daf-cli/src/sdr.ts
@@ -0,0 +1,120 @@
import * as W3c from 'daf-w3c'
import * as DIDComm from 'daf-did-comm'
import * as SD from 'daf-selective-disclosure'
import { core, dataStore } from './setup'
import program from 'commander'
import inquirer from 'inquirer'
import qrcode from 'qrcode-terminal'

program
.command('sdr')
.description('Create Selective Disclosure Request')
.option('-s, --send', 'Send')
.option('-q, --qrcode', 'Show qrcode')
.action(async cmd => {
const myDids = await core.identityManager.listDids()
if (myDids.length === 0) {
console.error('No dids')
process.exit()
}
const answers = await inquirer.prompt([
{
type: 'list',
name: 'iss',
choices: myDids,
message: 'Issuer DID',
},
{
type: 'input',
name: 'sub',
message: 'Subject DID (can be empty)',
},
{
type: 'input',
name: 'tag',
message: 'Tag',
},
])

let addMoreRequests = true
const claims = []

while (addMoreRequests) {
const answers2 = await inquirer.prompt([
{
type: 'input',
name: 'claimType',
message: 'Claim type',
default: 'name',
},
{
type: 'input',
name: 'reason',
message: 'Reason',
default: 'We need this to comply with local law',
},
{
type: 'list',
name: 'essential',
message: 'Is essential',
choices: [
{ name: 'Yes', value: true },
{ name: 'No', value: false },
],
},
{
type: 'list',
name: 'addMore',
message: 'Add another credential?',
choices: [
{ name: 'Yes', value: true },
{ name: 'No', value: false },
],
},
])
claims.push({
essential: answers2.essential,
claimType: answers2.claimType,
reason: answers2.reason,
} as SD.CredentialRequestInput)
addMoreRequests = answers2.addMore
}

const signAction: SD.ActionSignSdr = {
type: SD.ActionTypes.signSdr,
did: answers.iss,
data: {
tag: answers.tag === '' ? undefined : answers.tag,
sub: answers.sub === '' ? undefined : answers.sub,
claims,
},
}

const jwt = await core.handleAction(signAction)

await dataStore.initialize()
if (!cmd.send) {
await core.onRawMessage({ raw: jwt })
} else if (answers.sub !== '') {
const sendAction: DIDComm.ActionSendJWT = {
type: DIDComm.ActionTypes.sendJwt,
data: {
from: answers.iss,
to: answers.sub,
jwt,
},
}
try {
const result = await core.handleAction(sendAction)
console.log('Sent:', result)
} catch (e) {
console.error(e)
}
} else {
console.log('Subject not specified')
}

if (cmd.qrcode) {
qrcode.generate(jwt)
}
})
1 change: 1 addition & 0 deletions packages/daf-core/src/types.ts
Expand Up @@ -14,6 +14,7 @@ export interface PreValidatedMessage {
issuer: string
subject?: string
time?: string
tag?: string
raw: string
verified?: any
custom?: any
Expand Down
81 changes: 54 additions & 27 deletions packages/daf-data-store/src/data-store.ts
Expand Up @@ -42,6 +42,7 @@ export class DataStore {
sub: { did: row.sub },
jwt: row.jwt,
nbf: row.nbf,
iat: row.iat,
}))
}

Expand All @@ -62,6 +63,7 @@ export class DataStore {
sub: { did: row.sub },
jwt: row.jwt,
nbf: row.nbf,
iat: row.iat,
exp: row.exp,
}))
}
Expand All @@ -87,26 +89,63 @@ export class DataStore {
}))
}

async findMessages({
async findCredentialsByFields({
iss,
sub,
tag,
limit,
claim_type,
}: {
iss?: string
sub?: string
tag?: string
limit?: number
iss?: string[]
sub?: string[]
claim_type: string
}) {
let where = {}

if (iss) where = sql.and(where, sql.in('iss', iss))
if (sub) where = sql.and(where, sql.in('sub', sub))
if (claim_type) where = sql.and(where, { claim_type })

const query = sql
.select('rowid', '*')
.from('verifiable_credentials_fields')
.where(where)
.toParams()

const rows = await this.db.rows(query.text, query.values)

const hashes = rows.map((row: any) => row.parent_hash)

const query2 = sql
.select('rowid', '*')
.from('verifiable_credentials')
.where(sql.in('hash', hashes))
.toParams()

const rows2 = await this.db.rows(query2.text, query2.values)

return rows2.map((row: any) => ({
rowId: `${row.rowid}`,
hash: row.hash,
parentHash: row.parent_hash,
iss: { did: row.iss },
sub: { did: row.sub },
jwt: row.jwt,
nbf: row.nbf,
iat: row.iat,
exp: row.exp,
}))
}

async findMessages({ iss, sub, tag, limit }: { iss?: string; sub?: string; tag?: string; limit?: number }) {
let where = {}

if (iss && sub) {
where = sql.or(where, { iss, sub })
} else {
if (iss) where = sql.and(where, { iss })
if (sub) where = sql.and(where, { sub })
}
if (tag) where = sql.and(where, { tag })
where = sql.or(where, { sub: null })

let query = sql
.select('rowid', '*')
Expand All @@ -127,6 +166,7 @@ export class DataStore {
iss: { did: row.iss },
sub: { did: row.sub },
type: row.type,
tag: row.tag,
data: row.data,
jwt: row.jwt,
nbf: row.nbf,
Expand All @@ -149,6 +189,7 @@ export class DataStore {
iss: { did: row.iss },
sub: { did: row.sub },
type: row.type,
tag: row.tag,
jwt: row.jwt,
data: row.data,
nbf: row.nbf,
Expand All @@ -158,22 +199,13 @@ export class DataStore {
}

async allIdentities() {
const vcSubjects = await this.db.rows(
'select distinct sub as did from verifiable_credentials',
null,
)
const vcIssuers = await this.db.rows(
'select distinct iss as did from verifiable_credentials',
null,
)
const vcSubjects = await this.db.rows('select distinct sub as did from verifiable_credentials', null)
const vcIssuers = await this.db.rows('select distinct iss as did from verifiable_credentials', null)
const messageSubjects = await this.db.rows(
'select distinct sub as did from messages where sub is not null',
null,
)
const messageIssuers = await this.db.rows(
'select distinct iss as did from messages',
null,
)
const messageIssuers = await this.db.rows('select distinct iss as did from messages', null)
const uniqueDids = [
...new Set([
...messageSubjects.map((item: any) => item.did),
Expand Down Expand Up @@ -204,12 +236,7 @@ export class DataStore {
let query = sql
.select('count(*) as count')
.from('messages')
.where(
sql.or(
sql.and({ iss: did1 }, { sub: did2 }),
sql.and({ iss: did2 }, { sub: did1 }),
),
)
.where(sql.or(sql.and({ iss: did1 }, { sub: did2 }), sql.and({ iss: did2 }, { sub: did1 })))
.toParams()
const rows = await this.db.rows(query.text, query.values)

Expand Down Expand Up @@ -246,6 +273,7 @@ export class DataStore {
sub: message.subject,
nbf: message.time,
type: message.type,
tag: message.tag,
jwt: message.raw,
meta: message.meta && JSON.stringify(message.meta),
source_type,
Expand Down Expand Up @@ -288,8 +316,7 @@ export class DataStore {
for (const type in claim) {
if (claim.hasOwnProperty(type)) {
const value = claim[type]
const isObj =
typeof value === 'function' || (typeof value === 'object' && !!value)
const isObj = typeof value === 'function' || (typeof value === 'object' && !!value)

const fieldsQuery = sql
.insert('verifiable_credentials_fields', {
Expand Down
18 changes: 18 additions & 0 deletions packages/daf-selective-disclosure/src/action-handler.ts
Expand Up @@ -8,10 +8,28 @@ export const ActionTypes = {
signSdr: 'action.sign.sdr',
}

interface Iss {
did: string
url: string
}

export interface SDRInput {
state?: string
claims: CredentialRequestInput[]
}

export interface CredentialRequestInput {
reason?: string
essential?: boolean
claimType: string
iss?: Iss[]
}

export interface ActionSignSdr extends Types.Action {
did: string
data: {
sub?: string
tag?: string
claims: any
}
}
Expand Down

0 comments on commit 9afe0c5

Please sign in to comment.