diff --git a/lib/customers.js b/lib/customers.js index d7975972..23542928 100644 --- a/lib/customers.js +++ b/lib/customers.js @@ -114,6 +114,7 @@ function update (id, data, userToken) { async function updateCustomer (id, data, userToken) { const formattedData = _.pick( [ + 'sanctions', 'authorized_override', 'id_card_photo_override', 'id_card_data_override', diff --git a/lib/new-admin/admin-server.js b/lib/new-admin/admin-server.js index 3b13daf8..e2a7ab70 100644 --- a/lib/new-admin/admin-server.js +++ b/lib/new-admin/admin-server.js @@ -20,7 +20,7 @@ const { typeDefs, resolvers } = require('./graphql/schema') const findOperatorId = require('../middlewares/operatorId') const computeSchema = require('../compute-schema') 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 @@ -48,6 +48,7 @@ app.use(express.static(path.resolve(__dirname, '..', '..', 'public'))) app.use(cleanUserSessions(USER_SESSIONS_CLEAR_INTERVAL)) app.use(computeSchema) app.use(findOperatorId) +app.use(loadSanctionLists) app.use(session) app.use(graphqlUploadExpress()) diff --git a/lib/new-admin/graphql/resolvers/index.js b/lib/new-admin/graphql/resolvers/index.js index 81e79614..a20d9216 100644 --- a/lib/new-admin/graphql/resolvers/index.js +++ b/lib/new-admin/graphql/resolvers/index.js @@ -14,6 +14,7 @@ const machine = require('./machine.resolver') const notification = require('./notification.resolver') const pairing = require('./pairing.resolver') const rates = require('./rates.resolver') +const sanctions = require('./sanctions.resolver') const scalar = require('./scalar.resolver') const settings = require('./settings.resolver') const sms = require('./sms.resolver') @@ -37,6 +38,7 @@ const resolvers = [ notification, pairing, rates, + sanctions, scalar, settings, sms, diff --git a/lib/new-admin/graphql/resolvers/sanctions.resolver.js b/lib/new-admin/graphql/resolvers/sanctions.resolver.js new file mode 100644 index 00000000..9bd211dd --- /dev/null +++ b/lib/new-admin/graphql/resolvers/sanctions.resolver.js @@ -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 diff --git a/lib/new-admin/graphql/types/index.js b/lib/new-admin/graphql/types/index.js index a1886a28..f4794b67 100644 --- a/lib/new-admin/graphql/types/index.js +++ b/lib/new-admin/graphql/types/index.js @@ -14,6 +14,7 @@ const machine = require('./machine.type') const notification = require('./notification.type') const pairing = require('./pairing.type') const rates = require('./rates.type') +const sanctions = require('./sanctions.type') const scalar = require('./scalar.type') const settings = require('./settings.type') const sms = require('./sms.type') @@ -37,6 +38,7 @@ const types = [ notification, pairing, rates, + sanctions, scalar, settings, sms, diff --git a/lib/new-admin/graphql/types/sanctions.type.js b/lib/new-admin/graphql/types/sanctions.type.js new file mode 100644 index 00000000..76a5e572 --- /dev/null +++ b/lib/new-admin/graphql/types/sanctions.type.js @@ -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 diff --git a/lib/new-admin/middlewares/index.js b/lib/new-admin/middlewares/index.js index cedba31c..21cd4751 100644 --- a/lib/new-admin/middlewares/index.js +++ b/lib/new-admin/middlewares/index.js @@ -1,9 +1,11 @@ const cleanUserSessions = require('./cleanUserSessions') const buildApolloContext = require('./context') +const loadSanctionLists = require('./loadSanctionLists') const session = require('./session') module.exports = { cleanUserSessions, buildApolloContext, + loadSanctionLists, session } diff --git a/lib/new-admin/middlewares/loadSanctionLists.js b/lib/new-admin/middlewares/loadSanctionLists.js new file mode 100644 index 00000000..48d2c0b8 --- /dev/null +++ b/lib/new-admin/middlewares/loadSanctionLists.js @@ -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 diff --git a/new-lamassu-admin/src/pages/Customers/CustomerData.js b/new-lamassu-admin/src/pages/Customers/CustomerData.js index b605eb07..affe5159 100644 --- a/new-lamassu-admin/src/pages/Customers/CustomerData.js +++ b/new-lamassu-admin/src/pages/Customers/CustomerData.js @@ -73,7 +73,8 @@ const CustomerData = ({ authorizeCustomRequest, updateCustomEntry, retrieveAdditionalDataDialog, - setRetrieve + setRetrieve, + checkAgainstSanctions }) => { const classes = useStyles() const [listView, setListView] = useState(false) @@ -172,6 +173,14 @@ const CustomerData = ({ idCardData: R.merge(idData, formatDates(values)) }), validationSchema: customerDataSchemas.idCardData, + checkAgainstSanctions: () => + checkAgainstSanctions({ + variables: { + firstName: initialValues.idCardData.firstName, + lastName: initialValues.idCardData.lastName, + birthdate: R.replace(/-/g, '')(initialValues.idCardData.dateOfBirth) + } + }), initialValues: initialValues.idCardData, isAvailable: !R.isNil(idData), editable: true @@ -434,7 +443,8 @@ const CustomerData = ({ initialValues, hasImage, hasAdditionalData, - editable + editable, + checkAgainstSanctions }, idx ) => { @@ -455,6 +465,7 @@ const CustomerData = ({ save={save} deleteEditedData={deleteEditedData} retrieveAdditionalData={retrieveAdditionalData} + checkAgainstSanctions={checkAgainstSanctions} editable={editable}> ) } diff --git a/new-lamassu-admin/src/pages/Customers/CustomerProfile.js b/new-lamassu-admin/src/pages/Customers/CustomerProfile.js index d9256f3b..3c03cb4f 100644 --- a/new-lamassu-admin/src/pages/Customers/CustomerProfile.js +++ b/new-lamassu-admin/src/pages/Customers/CustomerProfile.js @@ -1,4 +1,4 @@ -import { useQuery, useMutation } from '@apollo/react-hooks' +import { useQuery, useMutation, useLazyQuery } from '@apollo/react-hooks' import { makeStyles, 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 history = useHistory() @@ -400,6 +416,13 @@ const CustomerProfile = memo(() => { onCompleted: () => getCustomer() }) + const [checkAgainstSanctions] = useLazyQuery(CHECK_AGAINST_SANCTIONS, { + onCompleted: ({ checkAgainstSanctions: { ofacSanctioned } }) => + updateCustomer({ + sanctions: !ofacSanctioned + }) + }) + const updateCustomer = it => setCustomer({ variables: { @@ -662,6 +685,7 @@ const CustomerProfile = memo(() => { authorizeCustomRequest={authorizeCustomRequest} updateCustomEntry={updateCustomEntry} setRetrieve={setRetrieve} + checkAgainstSanctions={checkAgainstSanctions} retrieveAdditionalDataDialog={ { diff --git a/new-lamassu-admin/src/pages/Customers/components/EditableCard.js b/new-lamassu-admin/src/pages/Customers/components/EditableCard.js index 1dc71165..ce5c85bd 100644 --- a/new-lamassu-admin/src/pages/Customers/components/EditableCard.js +++ b/new-lamassu-admin/src/pages/Customers/components/EditableCard.js @@ -145,7 +145,8 @@ const EditableCard = ({ deleteEditedData, retrieveAdditionalData, hasAdditionalData = true, - editable + editable, + checkAgainstSanctions }) => { const classes = useStyles() @@ -273,6 +274,16 @@ const EditableCard = ({ Retrieve API data )} + {checkAgainstSanctions && ( + checkAgainstSanctions()}> + Check against OFAC sanction list + + )} {editable && (