Skip to content

Commit

Permalink
adding endpoint to retrieve user metadata by user (#5471)
Browse files Browse the repository at this point in the history
  • Loading branch information
julien51 authored Dec 13, 2019
1 parent db4fc24 commit 9411750
Show file tree
Hide file tree
Showing 3 changed files with 184 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
import request from 'supertest'
import * as sigUtil from 'eth-sig-util'
import * as ethJsUtil from 'ethereumjs-util'
import { keyTypedData } from '../../test-helpers/typeDataGenerators'
import { addMetadata } from '../../../src/operations/userMetadataOperations'

import app = require('../../../src/app')
import Base64 = require('../../../src/utils/base64')

const keyHolder = [
'0xAaAdEED4c0B861cB36f4cE006a9C90BA2E43fdc2',
'0x6f7a54d6629b7416e17fc472b4003ae8ef18ef4c',
]
const lockAddress = '0x95de5F777A3e283bFf0c47374998E10D8A2183C7'
const privateKey = ethJsUtil.toBuffer(
'0xfd8abdd241b9e7679e3ef88f05b31545816d6fbcaf11e86ebd5a57ba281ce229'
)

const mockOnChainLockOwnership = {
owner: jest.fn(() => {
return Promise.resolve(keyHolder[0])
}),
}

const mockKeyHoldersByLock = {
getKeyHoldingAddresses: jest.fn(() => {
return Promise.resolve([keyHolder[0]])
}),
}

jest.mock('../../../src/utils/lockData', () => {
return function() {
return mockOnChainLockOwnership
}
})

jest.mock('../../../src/graphql/datasource/keyholdersByLock', () => ({
__esModule: true,
KeyHoldersByLock: jest.fn(() => {
return mockKeyHoldersByLock
}),
}))

describe('reading address holder metadata', () => {
beforeAll(async () => {
await addMetadata({
tokenAddress: lockAddress,
userAddress: keyHolder[0],
data: {
protected: {
hidden: 'metadata',
},
public: {
mock: 'values',
},
},
})
})

it('yields the stored passed data if the timestamp is recent', async () => {
expect.assertions(2)
const typedData = keyTypedData({
UserMetaData: {
owner: keyHolder[0],
timestamp: Date.now(),
},
})

const sig = sigUtil.signTypedData(privateKey, {
data: typedData,
})

const response = await request(app)
.get(`/api/key/${lockAddress}/user/${keyHolder[0]}`)
.set('Accept', 'json')
.set('Authorization', `Bearer ${Base64.encode(sig)}`)
.query({ data: encodeURIComponent(JSON.stringify(typedData)) })

expect(response.status).toEqual(200)
expect(response.body).toEqual({
userMetadata: {
protected: {
hidden: 'metadata',
},
public: {
mock: 'values',
},
},
})
})

it('does not yield the stored passed data if the timestamp is old', async () => {
expect.assertions(2)
const typedData = keyTypedData({
UserMetaData: {
owner: keyHolder[0],
timestamp: 0,
},
})

const sig = sigUtil.signTypedData(privateKey, {
data: typedData,
})

const response = await request(app)
.get(`/api/key/${lockAddress}/user/${keyHolder[0]}`)
.set('Accept', 'json')
.set('Authorization', `Bearer ${Base64.encode(sig)}`)
.query({ data: encodeURIComponent(JSON.stringify(typedData)) })

expect(response.status).toEqual(401)
expect(response.body).toEqual({})
})

describe('when an invalid signature is passed', () => {
it('returns unauthorized', async () => {
expect.assertions(2)

const typedData = keyTypedData({
UserMetaData: {
owner: keyHolder[0],
protected: {
hidden: 'metadata',
},
public: {
mock: 'values',
},
},
})

const sig = sigUtil.signTypedData(privateKey, {
data: typedData,
})

const response = await request(app)
.get(`/api/key/${lockAddress}/user/${keyHolder[0]}`)
.set('Accept', 'json')
.set('Authorization', `Bearer ${Base64.encode(sig)}`)
.query({ data: encodeURIComponent(JSON.stringify(typedData)) })

expect(response.status).toEqual(401)
expect(response.body).toEqual({})
})
})
})
25 changes: 24 additions & 1 deletion locksmith/src/controllers/metadataController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Response } from 'express-serve-static-core' // eslint-disable-line no-u
import Normalizer from '../utils/normalizer'
import LockData from '../utils/lockData'
import { expiredSignature } from '../utils/signature'
import { addMetadata } from '../operations/userMetadataOperations'
import { addMetadata, getMetadata } from '../operations/userMetadataOperations'
import { KeyHoldersByLock } from '../graphql/datasource/keyholdersByLock'
import * as lockOperations from '../operations/lockOperations'
import * as metadataOperations from '../operations/metadataOperations'
Expand Down Expand Up @@ -156,6 +156,29 @@ namespace MetadataController {
}
}

export const readUserMetadata = async (
req: any,
res: Response
): Promise<any> => {
const userAddress = Normalizer.ethereumAddress(req.params.userAddress)
const tokenAddress = Normalizer.ethereumAddress(req.params.address)

const payload = JSON.parse(decodeURIComponent(req.query.data))
const signatureTime = payload.message.UserMetaData.timestamp

if (!expiredSignature(signatureTime) && req.signee === userAddress) {
const userMetaData = await getMetadata(
tokenAddress,
userAddress,
true /* includeProtected */
)

res.json(userMetaData)
} else {
res.sendStatus(401)
}
}

export const keyHolderMetadata = async (
req: any,
res: Response
Expand Down
15 changes: 15 additions & 0 deletions locksmith/src/routes/metadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ const userMetaDataConfiguration = {
signee: 'owner',
}

const readUserMetaDataConfiguration = {
name: 'UserMetaData',
required: ['owner', 'timestamp'],
signee: 'owner',
}

const lockOwnerMetaDataConfiguration = {
name: 'LockMetaData',
required: ['address', 'owner', 'timestamp'],
Expand All @@ -43,6 +49,14 @@ router.put(
signatureValidationMiddleware.generateProcessor(userMetaDataConfiguration)
)

// Reads the user centric metadata
router.get(
'/:address/user/:userAddress',
signatureValidationMiddleware.generateSignatureEvaluator(
readUserMetaDataConfiguration
)
)

router.get(
'/:address/:keyId',
signatureValidationMiddleware.generateSignatureEvaluator(
Expand All @@ -62,5 +76,6 @@ router.get('/:address/:keyId', MetadataController.data)
router.put('/:address/:keyId', MetadataController.updateKeyMetadata)
router.put('/:address', MetadataController.updateDefaults)
router.put('/:address/user/:userAddress', MetadataController.updateUserMetadata)
router.get('/:address/user/:userAddress', MetadataController.readUserMetadata)

module.exports = router

0 comments on commit 9411750

Please sign in to comment.