Skip to content

Commit

Permalink
Merge branch '164355301-user-should-go-liveness-test' of https://gith…
Browse files Browse the repository at this point in the history
…ub.com/GoodDollar/GoodServer into 164355301-user-should-go-liveness-test
  • Loading branch information
hadarbmdev committed May 8, 2019
2 parents b2fb3fa + dc8d98e commit 74cb3cd
Show file tree
Hide file tree
Showing 18 changed files with 252 additions and 42 deletions.
5 changes: 2 additions & 3 deletions .env.dev
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ PLIVO_PHONE_NUMBER=1555555555
OTP_DIGITS=6
OTP_TTL_MINUTES=5
WALLET_URL=http://localhost:3000
SENDGRID_API_KEY=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
SENDGRID_TEMPLATE_EMAIL_CONFIRMATION=d-275e32418857480e98ee71ef42f1b395
SENDGRID_TEMPLATE_RECOVERY_INSTRUCTIONS=d-d536a75acf4d47619f15fd3e1e4e0bd6
FACE_RECO_SERVER=https://good-face-reco.herokuapp.com
MAUTIC_URL=https://go.gooddollar.org
MAUTIC_TOKEN=""
5 changes: 4 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,7 @@ SENDGRID_TEMPLATE_RECOVERY_INSTRUCTIONS=
SENDGRID_TEMPLATE_EMAIL_CONFIRMATION=
WALLET_URL=
FACE_RECO_SERVER=https://good-face-reco.herokuapp.com

MAUTIC_URL=
##template id
MAUTIC_VERIFY_ID=
MAUTIC_RECOVERY_ID=
15 changes: 15 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 5 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@
"test:staging": "jest --forceExit --detectOpenHandles",
"coverage": "jest --forceExit --detectOpenHandles --coverage",
"coveralls": "cat ./coverage/lcov.info | node node_modules/.bin/coveralls",
"start": "webpack --mode development --config webpack.server.config.js --watch --verbose",
"dev": "npm start | pino-pretty -c -f -s \"msg!='request completed'\"",
"dev:local": "npm link @gooddollar/goodcontracts && npm start | pino-pretty -c -f -s \"msg!='request completed'\"",
"start": "node dist/server.js",
"start:dev": "webpack --mode development --config webpack.server.config.js --watch --verbose",
"dev": "npm run start:dev | pino-pretty -c -f -s \"msg!='request completed'\"",
"dev:local": "npm link @gooddollar/goodcontracts && npm run dev",
"flow": "flow"
},
"repository": {
Expand Down Expand Up @@ -76,6 +77,7 @@
"@babel/preset-flow": "^7.0.0",
"@babel/preset-react": "^7.0.0",
"axios": "^0.18.0",
"@types/jest": "^24.0.11",
"babel-core": "^7.0.0-bridge.0",
"babel-eslint": "^10.0.0",
"babel-jest": "^24.0.0",
Expand Down
1 change: 0 additions & 1 deletion src/imports/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ export interface StorageAPI {
addUser(user: UserRecord): Promise<boolean>;
updateUser(user: UserRecord): Promise<boolean>;
deleteUser(user: UserRecord): Promise<boolean>;
sanitizeUser(user: UserRecord): UserRecord;
}

export interface VerificationAPI {
Expand Down
5 changes: 5 additions & 0 deletions src/server/gun/__tests__/GunDB.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,9 @@ describe('GunDB', () => {
let msg = await SEA.verify(res.sig, storage.user.is.pub)
expect(msg).toBeTruthy()
})

it('should remove gundb soul from records', async () => {
let res = await storage.recordSanitize({ _: { '#': 'soul' } })
expect(res).toEqual({})
})
})
16 changes: 5 additions & 11 deletions src/server/gun/gun-middleware.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ const setup = (app: Router) => {
global.Gun = Gun // / make global to `node --inspect` - debug only
log.info('Done setup GunDB middleware.')
}

class GunDB implements StorageAPI {
gun: Gun

Expand All @@ -68,6 +69,10 @@ class GunDB implements StorageAPI {
})
}

/**
* remove the soul field(_) from gun records
* @param {*} gun record
*/
recordSanitize(obj: any = {}) {
if (obj._ !== undefined) {
const { _, ...record } = obj
Expand Down Expand Up @@ -160,17 +165,6 @@ class GunDB implements StorageAPI {
return true
}

sanitizeUser(user: UserRecord): UserRecord {
return {
identifier: user.identifier,
fullName: user.fullName,
mobile: user.mobile,
email: user.email,
smsValidated: user.smsValidated,
isEmailConfirmed: user.isEmailConfirmed
}
}

async signClaim(subjectPubKey: string, claimData: any): Claim {
let attestation: Claim = {
issuer: { '@did': 'did:gooddollar:' + this.user.is.pub, publicKey: this.user.is.pub },
Expand Down
43 changes: 43 additions & 0 deletions src/server/mautic/__tests__/mauticAPI.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/**
* @jest-environment node
*/

import { Mautic } from '../mauticAPI'

describe('Send', () => {
var mauticId = ''
it('should add new contact', async () => {
const res = await Mautic.createContact({ firstname: 'h', lastname: 'r', email: 'hadar@gooddollar.org' })
mauticId = res.contact.fields.all.id
expect(res.contact.fields.all).toEqual(
expect.objectContaining({
id: expect.any(Number),
email: 'hadar@gooddollar.org',
firstname: 'h',
lastname: 'r'
})
)
})

it('should send verifiction email', async () => {
const res = await Mautic.sendVerificationEmail(
{
fullName: 'hadar r',
mauticId
},
'https://gooddapp.com/?verification=2345'
)
expect(res).toEqual({ success: true })
})

it('should send recovery email', async () => {
const res = await Mautic.sendRecoveryEmail(
{
fullName: 'h r',
mauticId
},
'test seed phrase'
)
expect(res).toEqual({ success: true })
})
})
48 changes: 48 additions & 0 deletions src/server/mautic/mauticAPI.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// @flow
import fetch from 'cross-fetch'
import logger from '../../imports/pino-logger'

import { UserRecord } from '../../imports/types'
import Config from '../server.config'

const log = logger.child({ from: 'Mautic' })

export const Mautic = {
baseUrl: Config.mauticURL,
baseHeaders: {
Authorization: `Bearer ${Config.mauticToken}`,
'Content-Type': 'application/json'
},
baseQuery(url, headers, body) {
const fullUrl = `${this.baseUrl}${url}`
return fetch(fullUrl, { method: 'post', body: JSON.stringify(body), headers })
.then(async res => {
if (res.status !== 200) throw new Error(await res.text())
return res.json()
})
.catch(e => {
delete body['mnemonic'] //hide confidential information
log.error('Error:', url, e, { body })
throw e
})
},
createContact(user: UserRecord) {
return this.baseQuery('/contacts/new', this.baseHeaders, user)
},
sendVerificationEmail(user: UserRecord, link: string) {
if (!(link && user.fullName && user.mauticId && Config.mauticVerifyEmailId))
throw new Error('missing input for sending verification email')

return this.baseQuery(`/emails/${Config.mauticVerifyEmailId}/contact/${user.mauticId}/send`, this.baseHeaders, {
tokens: { link, firstName: user.fullName }
})
},
sendRecoveryEmail(user: UserRecord, mnemonic: string) {
if (!(mnemonic && user.fullName && user.mauticId && Config.mauticRecoveryEmailId))
throw new Error('missing input for sending recovery email')

return this.baseQuery(`/emails/${Config.mauticRecoveryEmailId}/contact/${user.mauticId}/send`, this.baseHeaders, {
tokens: { seed: mnemonic, firstName: user.fullName }
})
}
}
7 changes: 6 additions & 1 deletion src/server/send/__tests__/send.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@
* @jest-environment node
*/

import { sendEmailConfirmationLink, sendLinkByEmail, sendLinkBySMS, sendRecoveryInstructionsByEmail } from '../send'
import {
sendEmailConfirmationLink,
sendLinkByEmail,
sendLinkBySMS,
sendRecoveryInstructionsByEmail
} from '../send.sendgrid'

describe('Send', () => {
describe('Send Via Email', () => {
Expand Down
33 changes: 33 additions & 0 deletions src/server/send/__tests__/sendAPI.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import request from 'supertest'
import makeServer from '../../server-test'
import { getToken } from '../../__util__/'
import { GunDBPrivate } from '../../gun/gun-middleware'

describe('sendAPÏ', () => {
let server
Expand Down Expand Up @@ -28,4 +29,36 @@ describe('sendAPÏ', () => {
.set('Authorization', `Bearer ${token}`)
.expect(200, { ok: 1, onlyInEnv: { current: 'test', onlyIn: ['production', 'staging'] } }, done)
})

test('/verify/sendemail with creds', async () => {
const token = await getToken(server)
//make sure fullname is set for user which is required for sending the recovery email
const user = await GunDBPrivate.usersCol
.get('0x7ac080f6607405705aed79675789701a48c76f55')
.putAck({ fullName: 'full name', mauticId: 3461 })
const res = await request(server)
.post('/send/recoveryinstructions')
.send({
mnemonic: 'unit test send recovery instructions'
})
.set('Authorization', `Bearer ${token}`)
.expect(200, { ok: 1 })
})

test('/verify/sendemail without required fields should fail', async () => {
const token = await getToken(server)
//make sure mauticid is unset which is required
await GunDBPrivate.usersCol
.get('0x7ac080f6607405705aed79675789701a48c76f55')
.get('mauticId')
.putAck(null)
const user = await GunDBPrivate.usersCol.get('0x7ac080f6607405705aed79675789701a48c76f55').then()
const res = await request(server)
.post('/send/recoveryinstructions')
.send({
mnemonic: 'unit test send recovery instructions'
})
.set('Authorization', `Bearer ${token}`)
.expect(400)
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { generateOTP } from '../../imports/otp'

sgMail.setApiKey(conf.sendGrid.apiKey)

const log = logger.child({ from: 'AdminWallet' })
const log = logger.child({ from: 'send.js' })

export const sendLinkByEmail = (to: string, link: string) => {
const text = `You got GD. To withdraw open: ${link}`
Expand Down
14 changes: 8 additions & 6 deletions src/server/send/sendAPI.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
import { Router } from 'express'
import passport from 'passport'
import { wrapAsync, onlyInEnv } from '../utils/helpers'
import { sendLinkByEmail, sendLinkBySMS, sendRecoveryInstructionsByEmail } from './send'
import { sendLinkByEmail, sendLinkBySMS } from './send.sendgrid'
import { Mautic } from '../mautic/mauticAPI'

const setup = (app: Router) => {
app.post(
Expand Down Expand Up @@ -38,14 +39,15 @@ const setup = (app: Router) => {
app.post(
'/send/recoveryinstructions',
passport.authenticate('jwt', { session: false }),
onlyInEnv('production', 'staging'),
onlyInEnv('production', 'staging', 'test'),
wrapAsync(async (req, res, next) => {
const log = req.log.child({ from: 'sendAPI - /send/linkemail' })
const log = req.log.child({ from: 'sendAPI - /send/recoveryinstructions' })
const { user } = req
const { to, name, mnemonic } = req.body
const { mnemonic } = req.body

log.info('sending email', { to, name, mnemonic })
await sendRecoveryInstructionsByEmail(to, name, mnemonic)
log.info('sending recovery email', user)
//at this stage user record should contain all his details
await Mautic.sendRecoveryEmail(user, mnemonic)
res.json({ ok: 1 })
})
)
Expand Down
24 changes: 24 additions & 0 deletions src/server/server.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,30 @@ const conf = convict({
format: '*',
env: 'FACE_RECO_SERVER',
default: 'FACE_RECO_SERVER'
},
mauticURL: {
doc: 'mautic URL',
format: '*',
env: 'MAUTIC_URL',
default: 'WALLET_URL'
},
mauticToken: {
doc: 'mautic token',
format: '*',
env: 'MAUTIC_TOKEN',
default: 'MAUTIC_TOKEN'
},
mauticRecoveryEmailId: {
doc: 'id of email template',
format: '*',
env: 'MAUTIC_RECOVERY_ID',
default: '9'
},
mauticVerifyEmailId: {
doc: 'id of email template',
format: '*',
env: 'MAUTIC_VERIFY_ID',
default: '4'
}
})

Expand Down
6 changes: 4 additions & 2 deletions src/server/storage/__tests__/storageAPI.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,10 @@ describe('storageAPI', () => {

test('/user/add creds', async done => {
const token = await getToken(server)
console.log(token)
const user: UserRecord = { identifier: '0x7ac080f6607405705aed79675789701a48c76f55' }
const user: UserRecord = {
identifier: '0x7ac080f6607405705aed79675789701a48c76f55',
email: 'useraddtest@gooddollar.org' // required for mautic create contact
}
request(server)
.post('/user/add')
.set('Authorization', `Bearer ${token}`)
Expand Down
9 changes: 7 additions & 2 deletions src/server/storage/storageAPI.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,19 @@ import passport from 'passport'
import { type StorageAPI } from '../../imports/types'
import { wrapAsync } from '../utils/helpers'
import { defaults } from 'lodash'
import { UserRecord } from '../../imports/types'
import { Mautic } from '../mautic/mauticAPI'
import get from 'lodash/get'
const setup = (app: Router, storage: StorageAPI) => {
app.post(
'/user/add',
passport.authenticate('jwt', { session: false }),
wrapAsync(async (req, res, next) => {
const { body } = req
const user = defaults(body.user, { identifier: req.user.loggedInAs })
await storage.addUser(user)
const user: UserRecord = defaults(body.user, { identifier: req.user.loggedInAs })
//mautic contact should already exists since it is first created during the email verification we update it here
const mauticRecord = process.env.NODE_ENV === 'development' ? {} : await Mautic.createContact(user)
await storage.updateUser({ ...user, mauticId: get(mauticRecord, 'contact.fields.all.id', -1) })
res.json({ ok: 1 })
})
)
Expand Down

0 comments on commit 74cb3cd

Please sign in to comment.