feat: add button to check against OFAC sanction list

This commit is contained in:
Sérgio Salgado 2022-07-07 19:28:33 +01:00 committed by Rafael
parent a29f3fc13c
commit c77fda2623
11 changed files with 114 additions and 5 deletions

View file

@ -114,6 +114,7 @@ function update (id, data, userToken) {
async function updateCustomer (id, data, userToken) { async function updateCustomer (id, data, userToken) {
const formattedData = _.pick( const formattedData = _.pick(
[ [
'sanctions',
'authorized_override', 'authorized_override',
'id_card_photo_override', 'id_card_photo_override',
'id_card_data_override', 'id_card_data_override',

View file

@ -20,7 +20,7 @@ const { typeDefs, resolvers } = require('./graphql/schema')
const findOperatorId = require('../middlewares/operatorId') const findOperatorId = require('../middlewares/operatorId')
const computeSchema = require('../compute-schema') const computeSchema = require('../compute-schema')
const { USER_SESSIONS_CLEAR_INTERVAL } = require('../constants') const { USER_SESSIONS_CLEAR_INTERVAL } = require('../constants')
const { session, cleanUserSessions, buildApolloContext } = require('./middlewares') const { session, cleanUserSessions, buildApolloContext, loadSanctionLists } = require('./middlewares')
const devMode = require('minimist')(process.argv.slice(2)).dev const devMode = require('minimist')(process.argv.slice(2)).dev
@ -48,6 +48,7 @@ app.use(express.static(path.resolve(__dirname, '..', '..', 'public')))
app.use(cleanUserSessions(USER_SESSIONS_CLEAR_INTERVAL)) app.use(cleanUserSessions(USER_SESSIONS_CLEAR_INTERVAL))
app.use(computeSchema) app.use(computeSchema)
app.use(findOperatorId) app.use(findOperatorId)
app.use(loadSanctionLists)
app.use(session) app.use(session)
app.use(graphqlUploadExpress()) app.use(graphqlUploadExpress())

View file

@ -14,6 +14,7 @@ const machine = require('./machine.resolver')
const notification = require('./notification.resolver') const notification = require('./notification.resolver')
const pairing = require('./pairing.resolver') const pairing = require('./pairing.resolver')
const rates = require('./rates.resolver') const rates = require('./rates.resolver')
const sanctions = require('./sanctions.resolver')
const scalar = require('./scalar.resolver') const scalar = require('./scalar.resolver')
const settings = require('./settings.resolver') const settings = require('./settings.resolver')
const sms = require('./sms.resolver') const sms = require('./sms.resolver')
@ -37,6 +38,7 @@ const resolvers = [
notification, notification,
pairing, pairing,
rates, rates,
sanctions,
scalar, scalar,
settings, settings,
sms, sms,

View file

@ -0,0 +1,14 @@
const _ = require('lodash/fp')
const ofac = require('../../../ofac')
const resolvers = {
Query: {
checkAgainstSanctions: (...[, { firstName, lastName, birthdate }]) => {
const ofacMatches = ofac.match({ firstName, lastName }, birthdate, { threshold: 0.85, fullNameThreshold: 0.95, debug: false })
return { ofacSanctioned: _.size(ofacMatches) > 0 }
}
}
}
module.exports = resolvers

View file

@ -14,6 +14,7 @@ const machine = require('./machine.type')
const notification = require('./notification.type') const notification = require('./notification.type')
const pairing = require('./pairing.type') const pairing = require('./pairing.type')
const rates = require('./rates.type') const rates = require('./rates.type')
const sanctions = require('./sanctions.type')
const scalar = require('./scalar.type') const scalar = require('./scalar.type')
const settings = require('./settings.type') const settings = require('./settings.type')
const sms = require('./sms.type') const sms = require('./sms.type')
@ -37,6 +38,7 @@ const types = [
notification, notification,
pairing, pairing,
rates, rates,
sanctions,
scalar, scalar,
settings, settings,
sms, sms,

View file

@ -0,0 +1,13 @@
const { gql } = require('apollo-server-express')
const typeDef = gql`
type SanctionMatches {
ofacSanctioned: Boolean
}
type Query {
checkAgainstSanctions(firstName: String, lastName: String, birthdate: String): SanctionMatches @auth
}
`
module.exports = typeDef

View file

@ -1,9 +1,11 @@
const cleanUserSessions = require('./cleanUserSessions') const cleanUserSessions = require('./cleanUserSessions')
const buildApolloContext = require('./context') const buildApolloContext = require('./context')
const loadSanctionLists = require('./loadSanctionLists')
const session = require('./session') const session = require('./session')
module.exports = { module.exports = {
cleanUserSessions, cleanUserSessions,
buildApolloContext, buildApolloContext,
loadSanctionLists,
session session
} }

View file

@ -0,0 +1,28 @@
const logger = require('../../logger')
const sanctions = require('../../ofac')
const sanctionStatus = {
loaded: false,
timestamp: null
}
const loadSanctionLists = (req, res, next) => {
if (!sanctionStatus.loaded) {
logger.info('No sanction lists loaded. Loading sanctions...')
return sanctions.load()
.then(() => {
logger.info('OFAC sanction list loaded!')
sanctionStatus.loaded = true
sanctionStatus.timestamp = Date.now()
return next()
})
.catch(e => {
logger.error('Couldn\'t load OFAC sanction list!')
return next(e)
})
}
return next()
}
module.exports = loadSanctionLists

View file

@ -73,7 +73,8 @@ const CustomerData = ({
authorizeCustomRequest, authorizeCustomRequest,
updateCustomEntry, updateCustomEntry,
retrieveAdditionalDataDialog, retrieveAdditionalDataDialog,
setRetrieve setRetrieve,
checkAgainstSanctions
}) => { }) => {
const classes = useStyles() const classes = useStyles()
const [listView, setListView] = useState(false) const [listView, setListView] = useState(false)
@ -172,6 +173,14 @@ const CustomerData = ({
idCardData: R.merge(idData, formatDates(values)) idCardData: R.merge(idData, formatDates(values))
}), }),
validationSchema: customerDataSchemas.idCardData, validationSchema: customerDataSchemas.idCardData,
checkAgainstSanctions: () =>
checkAgainstSanctions({
variables: {
firstName: initialValues.idCardData.firstName,
lastName: initialValues.idCardData.lastName,
birthdate: R.replace(/-/g, '')(initialValues.idCardData.dateOfBirth)
}
}),
initialValues: initialValues.idCardData, initialValues: initialValues.idCardData,
isAvailable: !R.isNil(idData), isAvailable: !R.isNil(idData),
editable: true editable: true
@ -434,7 +443,8 @@ const CustomerData = ({
initialValues, initialValues,
hasImage, hasImage,
hasAdditionalData, hasAdditionalData,
editable editable,
checkAgainstSanctions
}, },
idx idx
) => { ) => {
@ -455,6 +465,7 @@ const CustomerData = ({
save={save} save={save}
deleteEditedData={deleteEditedData} deleteEditedData={deleteEditedData}
retrieveAdditionalData={retrieveAdditionalData} retrieveAdditionalData={retrieveAdditionalData}
checkAgainstSanctions={checkAgainstSanctions}
editable={editable}></EditableCard> editable={editable}></EditableCard>
) )
} }

View file

@ -1,4 +1,4 @@
import { useQuery, useMutation } from '@apollo/react-hooks' import { useQuery, useMutation, useLazyQuery } from '@apollo/react-hooks'
import { import {
makeStyles, makeStyles,
Breadcrumbs, Breadcrumbs,
@ -292,6 +292,22 @@ const GET_ACTIVE_CUSTOM_REQUESTS = gql`
} }
` `
const CHECK_AGAINST_SANCTIONS = gql`
query checkAgainstSanctions(
$firstName: String
$lastName: String
$birthdate: String
) {
checkAgainstSanctions(
firstName: $firstName
lastName: $lastName
birthdate: $birthdate
) {
ofacSanctioned
}
}
`
const CustomerProfile = memo(() => { const CustomerProfile = memo(() => {
const history = useHistory() const history = useHistory()
@ -400,6 +416,13 @@ const CustomerProfile = memo(() => {
onCompleted: () => getCustomer() onCompleted: () => getCustomer()
}) })
const [checkAgainstSanctions] = useLazyQuery(CHECK_AGAINST_SANCTIONS, {
onCompleted: ({ checkAgainstSanctions: { ofacSanctioned } }) =>
updateCustomer({
sanctions: !ofacSanctioned
})
})
const updateCustomer = it => const updateCustomer = it =>
setCustomer({ setCustomer({
variables: { variables: {
@ -662,6 +685,7 @@ const CustomerProfile = memo(() => {
authorizeCustomRequest={authorizeCustomRequest} authorizeCustomRequest={authorizeCustomRequest}
updateCustomEntry={updateCustomEntry} updateCustomEntry={updateCustomEntry}
setRetrieve={setRetrieve} setRetrieve={setRetrieve}
checkAgainstSanctions={checkAgainstSanctions}
retrieveAdditionalDataDialog={ retrieveAdditionalDataDialog={
<RetrieveDataDialog <RetrieveDataDialog
onDismissed={() => { onDismissed={() => {

View file

@ -145,7 +145,8 @@ const EditableCard = ({
deleteEditedData, deleteEditedData,
retrieveAdditionalData, retrieveAdditionalData,
hasAdditionalData = true, hasAdditionalData = true,
editable editable,
checkAgainstSanctions
}) => { }) => {
const classes = useStyles() const classes = useStyles()
@ -273,6 +274,16 @@ const EditableCard = ({
Retrieve API data Retrieve API data
</ActionButton> </ActionButton>
)} )}
{checkAgainstSanctions && (
<ActionButton
color="primary"
type="button"
Icon={DataIcon}
InverseIcon={DataReversedIcon}
onClick={() => checkAgainstSanctions()}>
Check against OFAC sanction list
</ActionButton>
)}
</div> </div>
{editable && ( {editable && (
<ActionButton <ActionButton