From 3732b06b72e54baf78163ed50911a34cd6c9db4e Mon Sep 17 00:00:00 2001 From: Chris <34682781+monkins1010@users.noreply.github.com> Date: Thu, 15 Feb 2024 16:10:11 +0000 Subject: [PATCH 01/10] Add attestation --- package.json | 5 +- src/components/Attestation.js | 94 +++++++++++++ .../LoginRequestComplete.js | 54 ++++++-- .../LoginRequestIdentity.js | 43 +++++- .../LoginRequestInfo/LoginRequestInfo.js | 123 +++++++++++++++++- src/utils/deeplink/handleRedirect.js | 6 + 6 files changed, 312 insertions(+), 13 deletions(-) create mode 100644 src/components/Attestation.js diff --git a/package.json b/package.json index 99b2bbd6..c8178fc9 100755 --- a/package.json +++ b/package.json @@ -223,7 +223,10 @@ "@babel/core": "7.21.3", "blake2b-wasm": "https://github.com/michaeltout/blake2b-wasm/", "@bitgo/blake2b-wasm": "https://github.com/michaeltout/blake2b-wasm/", - "browserify-sign": "4.2.2" + "browserify-sign": "4.2.2", + "verus-typescript-primitives": "file:d:/dev/verus-typescript-primitives", + "verusid-ts-client": "file:d:/dev/verusid-ts-client", + "verusd-rpc-ts-client": "file:d:/dev/verusd-rpc-ts-client" }, "jest": { "preset": "react-native", diff --git a/src/components/Attestation.js b/src/components/Attestation.js new file mode 100644 index 00000000..05226877 --- /dev/null +++ b/src/components/Attestation.js @@ -0,0 +1,94 @@ +import React, {useEffect, useState} from 'react'; +import { SafeAreaView, ScrollView, View, Dimensions } from 'react-native'; +import { Button, Dialog, Portal, Text, List} from 'react-native-paper'; +import { connect } from 'react-redux'; +import Colors from '../globals/colors'; +import { getIdentity } from '../utils/api/channels/verusid/callCreators'; +import { primitives } from 'verusid-ts-client'; +import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons'; + +const getIdentities = async (identities) => { + + const identity = await Promise.all(identities.map(async (iaddress) => {return await getIdentity(null , iaddress)})); + console.log(identity); + return identity; + +} + +const AttestationModal = (props) => { + + const {visible, loginConsentResponse, attestation, viewOnly, buttons = [], mainTitle } = props; + const [signers, setSigners] = useState([]); + const signatures = Object.keys(attestation.signatures); + let attestationValues = {}; + + for (let [key, value] of attestation.components) { + if (primitives.ATTESTATION_IDENTITY_DATA[value.attestationKey]) { + attestationValues[primitives.ATTESTATION_IDENTITY_DATA[value.attestationKey].detail] = value.value; + } + } + + useEffect(() => { + getIdentities(signatures).then((identities) => setSigners(identities)); + + }, []); + + const title = attestationValues["Document Type"] || '' ; + + const { height } = Dimensions.get('window'); + const dialogContentMaxHeight = height * 0.6; // Adjust this value as needed + + return ( + + + {}} + style={{ maxHeight: '100%', marginBottom: 36 }} + > + + + + {mainTitle} + + + + + {`Issuer: ${signers.map((id) => id.fullyqualifiedname ).join("\n")}`} + Type: {title} + + {Object.entries(attestationValues).map(([key, value]) => ( + + ))} + + + + + {buttons != null + ? buttons.map((button, index) => ( + + )) + : null} + + + + + ); + +}; + +const mapStateToProps = (state) => { + return { + activeAlert: state.alert.active, + }; +}; + +export default connect(mapStateToProps)(AttestationModal); \ No newline at end of file diff --git a/src/containers/DeepLink/LoginRequestComplete/LoginRequestComplete.js b/src/containers/DeepLink/LoginRequestComplete/LoginRequestComplete.js index fe5a2feb..24b47acd 100644 --- a/src/containers/DeepLink/LoginRequestComplete/LoginRequestComplete.js +++ b/src/containers/DeepLink/LoginRequestComplete/LoginRequestComplete.js @@ -1,4 +1,4 @@ -import React, {useState} from 'react'; +import React, {useState, useEffect} from 'react'; import {ScrollView, View, TouchableOpacity} from 'react-native'; import Styles from '../../../styles/index'; import {primitives} from 'verusid-ts-client'; @@ -14,6 +14,8 @@ import AnimatedActivityIndicator from '../../../components/AnimatedActivityIndic import { useDispatch, useSelector } from 'react-redux'; import { resetDeeplinkData } from '../../../actions/actionCreators'; +import { useSelector } from 'react-redux'; +import Attestation from '../../../components/Attestation'; const LoginRequestComplete = props => { const {signedResponse} = props.route.params; @@ -28,6 +30,8 @@ const LoginRequestComplete = props => { let redirectinfo = null; const [loading, setLoading] = useState(false); const dispatch = useDispatch(); + const [showAttestation, setShowAttestation] = useState(false); + const [attestationObj, setAttestationObj] = useState(null); const cancel = () => { dispatch(resetDeeplinkData()); @@ -39,9 +43,36 @@ const LoginRequestComplete = props => { ); }; + const storeAttestation = () => { + + } + let redirectsObj = {}; redirects.forEach(a => {redirectsObj[a.vdxfkey] = a;}); + const attestationPresent = !!redirectsObj[primitives.LOGIN_CONSENT_ATTESTATION_WEBHOOK_VDXF_KEY.vdxfid]; + + useEffect(() => { + if (attestationPresent) { + try { + setLoading(true); + handleRedirect(signedResponse, + redirectsObj[primitives.LOGIN_CONSENT_ATTESTATION_WEBHOOK_VDXF_KEY.vdxfid]).then((attestation) => { + if (attestation && attestation.data) { + const localAttestaionObj = new primitives.Attestation(); + localAttestaionObj.fromBuffer(Buffer.from(attestation.data, 'hex')); + setAttestationObj(localAttestaionObj); + setShowAttestation(true); + } + setLoading(false); + }); + } catch(e) { + createAlert('Error', e.message); + setLoading(false); + cancel(); + } + } + }, []); if (redirectsObj[primitives.LOGIN_CONSENT_REDIRECT_VDXF_KEY.vdxfid]) { try { @@ -57,7 +88,6 @@ const LoginRequestComplete = props => { redirectinfo = redirectsObj[primitives.LOGIN_CONSENT_WEBHOOK_VDXF_KEY.vdxfid]; } - const tryRedirect = async () => { try { setLoading(true); @@ -85,11 +115,19 @@ const LoginRequestComplete = props => { return ( + style={{...Styles.fullWidth, ...Styles.backgroundColorWhite}} + contentContainerStyle={{ + ...Styles.focalCenter, + justifyContent: 'space-between', + }}> + {attestationObj && setShowAttestation(false)}, + {disabled: false, onPress: () => {storeAttestation(); setShowAttestation(false);}, text: "save"},]} + mainTitle={"Attestation Received"} + />} { fontSize: 20, color: Colors.verusDarkGray, }}> - {loading ? "Loading..." : 'Success!'} + {loading ? attestationPresent ? "Retrieving Attestation" : "Loading..." : 'Success!'} {loading ? ( diff --git a/src/containers/DeepLink/LoginRequestIdentity/LoginRequestIdentity.js b/src/containers/DeepLink/LoginRequestIdentity/LoginRequestIdentity.js index 1ba32c0a..20a2e7cd 100644 --- a/src/containers/DeepLink/LoginRequestIdentity/LoginRequestIdentity.js +++ b/src/containers/DeepLink/LoginRequestIdentity/LoginRequestIdentity.js @@ -27,6 +27,7 @@ const LoginRequestIdentity = props => { const [sortedIds, setSortedIds] = useState({}); const [idProvisionSuccess, setIdProvisionSuccess] = useState(false) const [canProvision, setCanProvision] = useState(false) + const [attestationID, setattestationID] = useState('') const req = new primitives.LoginConsentRequest(deeplinkData) const encryptedIds = useSelector(state => state.services.stored[VERUSID_SERVICE_ID]) const sendModal = useSelector((state) => state.sendModal); @@ -97,9 +98,44 @@ const LoginRequestIdentity = props => { const data = {[SEND_MODAL_IDENTITY_TO_LINK_FIELD]: passthrough.fqnToAutoLink, noLogin: noLogin}; openLinkIdentityModal(CoinDirectory.findCoinObj(system_id, null, true), data); } - }, [passthrough]) + }, [extraParams]) - //TODO: add a check that checks to see if the ID is ready, and relates to the provider. + useEffect(() => { + + if (idsloaded && req.challenge.subject && req.challenge.subject.length > 0 && + req.challenge.subject.some(item => item.vdxfkey === primitives.ID_ADDRESS_VDXF_KEY.vdxfid)) { + + const providionedId = req.challenge.subject.find(item => item.vdxfkey === primitives.ID_ADDRESS_VDXF_KEY.vdxfid).data; + if(req.challenge.redirect_uris && req.challenge.redirect_uris + .some((uriKey) => uriKey.vdxfkey === primitives.LOGIN_CONSENT_ATTESTATION_WEBHOOK_VDXF_KEY.vdxfid)){ + // if an attestation is provided, set the attestationID so it only shows the attestation the ID is for. + setattestationID(providionedId) + } + + for (const chainId of activeCoinIds) { + if (linkedIds[chainId] && Object.keys(linkedIds[chainId]).includes(providionedId)) { + return; + } + } + getIdentity(system_id, providionedId).then((provisionedID) => { + getPotentialPrimaryAddresses().then((addresses) => { + if (provisionedID.result) { + for (const address of provisionedID.result.identity.primaryaddresses) { + if (addresses.includes(address)) { + dispatch({ + type: SET_DEEPLINK_DATA_EXTRAPARAMS, + payload: { + extraParams: { fqn: provisionedID.result.fullyqualifiedname } + }, + }); + return; + } + } + } + }) + }) + } + }, [idsloaded]) useEffect(() => { onEncryptedIdsUpdate() @@ -185,6 +221,9 @@ const LoginRequestIdentity = props => { {`Linked ${chainId} VerusIDs`} )} {sortedIds[chainId].map(iAddr => { + if(attestationID && attestationID !== iAddr) { + return null + } return ( diff --git a/src/containers/DeepLink/LoginRequestInfo/LoginRequestInfo.js b/src/containers/DeepLink/LoginRequestInfo/LoginRequestInfo.js index 1c808c4e..407cf4f1 100644 --- a/src/containers/DeepLink/LoginRequestInfo/LoginRequestInfo.js +++ b/src/containers/DeepLink/LoginRequestInfo/LoginRequestInfo.js @@ -1,5 +1,5 @@ -import React, {useState, useEffect} from 'react'; -import {SafeAreaView, ScrollView, TouchableOpacity, View} from 'react-native'; +import React, { useState, useEffect } from 'react'; +import { Alert, SafeAreaView, ScrollView, TouchableOpacity, View } from 'react-native'; import Styles from '../../../styles/index'; import { primitives } from "verusid-ts-client" import { Button, Divider, List, Portal, Text } from 'react-native-paper'; @@ -17,6 +17,9 @@ import { createAlert, resolveAlert } from '../../../actions/actions/alert/dispat import { CoinDirectory } from '../../../utils/CoinData/CoinDirectory'; import { addCoin, addKeypairs, setUserCoins } from '../../../actions/actionCreators'; import { refreshActiveChainLifecycles } from '../../../actions/actions/intervals/dispatchers/lifecycleManager'; +import { requestServiceStoredData } from '../../../utils/auth/authBox'; +import { VERUSID_SERVICE_ID } from '../../../utils/constants/services'; +import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons'; const LoginRequestInfo = props => { const { deeplinkData, sigtime, cancel, signerFqn } = props @@ -112,6 +115,103 @@ const LoginRequestInfo = props => { setReq(new primitives.LoginConsentRequest(deeplinkData)) }, [deeplinkData]); + const buildAlert = (request) => { + return createAlert( + request.title, + request.data, + [ + { + text: 'DECLINE', + onPress: () => resolveAlert(false), + style: 'cancel', + }, + { + text: 'ACCEPT', onPress: () => { + + var _permissions = []; + for (let i = 0; i < permissions.length; i++) { + _permissions.push(permissions[i]); + if (_permissions[i].vdxfkey == request.vdxfkey) { + _permissions[i].agreed = true; + } + } + setExtraPermissions(_permissions); + + resolveAlert(true) + } + }, + ], + { + cancelable: true, + }, + ) + } + + + useEffect(() => { + + if (req && req.challenge && req.challenge.requested_access) { + var tempMethod = 0; + var attestationProvided = -1; + + if (req.challenge.redirect_uris.length > 0) { + attestationProvided = req.challenge.redirect_uris.map((data) => data.vdxfkey).indexOf(primitives.LOGIN_CONSENT_ATTESTATION_WEBHOOK_VDXF_KEY.vdxfid) + } + + var tempdata = {}; + if ((req.challenge.requested_access.length > 1) || (attestationProvided > -1) && !permissions) { + var loginTemp = []; + for (let i = 1; i < req.challenge.requested_access.length; i++) { + if (req.challenge.requested_access[i].vdxfkey === primitives.IDENTITY_AGREEMENT.vdxfid) { + tempMethod = tempMethod | 1; + tempdata = { data: req.challenge.requested_access[i].toJson().data, title: "Agreement to accept" } + } + // TODO: Add support for viewing identity data + loginTemp.push({ vdxfkey: req.challenge.requested_access[i].vdxfkey, ...tempdata, agreed: false }) + } + if (attestationProvided > -1) { + tempMethod = tempMethod | 4; + if (!req.challenge.subject.some((subject) => subject.vdxfkey === primitives.ID_ADDRESS_VDXF_KEY.vdxfid)) { + throw new Error("Attestation requested without ID Specified"); + } + const attestationId = req.challenge.subject.find((subject) => subject.vdxfkey === primitives.ID_ADDRESS_VDXF_KEY.vdxfid).data; + + requestServiceStoredData(VERUSID_SERVICE_ID).then((verusIdServiceData) => { + + if (verusIdServiceData.linked_ids) + + for (const chainId of Object.keys(verusIdServiceData.linked_ids)) { + if (verusIdServiceData.linked_ids[chainId] && + Object.keys(verusIdServiceData.linked_ids[chainId]) + .includes(attestationId)) { + loginTemp.push({ vdxfkey: primitives.LOGIN_CONSENT_ATTESTATION_WEBHOOK_VDXF_KEY.vdxfid, + title: verusIdServiceData.linked_ids[chainId][attestationId], + data: "Contains an Attestation for", + agreed: true, + nonChecked: true }) + } + } + setExtraPermissions(loginTemp); + }) + } + if (tempMethod > 5) Alert.alert("Error", "Invalid login method"); + } else if (req.challenge.requested_access.length === 1) { + setReady(true); + } + setLoginMethod(tempMethod); + } + }, []); + + useEffect(() => { + if (permissions) { + for (let i = 0; i < permissions.length; i++) { + if (!permissions[i].agreed) + return; + } + setReady(true); + } + }, [permissions]); + useEffect(() => { setSigDateString(unixToDate(sigtime)) }, [sigtime]); @@ -294,6 +394,25 @@ const LoginRequestInfo = props => { + {permissions && permissions.map((request, index) => { + return ( + !!request.nonChecked ? null : buildAlert(request) }> + ( + !!!request.nonChecked && + )} + left= {() => } + /> + + + ); + })} { + return await axios.post( + uri, + response + ); + }, [primitives.LOGIN_CONSENT_REDIRECT_VDXF_KEY.vdxfid]: (uri, response) => { const url = new URL(uri) const res = new primitives.LoginConsentResponse(response) From 010e91f725d50b53865962b4d6f82f592404410d Mon Sep 17 00:00:00 2001 From: Chris <34682781+monkins1010@users.noreply.github.com> Date: Thu, 15 Feb 2024 16:10:18 +0000 Subject: [PATCH 02/10] Add more data --- package.json | 5 +- shim.js | 26 ++++ src/components/Attestation.js | 94 ------------- .../LoginRequestComplete.js | 54 ++------ .../LoginRequestIdentity.js | 43 +----- .../LoginRequestInfo/LoginRequestInfo.js | 123 +----------------- src/utils/deeplink/handleRedirect.js | 6 - 7 files changed, 39 insertions(+), 312 deletions(-) create mode 100644 shim.js delete mode 100644 src/components/Attestation.js diff --git a/package.json b/package.json index c8178fc9..99b2bbd6 100755 --- a/package.json +++ b/package.json @@ -223,10 +223,7 @@ "@babel/core": "7.21.3", "blake2b-wasm": "https://github.com/michaeltout/blake2b-wasm/", "@bitgo/blake2b-wasm": "https://github.com/michaeltout/blake2b-wasm/", - "browserify-sign": "4.2.2", - "verus-typescript-primitives": "file:d:/dev/verus-typescript-primitives", - "verusid-ts-client": "file:d:/dev/verusid-ts-client", - "verusd-rpc-ts-client": "file:d:/dev/verusd-rpc-ts-client" + "browserify-sign": "4.2.2" }, "jest": { "preset": "react-native", diff --git a/shim.js b/shim.js new file mode 100644 index 00000000..812d6b45 --- /dev/null +++ b/shim.js @@ -0,0 +1,26 @@ +if (typeof __dirname === 'undefined') global.__dirname = '/' +if (typeof __filename === 'undefined') global.__filename = '' +if (typeof process === 'undefined') { + global.process = require('process') +} else { + const bProcess = require('process') + for (var p in bProcess) { + if (!(p in process)) { + process[p] = bProcess[p] + } + } +} + +process.browser = false +if (typeof Buffer === 'undefined') global.Buffer = require('buffer').Buffer + +// global.location = global.location || { port: 80 } +const isDev = typeof __DEV__ === 'boolean' && __DEV__ +process.env['NODE_ENV'] = isDev ? 'development' : 'production' +if (typeof localStorage !== 'undefined') { + localStorage.debug = isDev ? '*' : '' +} + +// If using the crypto shim, uncomment the following line to ensure +// crypto is loaded first, so it can populate global.crypto +// require('crypto') diff --git a/src/components/Attestation.js b/src/components/Attestation.js deleted file mode 100644 index 05226877..00000000 --- a/src/components/Attestation.js +++ /dev/null @@ -1,94 +0,0 @@ -import React, {useEffect, useState} from 'react'; -import { SafeAreaView, ScrollView, View, Dimensions } from 'react-native'; -import { Button, Dialog, Portal, Text, List} from 'react-native-paper'; -import { connect } from 'react-redux'; -import Colors from '../globals/colors'; -import { getIdentity } from '../utils/api/channels/verusid/callCreators'; -import { primitives } from 'verusid-ts-client'; -import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons'; - -const getIdentities = async (identities) => { - - const identity = await Promise.all(identities.map(async (iaddress) => {return await getIdentity(null , iaddress)})); - console.log(identity); - return identity; - -} - -const AttestationModal = (props) => { - - const {visible, loginConsentResponse, attestation, viewOnly, buttons = [], mainTitle } = props; - const [signers, setSigners] = useState([]); - const signatures = Object.keys(attestation.signatures); - let attestationValues = {}; - - for (let [key, value] of attestation.components) { - if (primitives.ATTESTATION_IDENTITY_DATA[value.attestationKey]) { - attestationValues[primitives.ATTESTATION_IDENTITY_DATA[value.attestationKey].detail] = value.value; - } - } - - useEffect(() => { - getIdentities(signatures).then((identities) => setSigners(identities)); - - }, []); - - const title = attestationValues["Document Type"] || '' ; - - const { height } = Dimensions.get('window'); - const dialogContentMaxHeight = height * 0.6; // Adjust this value as needed - - return ( - - - {}} - style={{ maxHeight: '100%', marginBottom: 36 }} - > - - - - {mainTitle} - - - - - {`Issuer: ${signers.map((id) => id.fullyqualifiedname ).join("\n")}`} - Type: {title} - - {Object.entries(attestationValues).map(([key, value]) => ( - - ))} - - - - - {buttons != null - ? buttons.map((button, index) => ( - - )) - : null} - - - - - ); - -}; - -const mapStateToProps = (state) => { - return { - activeAlert: state.alert.active, - }; -}; - -export default connect(mapStateToProps)(AttestationModal); \ No newline at end of file diff --git a/src/containers/DeepLink/LoginRequestComplete/LoginRequestComplete.js b/src/containers/DeepLink/LoginRequestComplete/LoginRequestComplete.js index 24b47acd..fe5a2feb 100644 --- a/src/containers/DeepLink/LoginRequestComplete/LoginRequestComplete.js +++ b/src/containers/DeepLink/LoginRequestComplete/LoginRequestComplete.js @@ -1,4 +1,4 @@ -import React, {useState, useEffect} from 'react'; +import React, {useState} from 'react'; import {ScrollView, View, TouchableOpacity} from 'react-native'; import Styles from '../../../styles/index'; import {primitives} from 'verusid-ts-client'; @@ -14,8 +14,6 @@ import AnimatedActivityIndicator from '../../../components/AnimatedActivityIndic import { useDispatch, useSelector } from 'react-redux'; import { resetDeeplinkData } from '../../../actions/actionCreators'; -import { useSelector } from 'react-redux'; -import Attestation from '../../../components/Attestation'; const LoginRequestComplete = props => { const {signedResponse} = props.route.params; @@ -30,8 +28,6 @@ const LoginRequestComplete = props => { let redirectinfo = null; const [loading, setLoading] = useState(false); const dispatch = useDispatch(); - const [showAttestation, setShowAttestation] = useState(false); - const [attestationObj, setAttestationObj] = useState(null); const cancel = () => { dispatch(resetDeeplinkData()); @@ -43,36 +39,9 @@ const LoginRequestComplete = props => { ); }; - const storeAttestation = () => { - - } - let redirectsObj = {}; redirects.forEach(a => {redirectsObj[a.vdxfkey] = a;}); - const attestationPresent = !!redirectsObj[primitives.LOGIN_CONSENT_ATTESTATION_WEBHOOK_VDXF_KEY.vdxfid]; - - useEffect(() => { - if (attestationPresent) { - try { - setLoading(true); - handleRedirect(signedResponse, - redirectsObj[primitives.LOGIN_CONSENT_ATTESTATION_WEBHOOK_VDXF_KEY.vdxfid]).then((attestation) => { - if (attestation && attestation.data) { - const localAttestaionObj = new primitives.Attestation(); - localAttestaionObj.fromBuffer(Buffer.from(attestation.data, 'hex')); - setAttestationObj(localAttestaionObj); - setShowAttestation(true); - } - setLoading(false); - }); - } catch(e) { - createAlert('Error', e.message); - setLoading(false); - cancel(); - } - } - }, []); if (redirectsObj[primitives.LOGIN_CONSENT_REDIRECT_VDXF_KEY.vdxfid]) { try { @@ -88,6 +57,7 @@ const LoginRequestComplete = props => { redirectinfo = redirectsObj[primitives.LOGIN_CONSENT_WEBHOOK_VDXF_KEY.vdxfid]; } + const tryRedirect = async () => { try { setLoading(true); @@ -115,19 +85,11 @@ const LoginRequestComplete = props => { return ( - {attestationObj && setShowAttestation(false)}, - {disabled: false, onPress: () => {storeAttestation(); setShowAttestation(false);}, text: "save"},]} - mainTitle={"Attestation Received"} - />} + style={{...Styles.fullWidth, ...Styles.backgroundColorWhite}} + contentContainerStyle={{ + ...Styles.focalCenter, + justifyContent: 'space-between', + }}> { fontSize: 20, color: Colors.verusDarkGray, }}> - {loading ? attestationPresent ? "Retrieving Attestation" : "Loading..." : 'Success!'} + {loading ? "Loading..." : 'Success!'} {loading ? ( diff --git a/src/containers/DeepLink/LoginRequestIdentity/LoginRequestIdentity.js b/src/containers/DeepLink/LoginRequestIdentity/LoginRequestIdentity.js index 20a2e7cd..1ba32c0a 100644 --- a/src/containers/DeepLink/LoginRequestIdentity/LoginRequestIdentity.js +++ b/src/containers/DeepLink/LoginRequestIdentity/LoginRequestIdentity.js @@ -27,7 +27,6 @@ const LoginRequestIdentity = props => { const [sortedIds, setSortedIds] = useState({}); const [idProvisionSuccess, setIdProvisionSuccess] = useState(false) const [canProvision, setCanProvision] = useState(false) - const [attestationID, setattestationID] = useState('') const req = new primitives.LoginConsentRequest(deeplinkData) const encryptedIds = useSelector(state => state.services.stored[VERUSID_SERVICE_ID]) const sendModal = useSelector((state) => state.sendModal); @@ -98,44 +97,9 @@ const LoginRequestIdentity = props => { const data = {[SEND_MODAL_IDENTITY_TO_LINK_FIELD]: passthrough.fqnToAutoLink, noLogin: noLogin}; openLinkIdentityModal(CoinDirectory.findCoinObj(system_id, null, true), data); } - }, [extraParams]) + }, [passthrough]) - useEffect(() => { - - if (idsloaded && req.challenge.subject && req.challenge.subject.length > 0 && - req.challenge.subject.some(item => item.vdxfkey === primitives.ID_ADDRESS_VDXF_KEY.vdxfid)) { - - const providionedId = req.challenge.subject.find(item => item.vdxfkey === primitives.ID_ADDRESS_VDXF_KEY.vdxfid).data; - if(req.challenge.redirect_uris && req.challenge.redirect_uris - .some((uriKey) => uriKey.vdxfkey === primitives.LOGIN_CONSENT_ATTESTATION_WEBHOOK_VDXF_KEY.vdxfid)){ - // if an attestation is provided, set the attestationID so it only shows the attestation the ID is for. - setattestationID(providionedId) - } - - for (const chainId of activeCoinIds) { - if (linkedIds[chainId] && Object.keys(linkedIds[chainId]).includes(providionedId)) { - return; - } - } - getIdentity(system_id, providionedId).then((provisionedID) => { - getPotentialPrimaryAddresses().then((addresses) => { - if (provisionedID.result) { - for (const address of provisionedID.result.identity.primaryaddresses) { - if (addresses.includes(address)) { - dispatch({ - type: SET_DEEPLINK_DATA_EXTRAPARAMS, - payload: { - extraParams: { fqn: provisionedID.result.fullyqualifiedname } - }, - }); - return; - } - } - } - }) - }) - } - }, [idsloaded]) + //TODO: add a check that checks to see if the ID is ready, and relates to the provider. useEffect(() => { onEncryptedIdsUpdate() @@ -221,9 +185,6 @@ const LoginRequestIdentity = props => { {`Linked ${chainId} VerusIDs`} )} {sortedIds[chainId].map(iAddr => { - if(attestationID && attestationID !== iAddr) { - return null - } return ( diff --git a/src/containers/DeepLink/LoginRequestInfo/LoginRequestInfo.js b/src/containers/DeepLink/LoginRequestInfo/LoginRequestInfo.js index 407cf4f1..1c808c4e 100644 --- a/src/containers/DeepLink/LoginRequestInfo/LoginRequestInfo.js +++ b/src/containers/DeepLink/LoginRequestInfo/LoginRequestInfo.js @@ -1,5 +1,5 @@ -import React, { useState, useEffect } from 'react'; -import { Alert, SafeAreaView, ScrollView, TouchableOpacity, View } from 'react-native'; +import React, {useState, useEffect} from 'react'; +import {SafeAreaView, ScrollView, TouchableOpacity, View} from 'react-native'; import Styles from '../../../styles/index'; import { primitives } from "verusid-ts-client" import { Button, Divider, List, Portal, Text } from 'react-native-paper'; @@ -17,9 +17,6 @@ import { createAlert, resolveAlert } from '../../../actions/actions/alert/dispat import { CoinDirectory } from '../../../utils/CoinData/CoinDirectory'; import { addCoin, addKeypairs, setUserCoins } from '../../../actions/actionCreators'; import { refreshActiveChainLifecycles } from '../../../actions/actions/intervals/dispatchers/lifecycleManager'; -import { requestServiceStoredData } from '../../../utils/auth/authBox'; -import { VERUSID_SERVICE_ID } from '../../../utils/constants/services'; -import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons'; const LoginRequestInfo = props => { const { deeplinkData, sigtime, cancel, signerFqn } = props @@ -115,103 +112,6 @@ const LoginRequestInfo = props => { setReq(new primitives.LoginConsentRequest(deeplinkData)) }, [deeplinkData]); - const buildAlert = (request) => { - return createAlert( - request.title, - request.data, - [ - { - text: 'DECLINE', - onPress: () => resolveAlert(false), - style: 'cancel', - }, - { - text: 'ACCEPT', onPress: () => { - - var _permissions = []; - for (let i = 0; i < permissions.length; i++) { - _permissions.push(permissions[i]); - if (_permissions[i].vdxfkey == request.vdxfkey) { - _permissions[i].agreed = true; - } - } - setExtraPermissions(_permissions); - - resolveAlert(true) - } - }, - ], - { - cancelable: true, - }, - ) - } - - - useEffect(() => { - - if (req && req.challenge && req.challenge.requested_access) { - var tempMethod = 0; - var attestationProvided = -1; - - if (req.challenge.redirect_uris.length > 0) { - attestationProvided = req.challenge.redirect_uris.map((data) => data.vdxfkey).indexOf(primitives.LOGIN_CONSENT_ATTESTATION_WEBHOOK_VDXF_KEY.vdxfid) - } - - var tempdata = {}; - if ((req.challenge.requested_access.length > 1) || (attestationProvided > -1) && !permissions) { - var loginTemp = []; - for (let i = 1; i < req.challenge.requested_access.length; i++) { - if (req.challenge.requested_access[i].vdxfkey === primitives.IDENTITY_AGREEMENT.vdxfid) { - tempMethod = tempMethod | 1; - tempdata = { data: req.challenge.requested_access[i].toJson().data, title: "Agreement to accept" } - } - // TODO: Add support for viewing identity data - loginTemp.push({ vdxfkey: req.challenge.requested_access[i].vdxfkey, ...tempdata, agreed: false }) - } - if (attestationProvided > -1) { - tempMethod = tempMethod | 4; - if (!req.challenge.subject.some((subject) => subject.vdxfkey === primitives.ID_ADDRESS_VDXF_KEY.vdxfid)) { - throw new Error("Attestation requested without ID Specified"); - } - const attestationId = req.challenge.subject.find((subject) => subject.vdxfkey === primitives.ID_ADDRESS_VDXF_KEY.vdxfid).data; - - requestServiceStoredData(VERUSID_SERVICE_ID).then((verusIdServiceData) => { - - if (verusIdServiceData.linked_ids) - - for (const chainId of Object.keys(verusIdServiceData.linked_ids)) { - if (verusIdServiceData.linked_ids[chainId] && - Object.keys(verusIdServiceData.linked_ids[chainId]) - .includes(attestationId)) { - loginTemp.push({ vdxfkey: primitives.LOGIN_CONSENT_ATTESTATION_WEBHOOK_VDXF_KEY.vdxfid, - title: verusIdServiceData.linked_ids[chainId][attestationId], - data: "Contains an Attestation for", - agreed: true, - nonChecked: true }) - } - } - setExtraPermissions(loginTemp); - }) - } - if (tempMethod > 5) Alert.alert("Error", "Invalid login method"); - } else if (req.challenge.requested_access.length === 1) { - setReady(true); - } - setLoginMethod(tempMethod); - } - }, []); - - useEffect(() => { - if (permissions) { - for (let i = 0; i < permissions.length; i++) { - if (!permissions[i].agreed) - return; - } - setReady(true); - } - }, [permissions]); - useEffect(() => { setSigDateString(unixToDate(sigtime)) }, [sigtime]); @@ -394,25 +294,6 @@ const LoginRequestInfo = props => { - {permissions && permissions.map((request, index) => { - return ( - !!request.nonChecked ? null : buildAlert(request) }> - ( - !!!request.nonChecked && - )} - left= {() => } - /> - - - ); - })} { - return await axios.post( - uri, - response - ); - }, [primitives.LOGIN_CONSENT_REDIRECT_VDXF_KEY.vdxfid]: (uri, response) => { const url = new URL(uri) const res = new primitives.LoginConsentResponse(response) From fcb15f13d1f843070c4483915cd99ef343fb4509 Mon Sep 17 00:00:00 2001 From: Chris <34682781+monkins1010@users.noreply.github.com> Date: Thu, 7 Mar 2024 07:40:05 +0000 Subject: [PATCH 03/10] Updates for attestations --- .gitignore | 18 ++++ package.json | 3 +- src/containers/DeepLink/DeepLink.js | 2 +- .../LoginRequestInfo/LoginRequestInfo.js | 97 ++++++++++++++++++- .../PersonalSelectData/PersonalSelectData.js | 88 +++++++++++++++++ .../PersonalSelectData.render.js | 19 ++++ .../DeepLinkStackScreens.js | 10 +- src/utils/nativeStore/personalDataStorage.js | 67 +++++++++++++ yarn.lock | 32 +++++- 9 files changed, 327 insertions(+), 9 deletions(-) create mode 100644 src/containers/DeepLink/PersonalSelectData/PersonalSelectData.js create mode 100644 src/containers/DeepLink/PersonalSelectData/PersonalSelectData.render.js create mode 100644 src/utils/nativeStore/personalDataStorage.js diff --git a/.gitignore b/.gitignore index 89f70fad..758474b2 100644 --- a/.gitignore +++ b/.gitignore @@ -112,3 +112,21 @@ android/app/src/main/res/mipmap-xxhdpi/ic_launcher_monochrome.png android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_background.png android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_monochrome.png +package.json +yarn.lock +android/app/src/main/assets/index.android.bundle +android/app/src/main/res/drawable-hdpi/node_modules_reactnavigation_drawer_src_views_assets_toggledrawericon.png +android/app/src/main/res/drawable-hdpi/node_modules_reactnavigation_elements_src_assets_backicon.png +android/app/src/main/res/drawable-mdpi/node_modules_reactnative_libraries_newappscreen_components_logo.png +android/app/src/main/res/drawable-mdpi/node_modules_reactnavigation_drawer_src_views_assets_toggledrawericon.png +android/app/src/main/res/drawable-mdpi/node_modules_reactnavigation_elements_src_assets_backicon.png +android/app/src/main/res/drawable-mdpi/node_modules_reactnavigation_elements_src_assets_backiconmask.png +android/app/src/main/res/drawable-mdpi/src_images_customicons_coinsicon.png +android/app/src/main/res/drawable-mdpi/src_images_customicons_recovericon.png +android/app/src/main/res/drawable-mdpi/src_images_customicons_revokeicon.png +android/app/src/main/res/drawable-xhdpi/node_modules_reactnavigation_drawer_src_views_assets_toggledrawericon.png +android/app/src/main/res/drawable-xhdpi/node_modules_reactnavigation_elements_src_assets_backicon.png +android/app/src/main/res/drawable-xxhdpi/node_modules_reactnavigation_drawer_src_views_assets_toggledrawericon.png +android/app/src/main/res/drawable-xxhdpi/node_modules_reactnavigation_elements_src_assets_backicon.png +android/app/src/main/res/drawable-xxxhdpi/node_modules_reactnavigation_drawer_src_views_assets_toggledrawericon.png +android/app/src/main/res/drawable-xxxhdpi/node_modules_reactnavigation_elements_src_assets_backicon.png diff --git a/package.json b/package.json index 99b2bbd6..376693c6 100755 --- a/package.json +++ b/package.json @@ -118,7 +118,7 @@ "uuid": "8.0.0", "version_compare": "0.0.3", "verus-pay-utils": "git+https://github.com/michaeltout/verus-pay-utils.git", - "verus-typescript-primitives": "https://github.com/VerusCoin/verus-typescript-primitives.git", + "verus-typescript-primitives": "file:d:/dev/verus-typescript-primitives", "verus-zkedid-utils": "git+https://github.com/michaeltout/verus-zkedid-utils.git", "verusd-rpc-ts-client": "git+https://github.com/VerusCoin/verusd-rpc-ts-client.git", "verusid-ts-client": "git+https://github.com/VerusCoin/verusid-ts-client.git", @@ -223,6 +223,7 @@ "@babel/core": "7.21.3", "blake2b-wasm": "https://github.com/michaeltout/blake2b-wasm/", "@bitgo/blake2b-wasm": "https://github.com/michaeltout/blake2b-wasm/", + "verus-typescript-primitives": "file:d:/dev/verus-typescript-primitives", "browserify-sign": "4.2.2" }, "jest": { diff --git a/src/containers/DeepLink/DeepLink.js b/src/containers/DeepLink/DeepLink.js index d6f310c1..c6dca947 100644 --- a/src/containers/DeepLink/DeepLink.js +++ b/src/containers/DeepLink/DeepLink.js @@ -21,7 +21,7 @@ import { getCurrency, getIdentity } from '../../utils/api/channels/verusid/callC import { convertFqnToDisplayFormat } from '../../utils/fullyqualifiedname'; import { resetDeeplinkData } from '../../actions/actionCreators'; -const authorizedPermissions = [primitives.IDENTITY_VIEW.vdxfid,/*TODO: primitives.IDENTITY_AGREEMENT.vdxfid, primitives.ATTESTATION_READ_REQUEST.vdxfid */] +const authorizedPermissions = [primitives.IDENTITY_VIEW.vdxfid, primitives.IDENTITY_AGREEMENT.vdxfid, primitives.ATTESTATION_READ_REQUEST.vdxfid, primitives.PROFILE_DATA_VIEW_REQUEST.vdxfid] import { CoinDirectory } from '../../utils/CoinData/CoinDirectory'; import BigNumber from 'bignumber.js'; import { blocksToTime, satsToCoins } from '../../utils/math'; diff --git a/src/containers/DeepLink/LoginRequestInfo/LoginRequestInfo.js b/src/containers/DeepLink/LoginRequestInfo/LoginRequestInfo.js index 1c808c4e..ac5ab14f 100644 --- a/src/containers/DeepLink/LoginRequestInfo/LoginRequestInfo.js +++ b/src/containers/DeepLink/LoginRequestInfo/LoginRequestInfo.js @@ -29,6 +29,8 @@ const LoginRequestInfo = props => { const signedIn = useSelector(state => state.authentication.signedIn) const passthrough = useSelector((state) => state.deeplink.passthrough); const sendModalType = useSelector(state => state.sendModal.type) + const [permissions, setExtraPermissions] = useState(null); + const [ready, setReady] = useState(false); const dispatch = useDispatch() @@ -121,6 +123,74 @@ const LoginRequestInfo = props => { setLoading(false) } else setLoading(true) }, [sendModalType]); + + const buildAlert = (request) => { + return createAlert( + request.title, + request.data, + [ + { + text: 'DECLINE', + onPress: () => resolveAlert(false), + style: 'cancel', + }, + { + text: 'ACCEPT', onPress: () => { + const _permissions = permissions.map(permission => { + if (permission.vdxfkey === request.vdxfkey) { + return { ...permission, agreed: true }; + } + return permission; + }); + setExtraPermissions(_permissions); + resolveAlert(true) + props.navigation.navigate("PersonalSelectData"); + + } + }, + ], + {cancelable: true}); + } + + + useEffect(() => { + + if (req && req.challenge && req.challenge.requested_access) { + if (req.challenge.requested_access.length === 1 && req.challenge.requested_access.some(primitives.IDENTITY_VIEW.vdxfid)) { + setReady(true); + } else { + var loginTemp = []; + for (let i = 0; i < req.challenge.requested_access.length; i++) { + var tempdata = {}; + + if (req.challenge.requested_access[i].vdxfkey === primitives.IDENTITY_VIEW.vdxfid) { + continue; + } else if (req.challenge.requested_access[i].vdxfkey === primitives.IDENTITY_AGREEMENT.vdxfid) { + tempdata = { data: req.challenge.requested_access[i].toJson().data, title: "Agreement to accept" } + } else if (req.challenge.requested_access[i].vdxfkey === primitives.ATTESTATION_READ_REQUEST.vdxfid) { + tempdata = { data: "Agree to share attestation data", title: "Attestation View Request" } + } else if (req.challenge.requested_access[i].vdxfkey === primitives.PROFILE_DATA_VIEW_REQUEST.vdxfid) { + tempdata = { data: "Agree to share profile data", title: "Personal Data Input Request" } + + } + loginTemp.push({ vdxfkey: req.challenge.requested_access[i].vdxfkey, ...tempdata, agreed: false }) + } + setExtraPermissions(loginTemp); + } + } + }, [req]); + + useEffect(() => { + if (permissions) { + for (let i = 0; i < permissions.length; i++) { + if (!permissions[i].agreed) + return; + } + setReady(true); + } + }, [permissions]); + + const addRootSystem = async () => { setLoading(true) @@ -189,8 +259,14 @@ const LoginRequestInfo = props => { } } - const handleContinue = () => { + const handleContinue = async () => { if (signedIn) { + if (!ready) { + for (let i = 0; i < permissions.length; i++) { + const result = await buildAlert(permissions[i], i); + if (!result) return; + } + } const coinObj = CoinDirectory.findCoinObj(chain_id); if (!!coinObj.testnet != isTestnet) { createAlert( @@ -294,6 +370,23 @@ const LoginRequestInfo = props => { + {permissions && permissions.map((request, index) => { + return ( + buildAlert(request)}> + ( + + )} /> + + + ); + })} { Cancel + + ); }; diff --git a/src/containers/RootStack/DeepLinkStackScreens/DeepLinkStackScreens.js b/src/containers/RootStack/DeepLinkStackScreens/DeepLinkStackScreens.js index 7c31ebdb..623ee7da 100644 --- a/src/containers/RootStack/DeepLinkStackScreens/DeepLinkStackScreens.js +++ b/src/containers/RootStack/DeepLinkStackScreens/DeepLinkStackScreens.js @@ -6,6 +6,7 @@ import LoginRequestIdentity from '../../DeepLink/LoginRequestIdentity/LoginReque import LoginRequestComplete from '../../DeepLink/LoginRequestComplete/LoginRequestComplete'; import InvoicePaymentConfiguration from '../../DeepLink/InvoicePaymentConfiguration/InvoicePaymentConfiguration'; import PersonalSelectData from '../../DeepLink/PersonalSelectData/PersonalSelectData'; +import ProfileStackScreens from '../ProfileStackScreens/ProfileStackScreens'; const DeepLinkStack = createStackNavigator(); const DeepLinkStackScreens = props => { @@ -51,6 +52,11 @@ const DeepLinkStackScreens = props => { title: "Personal Data" }} /> + ); }; From ec3e440e31bff797f33d86bac11b7ca578f98f01 Mon Sep 17 00:00:00 2001 From: Chris <34682781+monkins1010@users.noreply.github.com> Date: Thu, 14 Mar 2024 22:24:39 +0000 Subject: [PATCH 05/10] updates for personalinfo with vdxfids --- .../PersonalSelectData/PersonalSelectData.js | 12 +++--- .../PersonalAttributes/PersonalAttributes.js | 43 +++++++++++-------- .../PersonalAttributes.render.js | 20 +++++---- .../PersonalAttributesEditName.js | 9 ++-- .../PersonalAttributesEditName.render.js | 32 +++++++------- .../Personal/PersonalInfo/PersonalInfo.js | 13 +++--- .../PersonalInfo/PersonalInfo.render.js | 9 ++-- .../PersonalIntroSlider.js | 12 +++--- src/utils/personal/displayUtils.js | 10 +++-- 9 files changed, 87 insertions(+), 73 deletions(-) diff --git a/src/containers/DeepLink/PersonalSelectData/PersonalSelectData.js b/src/containers/DeepLink/PersonalSelectData/PersonalSelectData.js index c7f66777..b1fc409b 100644 --- a/src/containers/DeepLink/PersonalSelectData/PersonalSelectData.js +++ b/src/containers/DeepLink/PersonalSelectData/PersonalSelectData.js @@ -7,16 +7,18 @@ import { PERSONAL_ATTRIBUTES, PERSONAL_BIRTHDAY, PERSONAL_NATIONALITIES } from " import { provideCustomBackButton } from "../../../utils/navigation/customBack"; import { PersonalSelectDataRender } from "./PersonalSelectData.render" import { primitives } from "verusid-ts-client" +const { IDENTITYDATA_CONTACT, IDENTITYDATA_PERSONAL_DETAILS, IDENTITYDATA_LOCATIONS, IDENTITYDATA_DOCUMENTS_AND_IMAGES, IDENTITYDATA_BANKING_INFORMATION} = primitives; + //const { defaultPersonalProfileDataTemplate } = primitives; const EDIT = 'edit' const REMOVE = 'remove' const PERSONALDATACATAGORIES = [ - primitives.IDENTITYDATA_CONTACT.vdxfid, - primitives.IDENTITYDATA_LOCATIONS.vdxfid, - primitives.IDENTITYDATA_DOCUMENTS_AND_IMAGES.vdxfid, - primitives.IDENTITYDATA_PERSONAL_DETAILS.vdxfid, - primitives.IDENTITYDATA_BANKING_INFORMATION.vdxfid + IDENTITYDATA_CONTACT.vdxfid, + IDENTITYDATA_LOCATIONS.vdxfid, + IDENTITYDATA_DOCUMENTS_AND_IMAGES.vdxfid, + IDENTITYDATA_PERSONAL_DETAILS.vdxfid, + IDENTITYDATA_BANKING_INFORMATION.vdxfid ]; const PERSONALDATALINKS = [ diff --git a/src/containers/Personal/PersonalAttributes/PersonalAttributes.js b/src/containers/Personal/PersonalAttributes/PersonalAttributes.js index e4a872a8..c7c69e1d 100644 --- a/src/containers/Personal/PersonalAttributes/PersonalAttributes.js +++ b/src/containers/Personal/PersonalAttributes/PersonalAttributes.js @@ -1,11 +1,14 @@ import moment from "moment"; -import { Component } from "react" +import { Component } from "react" +import { Platform, NativeModules } from 'react-native' import { connect } from 'react-redux' import { modifyPersonalDataForUser } from "../../../actions/actionDispatchers"; import { requestPersonalData } from "../../../utils/auth/authBox"; import { PERSONAL_ATTRIBUTES, PERSONAL_BIRTHDAY, PERSONAL_NATIONALITIES } from "../../../utils/constants/personal"; import { provideCustomBackButton } from "../../../utils/navigation/customBack"; import { PersonalAttributesRender } from "./PersonalAttributes.render" +import { primitives } from "verusid-ts-client" +const { IDENTITYDATA_PERSONAL_DETAILS, IDENTITYDATA_FIRSTNAME, IDENTITYDATA_MIDDLENAME, IDENTITYDATA_LASTNAME, IDENTITYDATA_DATEOFBIRTH, IDENTITYDATA_NATIONALITY } = primitives; const EDIT = 'edit' const REMOVE = 'remove' @@ -15,17 +18,11 @@ class PersonalAttributes extends Component { super(); this.state = { attributes: { - name: { - first: "John", - middle: "", - last: "Doe" - }, - birthday: { - day: null, - month: null, - year: null - }, - nationalities: [] + [IDENTITYDATA_FIRSTNAME.vdxfid]: "John", + [IDENTITYDATA_MIDDLENAME.vdxfid]: "", + [IDENTITYDATA_LASTNAME.vdxfid]: "Doe", + [IDENTITYDATA_DATEOFBIRTH.vdxfid]: {}, + [IDENTITYDATA_NATIONALITY.vdxfid]: [], }, nationalityModalOpen: false, birthdaySelectorModalOpen: false, @@ -126,21 +123,23 @@ class PersonalAttributes extends Component { } addNationality(nationalityCode) { - const nationalities = this.state.attributes.nationalities ? this.state.attributes.nationalities : [] - this.updateAttribute(PERSONAL_NATIONALITIES, [...nationalities, nationalityCode]) + const nationalities = this.state.attributes[IDENTITYDATA_NATIONALITY.vdxfid] + ? this.state.attributes[IDENTITYDATA_NATIONALITY.vdxfid] : [] + this.updateAttribute(IDENTITYDATA_NATIONALITY.vdxfid, [...nationalities, nationalityCode]) } removeNationality(index) { - let nationalities = this.state.attributes.nationalities ? this.state.attributes.nationalities : [] + let nationalities = this.state.attributes[IDENTITYDATA_NATIONALITY.vdxfid] + ? this.state.attributes[IDENTITYDATA_NATIONALITY.vdxfid] : [] nationalities.splice(index, 1); - this.updateAttribute(PERSONAL_NATIONALITIES, nationalities) + this.updateAttribute(IDENTITYDATA_NATIONALITY.vdxfid, nationalities) } setBirthday(date) { this.setState({ birthdaySelectorModalOpen: false }, () => { - this.updateAttribute(PERSONAL_BIRTHDAY, date) + this.updateAttribute(IDENTITYDATA_DATEOFBIRTH.vdxfid, date) }) } @@ -173,10 +172,16 @@ class PersonalAttributes extends Component { } renderDate() { - const { day, month, year } = this.state.attributes.birthday; + const { day, month, year } = this.state.attributes[IDENTITYDATA_DATEOFBIRTH.vdxfid]; const date = this.getDateClassInstance(day, month, year) - return date.toLocaleDateString("en-US", { + const deviceLanguage = + Platform.OS === 'ios' + ? NativeModules.SettingsManager.settings.AppleLocale || + NativeModules.SettingsManager.settings.AppleLanguages[0] //iOS 13 + : NativeModules.I18nManager.localeIdentifier; + + return date.toLocaleDateString(deviceLanguage, { year: "numeric", month: "long", day: "numeric", diff --git a/src/containers/Personal/PersonalAttributes/PersonalAttributes.render.js b/src/containers/Personal/PersonalAttributes/PersonalAttributes.render.js index 562e3646..c043c91d 100644 --- a/src/containers/Personal/PersonalAttributes/PersonalAttributes.render.js +++ b/src/containers/Personal/PersonalAttributes/PersonalAttributes.render.js @@ -7,6 +7,8 @@ import Styles from "../../../styles"; import { ISO_3166_COUNTRIES, ISO_3166_ALPHA_2_CODES } from "../../../utils/constants/iso3166"; import { PERSONAL_NATIONALITIES } from "../../../utils/constants/personal"; import { renderPersonalBirthday, renderPersonalFullName } from "../../../utils/personal/displayUtils"; +import { primitives } from "verusid-ts-client" +const { IDENTITYDATA_PERSONAL_DETAILS, IDENTITYDATA_FIRSTNAME, IDENTITYDATA_MIDDLENAME, IDENTITYDATA_LASTNAME, IDENTITYDATA_DATEOFBIRTH, IDENTITYDATA_NATIONALITY } = primitives; export const PersonalAttributesRender = function () { return ( @@ -46,12 +48,12 @@ export const PersonalAttributesRender = function () { flexHeight={0.5} visible={this.state.birthdaySelectorModalOpen} initialDate={ - this.state.attributes.birthday == null + this.state.attributes[IDENTITYDATA_DATEOFBIRTH.vdxfid] == null ? new Date() : this.getDateClassInstance( - this.state.attributes.birthday.day, - this.state.attributes.birthday.month, - this.state.attributes.birthday.year + this.state.attributes[IDENTITYDATA_DATEOFBIRTH.vdxfid].day, + this.state.attributes[IDENTITYDATA_DATEOFBIRTH.vdxfid].month, + this.state.attributes[IDENTITYDATA_DATEOFBIRTH.vdxfid].year ) } onSelect={(date) => this.setBirthday(date)} @@ -64,7 +66,7 @@ export const PersonalAttributesRender = function () { {"Name"} ( )} @@ -73,9 +75,9 @@ export const PersonalAttributesRender = function () { {"Date of birth"} - {this.state.attributes.birthday != null ? ( + {this.state.attributes[IDENTITYDATA_DATEOFBIRTH.vdxfid] != null ? ( ( )} @@ -91,9 +93,9 @@ export const PersonalAttributesRender = function () { {"Nationalities"} - {this.state.attributes.nationalities == null + {this.state.attributes[IDENTITYDATA_NATIONALITY.vdxfid] == null ? null - : this.state.attributes.nationalities.map((code, index) => { + : this.state.attributes[IDENTITYDATA_NATIONALITY.vdxfid].map((code, index) => { const nationality = ISO_3166_COUNTRIES[code]; return ( diff --git a/src/containers/Personal/PersonalAttributes/PersonalAttributesEditName/PersonalAttributesEditName.js b/src/containers/Personal/PersonalAttributes/PersonalAttributesEditName/PersonalAttributesEditName.js index 3dc753b5..a636d203 100644 --- a/src/containers/Personal/PersonalAttributes/PersonalAttributesEditName/PersonalAttributesEditName.js +++ b/src/containers/Personal/PersonalAttributes/PersonalAttributesEditName/PersonalAttributesEditName.js @@ -4,17 +4,14 @@ import { modifyPersonalDataForUser } from "../../../../actions/actionDispatchers import { PERSONAL_ATTRIBUTES } from "../../../../utils/constants/personal"; import { provideCustomBackButton } from "../../../../utils/navigation/customBack"; import { PersonalAttributesEditNameRender } from "./PersonalAttributesEditName.render" +import { primitives } from "verusid-ts-client" +const { IDENTITYDATA_PERSONAL_DETAILS, IDENTITYDATA_DATEOFBIRTH} = primitives; class PersonalAttributesEditName extends Component { constructor(props) { super(props); this.state = { attributes: props.route.params.attributes, - name: { - first: props.route.params.attributes.name ? props.route.params.attributes.name.first : "", - middle: props.route.params.attributes.name ? props.route.params.attributes.name.middle : "", - last: props.route.params.attributes.name ? props.route.params.attributes.name.last : "", - }, currentTextInputModal: null, loading: false, }; @@ -33,7 +30,7 @@ class PersonalAttributesEditName extends Component { updateName() { this.setState({ loading: true }, async () => { await modifyPersonalDataForUser( - {...this.state.attributes, name: this.state.name}, + this.state.attributes, PERSONAL_ATTRIBUTES, this.props.activeAccount.accountHash ); diff --git a/src/containers/Personal/PersonalAttributes/PersonalAttributesEditName/PersonalAttributesEditName.render.js b/src/containers/Personal/PersonalAttributes/PersonalAttributesEditName/PersonalAttributesEditName.render.js index 3f3f3b16..f0d93110 100644 --- a/src/containers/Personal/PersonalAttributes/PersonalAttributesEditName/PersonalAttributesEditName.render.js +++ b/src/containers/Personal/PersonalAttributes/PersonalAttributesEditName/PersonalAttributesEditName.render.js @@ -4,6 +4,8 @@ import { Divider, List, Portal } from "react-native-paper"; import Styles from "../../../../styles"; import TextInputModal from "../../../../components/TextInputModal/TextInputModal" import Colors from "../../../../globals/colors"; +import { primitives } from "verusid-ts-client" +const { IDENTITYDATA_PERSONAL_DETAILS, IDENTITYDATA_FIRSTNAME, IDENTITYDATA_MIDDLENAME, IDENTITYDATA_LASTNAME, IDENTITYDATA_DATEOFBIRTH, IDENTITYDATA_NATIONALITY } = primitives; export const PersonalAttributesEditNameRender = function () { return ( @@ -12,13 +14,13 @@ export const PersonalAttributesEditNameRender = function () { {this.state.currentTextInputModal != null && ( { if (text != null) this.setState({ - name: { - ...this.state.name, + attributes: { + ...this.state.attributes, [this.state.currentTextInputModal]: text, }, }); @@ -31,19 +33,19 @@ export const PersonalAttributesEditNameRender = function () { } onPress={ - this.state.loading ? () => {} : () => this.setState({ currentTextInputModal: "first" }) + this.state.loading ? () => {} : () => this.setState({ currentTextInputModal: IDENTITYDATA_FIRSTNAME.vdxfid }) } /> @@ -51,19 +53,19 @@ export const PersonalAttributesEditNameRender = function () { } onPress={ - this.state.loading ? () => {} : () => this.setState({ currentTextInputModal: "middle" }) + this.state.loading ? () => {} : () => this.setState({ currentTextInputModal: IDENTITYDATA_MIDDLENAME.vdxfid }) } /> @@ -71,19 +73,19 @@ export const PersonalAttributesEditNameRender = function () { } onPress={ - this.state.loading ? () => {} : () => this.setState({ currentTextInputModal: "last" }) + this.state.loading ? () => {} : () => this.setState({ currentTextInputModal: IDENTITYDATA_LASTNAME.vdxfid }) } /> diff --git a/src/containers/Personal/PersonalInfo/PersonalInfo.js b/src/containers/Personal/PersonalInfo/PersonalInfo.js index 5592ff26..dcea3f81 100644 --- a/src/containers/Personal/PersonalInfo/PersonalInfo.js +++ b/src/containers/Personal/PersonalInfo/PersonalInfo.js @@ -3,24 +3,25 @@ use to configure their personal wallet profile. They can then take this profile data and submit it to applications that require KYC if they want. -*/ +*/ import { Component } from "react" import { connect } from 'react-redux' import { requestPersonalData } from "../../../utils/auth/authBox"; import { PERSONAL_ATTRIBUTES } from "../../../utils/constants/personal"; import { PersonalInfoRender } from "./PersonalInfo.render" +import { primitives } from "verusid-ts-client" +const { IDENTITYDATA_MIDDLENAME, IDENTITYDATA_FIRSTNAME, IDENTITYDATA_LASTNAME, IDENTITYDATA_DATEOFBIRTH } = primitives; class PersonalInfo extends Component { constructor(props) { super(props); this.state = { attributes: { - name: { - first: "John", - middle: "", - last: "Doe" - } + [IDENTITYDATA_FIRSTNAME.vdxfid]: "John", + [IDENTITYDATA_MIDDLENAME.vdxfid]: "", + [IDENTITYDATA_LASTNAME.vdxfid]: "Doe", + [IDENTITYDATA_DATEOFBIRTH.vdxfid]: {}, }, loading: false }; diff --git a/src/containers/Personal/PersonalInfo/PersonalInfo.render.js b/src/containers/Personal/PersonalInfo/PersonalInfo.render.js index a4bd5e87..88262fa0 100644 --- a/src/containers/Personal/PersonalInfo/PersonalInfo.render.js +++ b/src/containers/Personal/PersonalInfo/PersonalInfo.render.js @@ -3,7 +3,8 @@ import { Text, SafeAreaView, ScrollView, View } from "react-native"; import { Divider, List, Avatar, Title } from "react-native-paper"; import Styles from "../../../styles"; import { renderPersonalFullName } from "../../../utils/personal/displayUtils"; - +import { primitives } from "verusid-ts-client" +const { IDENTITYDATA_FIRSTNAME, IDENTITYDATA_LASTNAME, IDENTITYDATA_PERSONAL_DETAILS} = primitives; export const PersonalInfoRender = function () { return ( @@ -11,9 +12,9 @@ export const PersonalInfoRender = function () { - {renderPersonalFullName(this.state.attributes.name).title} + {renderPersonalFullName(this.state.attributes).title} diff --git a/src/containers/Personal/PersonalIntroSlider/PersonalIntroSlider.js b/src/containers/Personal/PersonalIntroSlider/PersonalIntroSlider.js index 9fd3f046..ad247a84 100644 --- a/src/containers/Personal/PersonalIntroSlider/PersonalIntroSlider.js +++ b/src/containers/Personal/PersonalIntroSlider/PersonalIntroSlider.js @@ -16,6 +16,8 @@ import { useEffect } from "react"; import { modifyPersonalDataForUser } from "../../../actions/actionDispatchers"; import { PERSONAL_ATTRIBUTES } from "../../../utils/constants/personal"; import { createAlert } from "../../../actions/actions/alert/dispatchers/alert"; +import { primitives } from "verusid-ts-client" +const { IDENTITYDATA_PERSONAL_DETAILS, IDENTITYDATA_FIRSTNAME, IDENTITYDATA_MIDDLENAME, IDENTITYDATA_LASTNAME, IDENTITYDATA_DATEOFBIRTH, IDENTITYDATA_NATIONALITY } = primitives; const slides = [ { @@ -48,7 +50,7 @@ const NameForm = (props) => { const [last, setLast] = React.useState(''); useEffect(() => { - onChange({ first: first.trim(), middle: middle.trim(), last: last.trim() }); + onChange({ [IDENTITYDATA_FIRSTNAME.vdxfid]: first.trim(), [IDENTITYDATA_MIDDLENAME.vdxfid]: middle.trim(), [IDENTITYDATA_LASTNAME.vdxfid]: last.trim() }); }, [first, middle, last]); return ( @@ -104,9 +106,9 @@ class PersonalIntroSlider extends Component { super(); this.state = { name: { - first: '', - middle: '', - last: '' + [IDENTITYDATA_FIRSTNAME.vdxfid]: '', + [IDENTITYDATA_MIDDLENAME.vdxfid]: '', + [IDENTITYDATA_LASTNAME.vdxfid]: '' }, loading: false, currentSlide: 0 @@ -179,7 +181,7 @@ class PersonalIntroSlider extends Component { _onDone = () => { this.setState({loading: true}, async () => { await modifyPersonalDataForUser( - { name: this.state.name }, + this.state.name, PERSONAL_ATTRIBUTES, this.props.activeAccount.accountHash ) diff --git a/src/utils/personal/displayUtils.js b/src/utils/personal/displayUtils.js index 610d406f..771c935a 100644 --- a/src/utils/personal/displayUtils.js +++ b/src/utils/personal/displayUtils.js @@ -8,12 +8,14 @@ import { } from "../constants/personal"; import { Platform } from 'react-native'; var RNFS = require('react-native-fs'); +import { primitives } from "verusid-ts-client" +const { IDENTITYDATA_FIRSTNAME, IDENTITYDATA_LASTNAME, IDENTITYDATA_MIDDLENAME} = primitives; -export const renderPersonalFullName = (name) => { +export const renderPersonalFullName = (state) => { return { - title: `${name.first} ${ - name.middle != null && name.middle.length > 0 ? name.middle + " " : "" - }${name.last}` + title: `${state[IDENTITYDATA_FIRSTNAME.vdxfid]} ${ + state[IDENTITYDATA_MIDDLENAME.vdxfid] != null && state[IDENTITYDATA_MIDDLENAME.vdxfid] > 0 ? state[IDENTITYDATA_MIDDLENAME.vdxfid] + " " : "" + }${state[IDENTITYDATA_LASTNAME.vdxfid]}` }; }; From 4ded607e13e1b3130e5a6924b2f388f3c3c41cac Mon Sep 17 00:00:00 2001 From: Chris <34682781+monkins1010@users.noreply.github.com> Date: Tue, 19 Mar 2024 15:56:29 +0000 Subject: [PATCH 06/10] Add VDXFKeys for personal data --- .../PersonalAttributes/PersonalAttributes.js | 2 +- .../PersonalContact/PersonalContact.js | 26 +++++++------- .../PersonalContact/PersonalContact.render.js | 10 +++--- .../PersonalLocations.render.js | 24 +++++++------ .../PersonalLocationsEditAddress.js | 27 ++++++++------- .../PersonalLocationsEditAddress.render.js | 11 +++--- ...nalPaymentMethodsEditBankAccount.render.js | 20 ++++++----- ...entMethodsEditBankAccountAddress.render.js | 24 +++++++------ src/utils/personal/displayUtils.js | 34 ++++++++++--------- 9 files changed, 97 insertions(+), 81 deletions(-) diff --git a/src/containers/Personal/PersonalAttributes/PersonalAttributes.js b/src/containers/Personal/PersonalAttributes/PersonalAttributes.js index c7c69e1d..18b16c09 100644 --- a/src/containers/Personal/PersonalAttributes/PersonalAttributes.js +++ b/src/containers/Personal/PersonalAttributes/PersonalAttributes.js @@ -8,7 +8,7 @@ import { PERSONAL_ATTRIBUTES, PERSONAL_BIRTHDAY, PERSONAL_NATIONALITIES } from " import { provideCustomBackButton } from "../../../utils/navigation/customBack"; import { PersonalAttributesRender } from "./PersonalAttributes.render" import { primitives } from "verusid-ts-client" -const { IDENTITYDATA_PERSONAL_DETAILS, IDENTITYDATA_FIRSTNAME, IDENTITYDATA_MIDDLENAME, IDENTITYDATA_LASTNAME, IDENTITYDATA_DATEOFBIRTH, IDENTITYDATA_NATIONALITY } = primitives; +const { IDENTITYDATA_FIRSTNAME, IDENTITYDATA_MIDDLENAME, IDENTITYDATA_LASTNAME, IDENTITYDATA_DATEOFBIRTH, IDENTITYDATA_NATIONALITY } = primitives; const EDIT = 'edit' const REMOVE = 'remove' diff --git a/src/containers/Personal/PersonalContact/PersonalContact.js b/src/containers/Personal/PersonalContact/PersonalContact.js index 6f046996..50e76313 100644 --- a/src/containers/Personal/PersonalContact/PersonalContact.js +++ b/src/containers/Personal/PersonalContact/PersonalContact.js @@ -9,6 +9,8 @@ import { } from "../../../utils/constants/personal"; import { provideCustomBackButton } from "../../../utils/navigation/customBack"; import { PersonalContactRender } from "./PersonalContact.render" +import { primitives } from "verusid-ts-client" +const { IDENTITYDATA_EMAIL, IDENTITYDATA_PHONENUMBER } = primitives; const EDIT = 'edit' const REMOVE = 'remove' @@ -18,8 +20,8 @@ class PersonalContact extends Component { super(); this.state = { contact: { - phone_numbers: [], - emails: [] + [IDENTITYDATA_PHONENUMBER.vdxfid]: [], + [IDENTITYDATA_EMAIL.vdxfid]: [] }, addPropertyModal: { open: false, @@ -157,13 +159,13 @@ class PersonalContact extends Component { } editPhone(phone, index) { - let phones = this.state.contact.phone_numbers ? this.state.contact.phone_numbers : [] + let phones = this.state.contact[IDENTITYDATA_PHONENUMBER.vdxfid] ? this.state.contact[IDENTITYDATA_PHONENUMBER.vdxfid] : [] if (index == null) { - this.updateContactPoint(PERSONAL_PHONE_NUMBERS, [...phones, phone]) + this.updateContactPoint(IDENTITYDATA_PHONENUMBER.vdxfid, [...phones, phone]) } else { phones[index] = phone - this.updateContactPoint(PERSONAL_PHONE_NUMBERS, phones) + this.updateContactPoint(IDENTITYDATA_PHONENUMBER.vdxfid, phones) } } @@ -179,26 +181,26 @@ class PersonalContact extends Component { } editEmail(email, index) { - let emails = this.state.contact.emails ? this.state.contact.emails : [] + let emails = this.state.contact[IDENTITYDATA_EMAIL.vdxfid] ? this.state.contact[IDENTITYDATA_EMAIL.vdxfid] : [] if (index == null) { - this.updateContactPoint(PERSONAL_EMAILS, [...emails, email]) + this.updateContactPoint(IDENTITYDATA_EMAIL.vdxfid, [...emails, email]) } else { emails[index] = email - this.updateContactPoint(PERSONAL_EMAILS, emails) + this.updateContactPoint(IDENTITYDATA_EMAIL.vdxfid, emails) } } removePhone(index) { - let phone_numbers = this.state.contact.phone_numbers ? this.state.contact.phone_numbers : [] + let phone_numbers = this.state.contact[IDENTITYDATA_PHONENUMBER.vdxfid] ? this.state.contact[IDENTITYDATA_PHONENUMBER.vdxfid] : [] phone_numbers.splice(index, 1); - this.updateContactPoint(PERSONAL_PHONE_NUMBERS, phone_numbers) + this.updateContactPoint(IDENTITYDATA_PHONENUMBER.vdxfid, phone_numbers) } removeEmail(index) { - let emails = this.state.contact.emails ? this.state.contact.emails : [] + let emails = this.state.contact[IDENTITYDATA_EMAIL.vdxfid] ? this.state.contact[IDENTITYDATA_EMAIL.vdxfid] : [] emails.splice(index, 1); - this.updateContactPoint(PERSONAL_EMAILS, emails) + this.updateContactPoint(IDENTITYDATA_EMAIL.vdxfid, emails) } finishPhoneEdit(phone, index) { diff --git a/src/containers/Personal/PersonalContact/PersonalContact.render.js b/src/containers/Personal/PersonalContact/PersonalContact.render.js index 9304024c..35e33d20 100644 --- a/src/containers/Personal/PersonalContact/PersonalContact.render.js +++ b/src/containers/Personal/PersonalContact/PersonalContact.render.js @@ -10,6 +10,8 @@ import { ISO_3166_COUNTRIES, ISO_3166_ALPHA_2_CODES } from "../../../utils/const import { CALLING_CODES_TO_ISO_3166 } from "../../../utils/constants/callingCodes"; import { PERSONAL_EMAILS, PERSONAL_NATIONALITIES, PERSONAL_PHONE_NUMBERS } from "../../../utils/constants/personal"; import { renderPersonalPhoneNumber } from "../../../utils/personal/displayUtils"; +import { primitives } from "verusid-ts-client" +const { IDENTITYDATA_EMAIL, IDENTITYDATA_PHONENUMBER } = primitives; export const PersonalContactRender = function () { return ( @@ -70,9 +72,9 @@ export const PersonalContactRender = function () { {"Email addresses"} - {this.state.contact.emails == null + {this.state.contact[IDENTITYDATA_EMAIL.vdxfid] == null ? null - : this.state.contact.emails.map((email, index) => { + : this.state.contact[IDENTITYDATA_EMAIL.vdxfid].map((email, index) => { return ( {"Phone numbers"} - {this.state.contact.phone_numbers == null + {this.state.contact[IDENTITYDATA_PHONENUMBER.vdxfid] == null ? null - : this.state.contact.phone_numbers.map((phone, index) => { + : this.state.contact[IDENTITYDATA_PHONENUMBER.vdxfid].map((phone, index) => { const type = phone.type; const typeFr = type.charAt(0).toUpperCase() + type.slice(1); diff --git a/src/containers/Personal/PersonalLocations/PersonalLocations.render.js b/src/containers/Personal/PersonalLocations/PersonalLocations.render.js index 60b8fccd..d8467791 100644 --- a/src/containers/Personal/PersonalLocations/PersonalLocations.render.js +++ b/src/containers/Personal/PersonalLocations/PersonalLocations.render.js @@ -5,6 +5,8 @@ import ListSelectionModal from "../../../components/ListSelectionModal/ListSelec import Styles from "../../../styles"; import { ISO_3166_COUNTRIES } from "../../../utils/constants/iso3166"; import { renderPersonalTaxId } from "../../../utils/personal/displayUtils"; +import { primitives } from "verusid-ts-client" +const { IDENTITYDATA_HOMEADDRESS_STREET1, IDENTITYDATA_HOMEADDRESS_STREET2, IDENTITYDATA_HOMEADDRESS_CITY, IDENTITYDATA_HOMEADDRESS_REGION, IDENTITYDATA_HOMEADDRESS_POSTCODE, IDENTITYDATA_HOMEADDRESS_COUNTRY } = primitives; export const PersonalLocationsRender = function () { return ( @@ -64,24 +66,24 @@ export const PersonalLocationsRender = function () { 0 - ? `${address.street1}${ - address.street2 != null && address.street2.length > 0 - ? `, ${address.street2}` + address[IDENTITYDATA_HOMEADDRESS_STREET1.vdxfid]?.length > 0 + ? `${address[IDENTITYDATA_HOMEADDRESS_STREET1.vdxfid]}${ + address[IDENTITYDATA_HOMEADDRESS_STREET2.vdxfid] != null && address[IDENTITYDATA_HOMEADDRESS_STREET2.vdxfid].length > 0 + ? `, ${address[IDENTITYDATA_HOMEADDRESS_STREET2.vdxfid]}` : "" }` : "Empty address" } description={`${ - address.postal_code.length > 0 ? `${address.postal_code} ` : "" + address[IDENTITYDATA_HOMEADDRESS_POSTCODE.vdxfid]?.length > 0 ? `${address[IDENTITYDATA_HOMEADDRESS_POSTCODE.vdxfid]} ` : "" }${ - address.state_province_region.length > 0 - ? `${address.state_province_region}, ` + address[IDENTITYDATA_HOMEADDRESS_REGION.vdxfid]?.length > 0 + ? `${address[IDENTITYDATA_HOMEADDRESS_REGION.vdxfid]}, ` : "" - }${address.city.length > 0 ? `${address.city}, ` : "Unknown City, "}${ - ISO_3166_COUNTRIES[address.country] != null - ? `${ISO_3166_COUNTRIES[address.country].emoji} ${ - ISO_3166_COUNTRIES[address.country].name + }${address[IDENTITYDATA_HOMEADDRESS_CITY.vdxfid]?.length > 0 ? `${address[IDENTITYDATA_HOMEADDRESS_CITY.vdxfid]}, ` : "Unknown City, "}${ + ISO_3166_COUNTRIES[address[IDENTITYDATA_HOMEADDRESS_COUNTRY.vdxfid]] != null + ? `${ISO_3166_COUNTRIES[address[IDENTITYDATA_HOMEADDRESS_COUNTRY.vdxfid]].emoji} ${ + ISO_3166_COUNTRIES[address[IDENTITYDATA_HOMEADDRESS_COUNTRY.vdxfid]].name }` : "Unknown Country" }`} diff --git a/src/containers/Personal/PersonalLocations/PersonalLocationsEditAddress/PersonalLocationsEditAddress.js b/src/containers/Personal/PersonalLocations/PersonalLocationsEditAddress/PersonalLocationsEditAddress.js index 53b0ec32..e8166f87 100644 --- a/src/containers/Personal/PersonalLocations/PersonalLocationsEditAddress/PersonalLocationsEditAddress.js +++ b/src/containers/Personal/PersonalLocations/PersonalLocationsEditAddress/PersonalLocationsEditAddress.js @@ -6,6 +6,8 @@ import { createAlert, resolveAlert } from "../../../../actions/actions/alert/dis import { PERSONAL_LOCATIONS } from "../../../../utils/constants/personal"; import { provideCustomBackButton } from "../../../../utils/navigation/customBack"; import { PersonalLocationsEditAddressRender } from "./PersonalLocationsEditAddress.render" +import { primitives } from "verusid-ts-client" +const { IDENTITYDATA_HOMEADDRESS_STREET1, IDENTITYDATA_HOMEADDRESS_STREET2, IDENTITYDATA_HOMEADDRESS_CITY, IDENTITYDATA_HOMEADDRESS_REGION, IDENTITYDATA_HOMEADDRESS_POSTCODE, IDENTITYDATA_HOMEADDRESS_COUNTRY } = primitives; class PersonalLocationsEditAddress extends Component { constructor(props) { @@ -19,12 +21,12 @@ class PersonalLocationsEditAddress extends Component { props.route.params.index ] : { - street1: "", - street2: "", - city: "", - state_province_region: "", - postal_code: "", - country: "", + [IDENTITYDATA_HOMEADDRESS_STREET1.vdxfid]: "", + [IDENTITYDATA_HOMEADDRESS_STREET2.vdxfid]: "", + [IDENTITYDATA_HOMEADDRESS_CITY.vdxfid]: "", + [IDENTITYDATA_HOMEADDRESS_REGION.vdxfid]: "", + [IDENTITYDATA_HOMEADDRESS_POSTCODE.vdxfid]: "", + [IDENTITYDATA_HOMEADDRESS_COUNTRY.vdxfid]: "", }; this.state = { @@ -37,29 +39,30 @@ class PersonalLocationsEditAddress extends Component { }; this.addressTextInputLabels = { - street1: { + [IDENTITYDATA_HOMEADDRESS_STREET1.vdxfid]: { title: "Address line 1", description: "Street address, company name, P.O. box", placeholder: "required" }, - street2: { + [IDENTITYDATA_HOMEADDRESS_STREET2.vdxfid]: { title: "Address line 2", description: "Apartment number, unit, floor, etc.", placeholder: "optional" }, - city: { + [IDENTITYDATA_HOMEADDRESS_CITY.vdxfid]: { title: "City", placeholder: "required" }, - state_province_region: { + [IDENTITYDATA_HOMEADDRESS_REGION.vdxfid]: { title: "State/Province/Region", placeholder: "optional" }, - postal_code: { + [IDENTITYDATA_HOMEADDRESS_COUNTRY.vdxfid]: { title: "ZIP/Postal Code", placeholder: "required" } } + const sdf =324 } componentDidMount() { @@ -142,7 +145,7 @@ class PersonalLocationsEditAddress extends Component { this.setState({ address: { ...this.state.address, - country: countryCode + [IDENTITYDATA_HOMEADDRESS_COUNTRY.vdxfid]: countryCode } }, () => this.updateAddress()) } diff --git a/src/containers/Personal/PersonalLocations/PersonalLocationsEditAddress/PersonalLocationsEditAddress.render.js b/src/containers/Personal/PersonalLocations/PersonalLocationsEditAddress/PersonalLocationsEditAddress.render.js index 7fe2db15..15c1f9a8 100644 --- a/src/containers/Personal/PersonalLocations/PersonalLocationsEditAddress/PersonalLocationsEditAddress.render.js +++ b/src/containers/Personal/PersonalLocations/PersonalLocationsEditAddress/PersonalLocationsEditAddress.render.js @@ -6,7 +6,8 @@ import TextInputModal from "../../../../components/TextInputModal/TextInputModal import ListSelectionModal from "../../../../components/ListSelectionModal/ListSelectionModal"; import { ISO_3166_COUNTRIES, ISO_3166_ALPHA_2_CODES } from "../../../../utils/constants/iso3166"; import Colors from "../../../../globals/colors"; - +import { primitives } from "verusid-ts-client" +const { IDENTITYDATA_HOMEADDRESS_COUNTRY } = primitives; export const PersonalLocationsEditAddressRender = function () { return ( @@ -83,15 +84,15 @@ export const PersonalLocationsEditAddressRender = function () { 0 - ? `${address.street1}${ - address.street2 != null && address.street2.length > 0 ? `, ${address.street2}` : "" + address[IDENTITYDATA_HOMEADDRESS_STREET1.vdxfid]?.length > 0 + ? `${address[IDENTITYDATA_HOMEADDRESS_STREET1.vdxfid]}${ + address[IDENTITYDATA_HOMEADDRESS_STREET2.vdxfid] != null && address[IDENTITYDATA_HOMEADDRESS_STREET2.vdxfid]?.length > 0 ? `, ${address[IDENTITYDATA_HOMEADDRESS_STREET2.vdxfid]}` : "" }` : "Configure address", - `${address.postal_code.length > 0 ? `${address.postal_code} ` : ""}${ - address.state_province_region.length > 0 ? `${address.state_province_region}, ` : "" - }${address.city.length > 0 ? `${address.city}, ` : "Unknown City, "}${ - ISO_3166_COUNTRIES[address.country] != null - ? `${ISO_3166_COUNTRIES[address.country].emoji} ${ - ISO_3166_COUNTRIES[address.country].name + `${address[IDENTITYDATA_HOMEADDRESS_POSTCODE.vdxfid]?.length > 0 ? `${address[IDENTITYDATA_HOMEADDRESS_POSTCODE.vdxfid]} ` : ""}${ + address[IDENTITYDATA_HOMEADDRESS_REGION.vdxfid].length > 0 ? `${address[IDENTITYDATA_HOMEADDRESS_REGION.vdxfid]}, ` : "" + }${address[IDENTITYDATA_HOMEADDRESS_CITY.vdxfid].length > 0 ? `${address[IDENTITYDATA_HOMEADDRESS_CITY.vdxfid]}, ` : "Unknown City, "}${ + ISO_3166_COUNTRIES[address[IDENTITYDATA_HOMEADDRESS_COUNTRY.vdxfid]] != null + ? `${ISO_3166_COUNTRIES[address[IDENTITYDATA_HOMEADDRESS_COUNTRY.vdxfid]].emoji} ${ + ISO_3166_COUNTRIES[address[IDENTITYDATA_HOMEADDRESS_COUNTRY.vdxfid]].name }` : "Unknown Country" }`, diff --git a/src/containers/Personal/PersonalPaymentMethods/PersonalPaymentMethodsEditBankAccountAddress/PersonalPaymentMethodsEditBankAccountAddress.render.js b/src/containers/Personal/PersonalPaymentMethods/PersonalPaymentMethodsEditBankAccountAddress/PersonalPaymentMethodsEditBankAccountAddress.render.js index 9dad0575..9e56d5fe 100644 --- a/src/containers/Personal/PersonalPaymentMethods/PersonalPaymentMethodsEditBankAccountAddress/PersonalPaymentMethodsEditBankAccountAddress.render.js +++ b/src/containers/Personal/PersonalPaymentMethods/PersonalPaymentMethodsEditBankAccountAddress/PersonalPaymentMethodsEditBankAccountAddress.render.js @@ -4,6 +4,8 @@ import { Divider, List } from "react-native-paper"; import Styles from "../../../../styles"; import Colors from "../../../../globals/colors"; import { ISO_3166_COUNTRIES } from "../../../../utils/constants/iso3166"; +import { primitives } from "verusid-ts-client" +const { IDENTITYDATA_HOMEADDRESS_STREET1, IDENTITYDATA_HOMEADDRESS_STREET2, IDENTITYDATA_HOMEADDRESS_CITY, IDENTITYDATA_HOMEADDRESS_REGION, IDENTITYDATA_HOMEADDRESS_POSTCODE, IDENTITYDATA_HOMEADDRESS_COUNTRY } = primitives; export const PersonalPaymentMethodsEditBankAccountAddressRender = function () { return ( @@ -20,24 +22,24 @@ export const PersonalPaymentMethodsEditBankAccountAddressRender = function () { 0 - ? `${address.street1}${ - address.street2 != null && address.street2.length > 0 - ? `, ${address.street2}` + address[IDENTITYDATA_HOMEADDRESS_STREET1.vdxfid]?.length > 0 + ? `${address[IDENTITYDATA_HOMEADDRESS_STREET1.vdxfid]}${ + address[IDENTITYDATA_HOMEADDRESS_STREET2.vdxfid] != null && address[IDENTITYDATA_HOMEADDRESS_STREET2.vdxfid].length > 0 + ? `, ${address[IDENTITYDATA_HOMEADDRESS_STREET2.vdxfid]}` : "" }` : "Empty address" } description={`${ - address.postal_code.length > 0 ? `${address.postal_code} ` : "" + address[IDENTITYDATA_HOMEADDRESS_POSTCODE.vdxfid]?.length > 0 ? `${address[IDENTITYDATA_HOMEADDRESS_POSTCODE.vdxfid]} ` : "" }${ - address.state_province_region.length > 0 - ? `${address.state_province_region}, ` + address[IDENTITYDATA_HOMEADDRESS_REGION.vdxfid]?.length > 0 + ? `${address[IDENTITYDATA_HOMEADDRESS_REGION.vdxfid]}, ` : "" - }${address.city.length > 0 ? `${address.city}, ` : "Unknown City, "}${ - ISO_3166_COUNTRIES[address.country] != null - ? `${ISO_3166_COUNTRIES[address.country].emoji} ${ - ISO_3166_COUNTRIES[address.country].name + }${address[IDENTITYDATA_HOMEADDRESS_CITY.vdxfid]?.length > 0 ? `${address[IDENTITYDATA_HOMEADDRESS_CITY.vdxfid]}, ` : "Unknown City, "}${ + ISO_3166_COUNTRIES[address[IDENTITYDATA_HOMEADDRESS_COUNTRY.vdxfid]] != null + ? `${ISO_3166_COUNTRIES[address[IDENTITYDATA_HOMEADDRESS_COUNTRY.vdxfid]].emoji} ${ + ISO_3166_COUNTRIES[address[IDENTITYDATA_HOMEADDRESS_COUNTRY.vdxfid]].name }` : "Unknown Country" }`} diff --git a/src/utils/personal/displayUtils.js b/src/utils/personal/displayUtils.js index 771c935a..bd269db5 100644 --- a/src/utils/personal/displayUtils.js +++ b/src/utils/personal/displayUtils.js @@ -9,7 +9,9 @@ import { import { Platform } from 'react-native'; var RNFS = require('react-native-fs'); import { primitives } from "verusid-ts-client" -const { IDENTITYDATA_FIRSTNAME, IDENTITYDATA_LASTNAME, IDENTITYDATA_MIDDLENAME} = primitives; +const { IDENTITYDATA_FIRSTNAME, IDENTITYDATA_LASTNAME, IDENTITYDATA_MIDDLENAME, + BANK_ACCOUNT_COUNTRY, BANK_ACCOUNT_CURRENCY, BANK_ACCOUNT_NUMBER, BANK_ACCOUNT_TYPE} = primitives; + const { IDENTITYDATA_HOMEADDRESS_STREET1, IDENTITYDATA_HOMEADDRESS_STREET2, IDENTITYDATA_HOMEADDRESS_CITY, IDENTITYDATA_HOMEADDRESS_REGION, IDENTITYDATA_HOMEADDRESS_POSTCODE, IDENTITYDATA_HOMEADDRESS_COUNTRY } = primitives; export const renderPersonalFullName = (state) => { return { @@ -104,34 +106,34 @@ export const renderPersonalTaxId = (taxCountry) => { export const renderPersonalAddress = (address) => { return { title: - address.street1.length > 0 - ? `${address.street1}${ - address.street2 != null && address.street2.length > 0 ? `, ${address.street2}` : "" + address[IDENTITYDATA_HOMEADDRESS_COUNTRY.vdxfid].length > 0 + ? `${address[IDENTITYDATA_HOMEADDRESS_STREET1.vdxfid]}${ + address[IDENTITYDATA_HOMEADDRESS_STREET2.vdxfid] != null && address[IDENTITYDATA_HOMEADDRESS_STREET2.vdxfid].length > 0 ? `, ${address[IDENTITYDATA_HOMEADDRESS_STREET2.vdxfid]}` : "" }` : "Empty address", - description: `${address.postal_code.length > 0 ? `${address.postal_code} ` : ""}${ - address.state_province_region.length > 0 ? `${address.state_province_region}, ` : "" - }${address.city.length > 0 ? `${address.city}, ` : "Unknown City, "}${ - ISO_3166_COUNTRIES[address.country] != null - ? `${ISO_3166_COUNTRIES[address.country].emoji} ${ISO_3166_COUNTRIES[address.country].name}` + description: `${address[IDENTITYDATA_HOMEADDRESS_POSTCODE.vdxfid].length > 0 ? `${address[IDENTITYDATA_HOMEADDRESS_POSTCODE.vdxfid]} ` : ""}${ + address[IDENTITYDATA_HOMEADDRESS_REGION.vdxfid]?.length > 0 ? `${address[IDENTITYDATA_HOMEADDRESS_REGION.vdxfid]}, ` : "" + }${address[IDENTITYDATA_HOMEADDRESS_CITY.vdxfid]?.length > 0 ? `${address[IDENTITYDATA_HOMEADDRESS_CITY.vdxfid]}, ` : "Unknown City, "}${ + ISO_3166_COUNTRIES[address[IDENTITYDATA_HOMEADDRESS_COUNTRY.vdxfid]] != null + ? `${ISO_3166_COUNTRIES[address[IDENTITYDATA_HOMEADDRESS_COUNTRY.vdxfid]].emoji} ${ISO_3166_COUNTRIES[address[IDENTITYDATA_HOMEADDRESS_COUNTRY.vdxfid]].name}` : "Unknown Country" }`, }; } export const renderPersonalBankAccount = (account) => { - const accountLocaleString = ISO_3166_COUNTRIES[account.country] - ? `${ISO_3166_COUNTRIES[account.country].emoji} Account` + const accountLocaleString = ISO_3166_COUNTRIES[account[BANK_ACCOUNT_COUNTRY.vdxfid]] + ? `${ISO_3166_COUNTRIES[account[BANK_ACCOUNT_COUNTRY.vdxfid]].emoji} Account` : "Bank Account"; const accountNumberString = - account.account_number != null && account.account_number.length > 4 - ? ` ending in ${account.account_number.slice(-4)}` + account[BANK_ACCOUNT_NUMBER.vdxfid] != null && account[BANK_ACCOUNT_NUMBER.vdxfid].length > 4 + ? ` ending in ${account[BANK_ACCOUNT_NUMBER.vdxfid].slice(-4)}` : ""; const accountDescription = `${ - account.primary_currency != null && account.primary_currency.length > 0 - ? account.primary_currency + " " + account[BANK_ACCOUNT_CURRENCY.vdxfid] != null && account[BANK_ACCOUNT_CURRENCY.vdxfid].length > 0 + ? account[BANK_ACCOUNT_CURRENCY.vdxfid] + " " : "" - }${account.account_type}`; + }${account[BANK_ACCOUNT_TYPE.vdxfid]}`; return { title: `${accountLocaleString}${accountNumberString}`, From 2475143d5f14952cd1494a085cb9cc705af41c40 Mon Sep 17 00:00:00 2001 From: Chris <34682781+monkins1010@users.noreply.github.com> Date: Mon, 25 Mar 2024 21:27:39 +0000 Subject: [PATCH 07/10] personal data handlers --- .../LoginRequestInfo/LoginRequestInfo.js | 22 ++- .../PersonalSelectData/PersonalSelectData.js | 100 ++++++++--- .../PersonalSelectData.render.js | 38 ++--- .../PersonalLocationsEditAddress.js | 2 +- .../PersonalPaymentMethodsEditBankAccount.js | 14 +- src/utils/deeplink/handlePersonalDataSend.js | 18 ++ src/utils/personal/displayUtils.js | 161 ++++++++++++++---- 7 files changed, 253 insertions(+), 102 deletions(-) create mode 100644 src/utils/deeplink/handlePersonalDataSend.js diff --git a/src/containers/DeepLink/LoginRequestInfo/LoginRequestInfo.js b/src/containers/DeepLink/LoginRequestInfo/LoginRequestInfo.js index c54b3805..7b9e85c2 100644 --- a/src/containers/DeepLink/LoginRequestInfo/LoginRequestInfo.js +++ b/src/containers/DeepLink/LoginRequestInfo/LoginRequestInfo.js @@ -126,6 +126,16 @@ const LoginRequestInfo = props => { const buildAlert = (request) => { + const setPermission = () => { + const _permissions = permissions.map(permission => { + if (permission.vdxfkey === request.vdxfkey) { + return { ...permission, agreed: true }; + } + return permission; + }); + setExtraPermissions(_permissions); + } + if (request.agreed) return; if (request.openAttestation) { @@ -135,7 +145,9 @@ const LoginRequestInfo = props => { props.navigation.navigate("PersonalSelectData", { deeplinkData, - fromService: false + fromService: false, + cancel: {cancel}, + onGoBack: (data) => data ? setPermission(data) : () => {}, }); return; } @@ -151,13 +163,7 @@ const LoginRequestInfo = props => { }, { text: 'ACCEPT', onPress: () => { - const _permissions = permissions.map(permission => { - if (permission.vdxfkey === request.vdxfkey) { - return { ...permission, agreed: true }; - } - return permission; - }); - setExtraPermissions(_permissions); + setPermission(); resolveAlert(true) diff --git a/src/containers/DeepLink/PersonalSelectData/PersonalSelectData.js b/src/containers/DeepLink/PersonalSelectData/PersonalSelectData.js index b1fc409b..30e45274 100644 --- a/src/containers/DeepLink/PersonalSelectData/PersonalSelectData.js +++ b/src/containers/DeepLink/PersonalSelectData/PersonalSelectData.js @@ -1,12 +1,15 @@ import moment from "moment"; import { Component } from "react" import { connect } from 'react-redux' +import { createAlert } from "../../../actions/actions/alert/dispatchers/alert" import { modifyPersonalDataForUser } from "../../../actions/actionDispatchers"; import { requestPersonalData } from "../../../utils/auth/authBox"; import { PERSONAL_ATTRIBUTES, PERSONAL_BIRTHDAY, PERSONAL_NATIONALITIES } from "../../../utils/constants/personal"; import { provideCustomBackButton } from "../../../utils/navigation/customBack"; import { PersonalSelectDataRender } from "./PersonalSelectData.render" -import { primitives } from "verusid-ts-client" +import { checkPersonalDataCatagories, checkPersonalDataKeys } from "../../../utils/personal/displayUtils"; +import { primitives } from "verus-typescript-primitives" + const { IDENTITYDATA_CONTACT, IDENTITYDATA_PERSONAL_DETAILS, IDENTITYDATA_LOCATIONS, IDENTITYDATA_DOCUMENTS_AND_IMAGES, IDENTITYDATA_BANKING_INFORMATION} = primitives; @@ -30,7 +33,7 @@ const PERSONALDATALINKS = [ ] class PersonalSelectData extends Component { constructor(props) { - super(); + super(props); this.state = { loginConsent: null, nationalityModalOpen: false, @@ -43,17 +46,34 @@ class PersonalSelectData extends Component { }, loading: false, ready: false, - permissions: [1, 2, 3, 4], catagoriesRequested: null, - otherKeysRequested: null + personalDataURL: "" }; } componentDidMount() { + this.updateDisplay(); + } + + componentDidUpdate(lastProps) { + if (lastProps.encryptedPersonalData !== this.props.encryptedPersonalData) { + this.updateDisplay(); + } + } + cancel = () => { + if (this.props.route.params.cancel) { + this.props.route.params.cancel.cancel() + } + } + + updateDisplay() { const { deeplinkData } = this.props.route.params const loginConsent = new primitives.LoginConsentRequest(deeplinkData); + const personalDataURL = loginConsent.challenge.subject + .filter((permission) => permission.vdxfkey === primitives.LOGIN_CONSENT_PERSONALINFO_WEBHOOK_VDXF_KEY.vdxfid); + const requestedPersonalData = loginConsent.challenge.subject .filter((permission) => permission.vdxfkey === primitives.PROFILE_DATA_VIEW_REQUEST.vdxfid); @@ -64,42 +84,70 @@ class PersonalSelectData extends Component { primitives.defaultPersonalProfileDataTemplate.forEach((templateCategory) => { if (templateCategory.vdxfid === permission.data) { catagoriesRequested[permission.data] = { title: templateCategory.category, - details: templateCategory.details, navigateTo: PERSONALDATALINKS[idx] }; + details: templateCategory.details, navigateTo: PERSONALDATALINKS[idx], color: "black" }; } }) } }) - }) - - const otherKeysRequested = {} - requestedPersonalData.forEach((permission) => { - - if (primitives.IdentityVdxfidMap[permission.data]) { - otherKeysRequested[permission.data] = primitives.IdentityVdxfidMap[permission.data].name; - } + }); - }) - this.setState({ catagoriesRequested, otherKeysRequested }); - this.loadPersonalAttributes() + checkPersonalDataCatagories(catagoriesRequested).then((success) => { + this.setState({ catagoriesRequested: catagoriesRequested, personalDataURL, ready: success}); + }); } - handleContinue() { } - - loadPersonalAttributes() { - this.setState({ loading: true }, async () => { - this.setState({ - attributes: await requestPersonalData(PERSONAL_ATTRIBUTES), - loading: false + handleContinue() { + this.setState({loading: true}); + + this.getPersonalDataFromCategories(personalData).then(() => { + sendPersonalData(personalData, this.state.personalDataURL) + .then(() => { + this.setState({loading: false}); + this.props.route.params.onGoBack(true); + this.props.navigation.goBack(); }) - }) + .catch((e) => { + this.setState({loading: false}); + createAlert("Error", e.message); + }) + } + ).catch((e) => { + this.setState({loading: false}); + createAlert("Error", e.message); + })}; + + async getPersonalDataFromCategories() { + let personalData = {}; + // await Promise.all(Object.keys(this.state.catagoriesRequested).forEach(async (categoryVdxfkey) => { + // switch (categoryVdxfkey) { + // case IDENTITYDATA_PERSONAL_DETAILS.vdxfid: + // personalData[IDENTITYDATA_PERSONAL_DETAILS.vdxfid] = await requestPersonalData(PERSONAL_ATTRIBUTES); + // break; + // case IDENTITYDATA_CONTACT.vdxfid: + // personalData[IDENTITYDATA_CONTACT.vdxfid] = await requestPersonalData(PERSONAL_CONTACT); + // break; + // case IDENTITYDATA_LOCATIONS.vdxfid: + // personalData[IDENTITYDATA_LOCATIONS.vdxfid] = await requestPersonalData(PERSONAL_LOCATIONS); + // break; + // case IDENTITYDATA_BANKING_INFORMATION.vdxfid: + // personalData[IDENTITYDATA_BANKING_INFORMATION.vdxfid] = await requestPersonalData(PERSONAL_PAYMENT_METHODS); + // break; + // case IDENTITYDATA_DOCUMENTS_AND_IMAGES.vdxfid: + // personalData[IDENTITYDATA_DOCUMENTS_AND_IMAGES.vdxfid] = await requestPersonalData(PERSONAL_IMAGES); + // break; + // default: + // break; + // } + // })); + return personalData; } + openAttributes(navigateTo) { this.props.navigation.navigate("ProfileStackScreens", { screen: navigateTo }); } - render() { return PersonalSelectDataRender.call(this); } @@ -108,7 +156,7 @@ class PersonalSelectData extends Component { const mapStateToProps = (state) => { return { activeAccount: state.authentication.activeAccount, - encryptedAttributes: state.personal.attributes + encryptedPersonalData: state.personal } }; diff --git a/src/containers/DeepLink/PersonalSelectData/PersonalSelectData.render.js b/src/containers/DeepLink/PersonalSelectData/PersonalSelectData.render.js index 1917a59d..b981ebbf 100644 --- a/src/containers/DeepLink/PersonalSelectData/PersonalSelectData.render.js +++ b/src/containers/DeepLink/PersonalSelectData/PersonalSelectData.render.js @@ -1,16 +1,13 @@ import React from "react"; import { SafeAreaView, ScrollView, View } from "react-native"; -import { Divider, List, Portal, Button, Text } from "react-native-paper"; -import DatePickerModal from "../../../components/DatePickerModal/DatePickerModal"; -import ListSelectionModal from "../../../components/ListSelectionModal/ListSelectionModal"; +import { Divider, List, Button, Text } from "react-native-paper"; + import Styles from "../../../styles"; -import { ISO_3166_COUNTRIES, ISO_3166_ALPHA_2_CODES } from "../../../utils/constants/iso3166"; -import { PERSONAL_NATIONALITIES } from "../../../utils/constants/personal"; -import { renderPersonalBirthday, renderPersonalFullName } from "../../../utils/personal/displayUtils"; + import Colors from '../../../globals/colors'; -export const PersonalSelectDataRender = function () { - const permissions = [1, 2, 3, 4] +export const PersonalSelectDataRender = function (props) { + return ( Agree to share the following personal data. - {this.state.catagoriesRequested && Object.values(this.state.catagoriesRequested).map((request, index) => { + {this.state.catagoriesRequested && Object.values(this.state.catagoriesRequested).map(request => { return ( - + {request.details}} onPress={this.state.loading ? () => { } : () => this.openAttributes(request.navigateTo)} right={(props) => } /> @@ -35,20 +32,7 @@ export const PersonalSelectDataRender = function () { ); })} - {this.state.otherKeysRequested && Object.values(this.state.otherKeysRequested).map((request, index) => { - return ( - - { }} - right={(props) => } - /> - - - - ); - })} + @@ -63,11 +47,11 @@ export const PersonalSelectDataRender = function () { + + + + ); +}; diff --git a/src/containers/DeepLink/LoginRequestInfo/LoginRequestInfo.js b/src/containers/DeepLink/LoginRequestInfo/LoginRequestInfo.js index 25b73159..08168497 100644 --- a/src/containers/DeepLink/LoginRequestInfo/LoginRequestInfo.js +++ b/src/containers/DeepLink/LoginRequestInfo/LoginRequestInfo.js @@ -1,5 +1,5 @@ -import React, {useState, useEffect} from 'react'; -import {SafeAreaView, ScrollView, TouchableOpacity, View} from 'react-native'; +import React, { useState, useEffect } from 'react'; +import { SafeAreaView, ScrollView, TouchableOpacity, View } from 'react-native'; import Styles from '../../../styles/index'; import { primitives } from "verusid-ts-client" import { Button, Divider, List, Portal, Text } from 'react-native-paper'; @@ -58,6 +58,8 @@ const LoginRequestInfo = props => { } else { if (passthrough?.fqnToAutoLink) { mainLoginMessage = `VerusID from ${signerFqn} now ready to link` + } else if (challenge.attestations && challenge.attestations.length > 0) { + mainLoginMessage = `Would you like to accept an attestation from ${signerFqn}?` } else { mainLoginMessage = `Would you like to request a VerusID from ${signerFqn}?` } @@ -79,7 +81,7 @@ const LoginRequestInfo = props => { loadFriendlyNames: async () => { try { const identityObj = await getVerusId(chain, iAddress); - + return getFriendlyNameMap(CoinDirectory.getBasicCoinObj(chain), identityObj); } catch (e) { return { @@ -123,7 +125,7 @@ const LoginRequestInfo = props => { setLoading(false) } else setLoading(true) }, [sendModalType]); - + const buildAlert = (request) => { const setPermission = () => { @@ -138,19 +140,31 @@ const LoginRequestInfo = props => { if (request.agreed) return; - if (request.openAttestation) { + if (request.viewAttestation) { return; } - else if (request.openProfile) { - if(!signedIn) return; + else if (request.attestationToAccept) { + if (!signedIn) return; + props.navigation.navigate("LoginReceiveAttestation", + { + deeplinkData, + fromService: false, + cancel: { cancel }, + onGoBack: (data) => data ? setPermission(data) : () => { }, + signerFqn + }); + return; + } + else if (request.openProfile) { + if (!signedIn) return; props.navigation.navigate("PersonalSelectData", - { - deeplinkData, - fromService: false, - cancel: {cancel}, - onGoBack: (data) => data ? setPermission(data) : () => {}, - signerFqn - }); + { + deeplinkData, + fromService: false, + cancel: { cancel }, + onGoBack: (data) => data ? setPermission(data) : () => { }, + signerFqn + }); return; } @@ -166,29 +180,33 @@ const LoginRequestInfo = props => { { text: 'ACCEPT', onPress: () => { setPermission(); - resolveAlert(true) + resolveAlert(true) } }, ], - {cancelable: true}); - } + { cancelable: true }); + } useEffect(() => { if (req && req.challenge && req.challenge.requested_access) { - if (req.challenge.requested_access.length === 1 && req.challenge.requested_access.some(primitives.IDENTITY_VIEW.vdxfid)) { - setReady(true); + var loginTemp = []; + if (req.challenge.requested_access.length === 1 && req.challenge.requested_access.some(value => value.vdxfkey === primitives.IDENTITY_VIEW.vdxfid)) { + if (req.challenge.attestations && req.challenge.attestations.length > 0) { + loginTemp.push({ data: "Accept attestation", title: "Attestation Provisioning Request", attestationToAccept: true, agreed: false }); + } else { + setReady(true); + } } else { - var loginTemp = []; for (let i = 0; i < req.challenge.requested_access.length; i++) { var tempdata = {}; - if (req.challenge.requested_access[i].vdxfkey === primitives.IDENTITY_VIEW.vdxfid) { + if (req.challenge.requested_access[i].vdxfkey === primitives.IDENTITY_VIEW.vdxfid) { continue; } else if (req.challenge.requested_access[i].vdxfkey === primitives.IDENTITY_AGREEMENT.vdxfid) { tempdata = { data: req.challenge.requested_access[i].toJson().data, title: "Agreement to accept" } } else if (req.challenge.requested_access[i].vdxfkey === primitives.ATTESTATION_READ_REQUEST.vdxfid) { - tempdata = { data: "Agree to share attestation data", title: "Attestation View Request", openAttestation: true } + tempdata = { data: "Agree to share attestation data", title: "Attestation View Request", viewAttestation: true } } else if (req.challenge.requested_access[i].vdxfkey === primitives.PROFILE_DATA_VIEW_REQUEST.vdxfid) { tempdata = { data: "Agree to share profile data", title: "Personal Data Input Request", openProfile: true } } else if (req.challenge.requested_access[i].vdxfkey === primitives.LOGIN_CONSENT_PERSONALINFO_WEBHOOK_VDXF_KEY.vdxfid) { @@ -196,8 +214,9 @@ const LoginRequestInfo = props => { } loginTemp.push({ vdxfkey: req.challenge.requested_access[i].vdxfkey, ...tempdata, agreed: false }) } - setExtraPermissions(loginTemp); } + + if (loginTemp.length > 0) setExtraPermissions(loginTemp); } }, [req]); @@ -228,28 +247,28 @@ const LoginRequestInfo = props => { : activeAccount.keyDerivationVersion, ), ); - + const addCoinAction = await addCoin( fullCoinData, activeCoinList, activeAccount.id, fullCoinData.compatible_channels, ); - + if (addCoinAction) { dispatch(addCoinAction); - + const setUserCoinsAction = setUserCoins( activeCoinList, activeAccount.id, ); dispatch(setUserCoinsAction); - + refreshActiveChainLifecycles(setUserCoinsAction.payload.activeCoinsForUser); } else { createAlert("Error", "Error adding coin") } - } catch(e) { + } catch (e) { createAlert("Error", e.message) } @@ -266,7 +285,7 @@ const LoginRequestInfo = props => { onPress: () => resolveAlert(false), style: 'cancel', }, - {text: 'Yes', onPress: () => resolveAlert(true)}, + { text: 'Yes', onPress: () => resolveAlert(true) }, ], { cancelable: true, @@ -292,9 +311,8 @@ const LoginRequestInfo = props => { if (!!coinObj.testnet != isTestnet) { createAlert( "Incorrect profile type", - `Please login to a ${ - coinObj.testnet ? 'testnet' : 'mainnet' - } profile to use this login request.`, ); + `Please login to a ${coinObj.testnet ? 'testnet' : 'mainnet' + } profile to use this login request.`,); return; } else if (!rootSystemAdded) { @@ -331,15 +349,13 @@ const LoginRequestInfo = props => { const data = { [SEND_MODAL_USER_ALLOWLIST]: allowList } - + openAuthenticateUserModal(data); } else { createAlert( "Cannot continue", - `No ${ - coinObj.testnet ? 'testnet' : 'mainnet' - } profiles found, cannot respond to ${ - coinObj.testnet ? 'testnet' : 'mainnet' + `No ${coinObj.testnet ? 'testnet' : 'mainnet' + } profiles found, cannot respond to ${coinObj.testnet ? 'testnet' : 'mainnet' } login request.`, ); } @@ -360,7 +376,7 @@ const LoginRequestInfo = props => { contentContainerStyle={Styles.focalCenter}> - + {mainLoginMessage} @@ -419,13 +435,13 @@ const LoginRequestInfo = props => { }}> diff --git a/src/containers/RootStack/DeepLinkStackScreens/DeepLinkStackScreens.js b/src/containers/RootStack/DeepLinkStackScreens/DeepLinkStackScreens.js index 623ee7da..ba899fa9 100644 --- a/src/containers/RootStack/DeepLinkStackScreens/DeepLinkStackScreens.js +++ b/src/containers/RootStack/DeepLinkStackScreens/DeepLinkStackScreens.js @@ -7,6 +7,7 @@ import LoginRequestComplete from '../../DeepLink/LoginRequestComplete/LoginReque import InvoicePaymentConfiguration from '../../DeepLink/InvoicePaymentConfiguration/InvoicePaymentConfiguration'; import PersonalSelectData from '../../DeepLink/PersonalSelectData/PersonalSelectData'; import ProfileStackScreens from '../ProfileStackScreens/ProfileStackScreens'; +import LoginReceiveAttestation from '../../DeepLink/LoginReceiveAttestation/LoginReceiveAttestation'; const DeepLinkStack = createStackNavigator(); const DeepLinkStackScreens = props => { @@ -57,6 +58,14 @@ const DeepLinkStackScreens = props => { component={ProfileStackScreens} options={{ headerShown: false }} /> + null, + title: "Receive Attestation" + }} + /> ); }; diff --git a/src/reducers/attestations.js b/src/reducers/attestations.js new file mode 100644 index 00000000..b505d6fc --- /dev/null +++ b/src/reducers/attestations.js @@ -0,0 +1,19 @@ +import {SIGN_OUT, SET_ATTESTATION_DATA} from '../utils/constants/storeType'; + +export const attestation = ( + state = { + attestations: null + }, + action, +) => { + switch (action.type) { + case SET_ATTESTATION_DATA: + return action.data; + case SIGN_OUT: + return { + attestations: null + }; + default: + return state; + } +}; diff --git a/src/reducers/index.js b/src/reducers/index.js index 250fa43d..bfd9704f 100644 --- a/src/reducers/index.js +++ b/src/reducers/index.js @@ -4,6 +4,7 @@ import { coins } from './coins'; import { ledger } from './ledger'; import { settings } from './settings'; import { personal } from './personal'; +import { attestation } from './attestations'; import { services } from './services'; import { electrum } from './cache/electrum'; import { headers } from './cache/headers'; @@ -71,5 +72,6 @@ export default combineReducers({ sendModal, loadingModal, secureLoading, - deeplink + deeplink, + attestation }); diff --git a/src/utils/api/channels/vrpc/requests/verifyHash.js b/src/utils/api/channels/vrpc/requests/verifyHash.js new file mode 100644 index 00000000..376cdddf --- /dev/null +++ b/src/utils/api/channels/vrpc/requests/verifyHash.js @@ -0,0 +1,15 @@ +import VrpcProvider from "../../../../vrpc/vrpcInterface" + +export const verifyHash = async (systemId, iAddrOrIdentity, base64Sig, hash) => { + const provider = VrpcProvider.getVerusIdInterface(systemId); + + const identity = await provider.interface.getIdentity(iAddrOrIdentity); + + return provider.verifyHash( + iAddrOrIdentity, + base64Sig, + hash, + identity.result, + systemId + ); +} \ No newline at end of file diff --git a/src/utils/constants/attestations.js b/src/utils/constants/attestations.js new file mode 100644 index 00000000..337c15e5 --- /dev/null +++ b/src/utils/constants/attestations.js @@ -0,0 +1 @@ +export const ATTESTATIONS_PROVISIONED = "attestations_provisioned" \ No newline at end of file diff --git a/src/utils/constants/storeType.js b/src/utils/constants/storeType.js index 1717a2db..445b53ff 100644 --- a/src/utils/constants/storeType.js +++ b/src/utils/constants/storeType.js @@ -309,4 +309,7 @@ export const SET_PERSONAL_DATA = "SET_PERSONAL_DATA" // Services export const SET_SERVICE_STORED_DATA = "SET_SERVICE_STORED_DATA" -export const SET_SERVICE_DATA = "SET_SERVICE_DATA" \ No newline at end of file +export const SET_SERVICE_DATA = "SET_SERVICE_DATA" + +// Attestations +export const SET_ATTESTATION_DATA = "SET_ATTESTATION_DATA" \ No newline at end of file diff --git a/src/utils/nativeStore/attestationDataStorage.js b/src/utils/nativeStore/attestationDataStorage.js new file mode 100644 index 00000000..1296e5db --- /dev/null +++ b/src/utils/nativeStore/attestationDataStorage.js @@ -0,0 +1,69 @@ +var RNFS = require('react-native-fs'); +import { ATTESTATION_DATA_STORAGE_INTERNAL_KEY } from '../../../env/index' + +export const storeAttestationData = (data) => { + if (typeof data !== 'object') throw new Error(`Attestation data store function expected object, received ${typeof data}`) + + return new Promise((resolve, reject) => { + RNFS.writeFile(RNFS.DocumentDirectoryPath + `/${ATTESTATION_DATA_STORAGE_INTERNAL_KEY}.txt`, JSON.stringify(data), 'utf8') + .then((success) => { + resolve(data); + }) + .catch(err => { + reject(err) + }) + }) +}; + +export const loadAttestationData = () => { + return new Promise((resolve, reject) => { + RNFS.readFile(RNFS.DocumentDirectoryPath + `/${ATTESTATION_DATA_STORAGE_INTERNAL_KEY}.txt`, "utf8") + .then(res => { + if (!res) { + resolve({}); + } else { + _res = JSON.parse(res); + resolve(_res); + } + }) + .catch(err => { + if (err.code === 'ENOENT') { + resolve({}); + } else { + reject(err); + } + }); + }) +}; + +export const clearAllAttestationData = () => { + return new Promise((resolve, reject) => { + RNFS.unlink(RNFS.DocumentDirectoryPath + `/${ATTESTATION_DATA_STORAGE_INTERNAL_KEY}.txt`) + .then(() => { + resolve(); + }) + .catch(err => reject(err)); + }) +}; + +export const storeAttestationDataForUser = async (data, accountHash) => { + let allAttestationData = { ...(await loadAttestationData()) } + allAttestationData[accountHash] = data + return (await storeAttestationData(allAttestationData))[accountHash] +} + +export const deleteAttestationDataForUser = async (accountHash) => { + let allAttestationData = { ...(await loadAttestationData()) } + delete allAttestationData[accountHash] + return (await storeAttestationData(allAttestationData))[accountHash] +} + +export const loadAttestationDataForUser = async (accountHash) => { + const allAttestationData = await loadAttestationData() + + if (allAttestationData[accountHash] == null) + return { + attestations: null + }; + else return allAttestationData[accountHash]; +}; From c466359718a998ef8c36ebadf9a1d8a9ca619781 Mon Sep 17 00:00:00 2001 From: Chris <34682781+monkins1010@users.noreply.github.com> Date: Wed, 22 May 2024 14:35:46 +0100 Subject: [PATCH 10/10] Add Attestations, send + recieve --- .../attestations/creators/attestations.js | 2 +- .../LoginReceiveAttestation.js | 7 +- .../LoginRequestInfo/LoginRequestInfo.js | 9 + .../LoginShareAttestation.js | 207 ++++++++++++++++++ .../LoginShareAttestation.render.js | 68 ++++++ .../DeepLinkStackScreens.js | 10 + .../ServicesStackScreens.js | 9 +- src/containers/Services/Service/Service.js | 7 +- .../AttestationService/AttestationService.js | 66 ++++++ .../AttestationService.render.js | 54 +++++ .../ViewAttestation/ViewAttestation.js | 104 +++++++++ src/images/servicelogo/index.js | 5 +- src/reducers/services.js | 8 +- .../asyncStore/serviceStoredDataStorage.js | 2 +- .../attestations/createAttestationResponse.js | 37 ++++ src/utils/auth/authBox.js | 27 ++- src/utils/constants/services.js | 8 +- src/utils/deeplink/handlePersonalDataSend.js | 12 + .../nativeStore/attestationDataStorage.js | 2 +- yarn.lock | 6 - 20 files changed, 628 insertions(+), 22 deletions(-) create mode 100644 src/containers/DeepLink/LoginShareAttestation/LoginShareAttestation.js create mode 100644 src/containers/DeepLink/LoginShareAttestation/LoginShareAttestation.render.js create mode 100644 src/containers/Services/ServiceComponents/AttestationService/AttestationService.js create mode 100644 src/containers/Services/ServiceComponents/AttestationService/AttestationService.render.js create mode 100644 src/containers/Services/ServiceComponents/AttestationService/ViewAttestation/ViewAttestation.js create mode 100644 src/utils/attestations/createAttestationResponse.js diff --git a/src/actions/actions/attestations/creators/attestations.js b/src/actions/actions/attestations/creators/attestations.js index 5e4226f9..bef5efda 100644 --- a/src/actions/actions/attestations/creators/attestations.js +++ b/src/actions/actions/attestations/creators/attestations.js @@ -2,7 +2,7 @@ import { SET_ATTESTATION_DATA } from "../../../../utils/constants/storeType" export const setAttestationData = ( data = { - attestations: null + attestations_provisioned: null } ) => ({ type: SET_ATTESTATION_DATA, diff --git a/src/containers/DeepLink/LoginReceiveAttestation/LoginReceiveAttestation.js b/src/containers/DeepLink/LoginReceiveAttestation/LoginReceiveAttestation.js index 0718078c..35fd6b91 100644 --- a/src/containers/DeepLink/LoginReceiveAttestation/LoginReceiveAttestation.js +++ b/src/containers/DeepLink/LoginReceiveAttestation/LoginReceiveAttestation.js @@ -131,7 +131,11 @@ class LoginReceiveAttestation extends Component { } const containingData = this.getAttestationData(mmrData.datadescriptors); - this.setState({ attestationData: containingData, completeAttestaton: {[loginConsent.getChallengeHash(1).toString('base64')]: dataDescriptorObject} }); + this.setState({ attestationData: containingData, + completeAttestaton: {[loginConsent.getChallengeHash(1).toString('base64')]:{ name: Object.values(attestationName)[0].objectdata.message, + signer: this.state.signerFqn, + data: dataDescriptorObject}} + }); } else { createAlert("Error", "Invalid attestation type."); @@ -146,7 +150,6 @@ class LoginReceiveAttestation extends Component { this.setState( { loading: true }, async () => { - // console.log(JSON.stringify(this.state.completeAttestaton, null, 2)) await modifyAttestationDataForUser( this.state.completeAttestaton, ATTESTATIONS_PROVISIONED, diff --git a/src/containers/DeepLink/LoginRequestInfo/LoginRequestInfo.js b/src/containers/DeepLink/LoginRequestInfo/LoginRequestInfo.js index 08168497..27671d7a 100644 --- a/src/containers/DeepLink/LoginRequestInfo/LoginRequestInfo.js +++ b/src/containers/DeepLink/LoginRequestInfo/LoginRequestInfo.js @@ -141,6 +141,15 @@ const LoginRequestInfo = props => { if (request.agreed) return; if (request.viewAttestation) { + if (!signedIn) return; + props.navigation.navigate("LoginShareAttestation", + { + deeplinkData, + fromService: false, + cancel: { cancel }, + onGoBack: (data) => data ? setPermission(data) : () => { }, + signerFqn + }); return; } else if (request.attestationToAccept) { diff --git a/src/containers/DeepLink/LoginShareAttestation/LoginShareAttestation.js b/src/containers/DeepLink/LoginShareAttestation/LoginShareAttestation.js new file mode 100644 index 00000000..6511545a --- /dev/null +++ b/src/containers/DeepLink/LoginShareAttestation/LoginShareAttestation.js @@ -0,0 +1,207 @@ +import moment from "moment"; +import { Component } from "react" +import { connect } from 'react-redux' +import { createAlert, resolveAlert } from "../../../actions/actions/alert/dispatchers/alert" +import { modifyPersonalDataForUser } from "../../../actions/actionDispatchers"; +import { requestPersonalData } from "../../../utils/auth/authBox"; +import { PERSONAL_ATTRIBUTES, PERSONAL_CONTACT, PERSONAL_LOCATIONS, PERSONAL_PAYMENT_METHODS, PERSONAL_IMAGES } from "../../../utils/constants/personal"; +import { provideCustomBackButton } from "../../../utils/navigation/customBack"; +import { LoginShareAttestationRender } from "./LoginShareAttestation.render" +import { checkPersonalDataCatagories } from "../../../utils/personal/displayUtils"; +import { handleAttestationDataSend } from "../../../utils/deeplink/handlePersonalDataSend"; +import { primitives } from "verusid-ts-client" +import { ATTESTATIONS_PROVISIONED } from "../../../utils/constants/attestations"; +import { requestAttestationData } from "../../../utils/auth/authBox"; +import { IdentityVdxfidMap } from 'verus-typescript-primitives/dist/vdxf/classes/IdentityData'; +import { getIdentity } from '../../../utils/api/channels/verusid/callCreators'; +import { createAttestationResponse } from "../../../utils/attestations/createAttestationResponse"; + +const { IDENTITYDATA_CONTACT, IDENTITYDATA_PERSONAL_DETAILS, IDENTITYDATA_LOCATIONS, IDENTITYDATA_DOCUMENTS_AND_IMAGES, IDENTITYDATA_BANKING_INFORMATION } = primitives; +import * as VDXF_Data from "verus-typescript-primitives/dist/vdxf/vdxfDataKeys"; +import { DrawerContentScrollView } from "@react-navigation/drawer"; + +class LoginShareAttestation extends Component { + constructor(props) { + super(props); + this.state = { + attestationName: "", + attestationAcceptedAttestors: [], + attestationRequestedFields: [], + attestationRequestedVdxfKeys:[], + attestationID: "", + loading: false, + ready: false, + attestationDataURL: "", + signerFqn: this.props.route.params.signerFqn + }; + const sdf = 234111 + } + + componentDidMount() { + this.updateDisplay(); + } + + cancel = () => { + if (this.props.route.params.cancel) { + this.props.route.params.cancel.cancel() + } + } + + createAttestationReply = async () => { + + const reply = await createAttestationResponse(this.state.attestationID, this.state.attestationRequestedVdxfKeys) + return reply; + } + + + updateDisplay = async () => { + const { deeplinkData } = this.props.route.params + const loginConsent = new primitives.LoginConsentRequest(deeplinkData); + + const subjectKeys = {}; + let attestationName = ""; + let attestationAcceptedAttestors = []; + let attestationID = ""; + + const attestationDataURL = loginConsent.challenge.subject + .filter((permission) => permission.vdxfkey === primitives.LOGIN_CONSENT_PERSONALINFO_WEBHOOK_VDXF_KEY.vdxfid); + + for (let i = 0; i < loginConsent.challenge.subject.length; i++) { + + let permission = loginConsent.challenge.subject[i]; + + if (permission.vdxfkey == primitives.ATTESTATION_VIEW_REQUEST_NAME.vdxfid) { + attestationName = permission.data; + } else if (permission.vdxfkey == primitives.ATTESTATION_VIEW_REQUEST_ATTESTOR.vdxfid) { + let attestorFqn; + + try { + const reply = await getIdentity(loginConsent.system_id, permission.data); + attestorFqn = reply.result.friendlyname; + } catch (e) { + console.log("Error getting attestation signer ", e); + return false; + } + attestationAcceptedAttestors.push(attestorFqn); + + } + else { + subjectKeys[permission.data] = true; + } + }; + + let attestationData; + try { + attestationData = await requestAttestationData(ATTESTATIONS_PROVISIONED); + } catch (e) { + createAlert('Error Loading Attestations', e.message); + } + + // check that the requested attestestation name is in the attestationData + + let attestationRequestedFields = []; + const attestationDataKeys = Object.keys(attestationData); + const attestationDataValues = Object.values(attestationData); + let attestationRequestedVdxfKeys = loginConsent.challenge.subject + .map((permission) => permission.data); + + for (let i = 0; i < attestationDataKeys.length; i++) { + + for (let j = 0; j < attestationDataValues[i].data.length; j++) { + + const vdxfobjKey = Object.keys(attestationDataValues[i].data[j])[0]; + const vdxfobjValue = attestationDataValues[i].data[j][vdxfobjKey]; + + if (vdxfobjKey === VDXF_Data.MMRDescriptorKey().vdxfid) { + + for (let k = 0; k < vdxfobjValue.datadescriptors.length; k++) { + + const item = vdxfobjValue.datadescriptors[k].objectdata[VDXF_Data.DataDescriptorKey().vdxfid]; + + if (item.label === primitives.ATTESTATION_NAME.vdxfid) { + if (item.objectdata.message !== attestationName) { + continue; + } + } else if (item.label === primitives.ATTESTATION_VIEW_REQUEST_ATTESTOR.vdxfid) { + if (attestationAcceptedAttestors.indexOf(item.objectdata.message) === -1) { + continue; + } + } else if (subjectKeys[item.label]) { + let name; + + if (name = IdentityVdxfidMap[item.label]?.name) { + attestationRequestedFields.push(name); + } else { + attestationRequestedFields.push(item.label); + } + } + } + if (attestationRequestedFields && attestationAcceptedAttestors) { + attestationID = attestationDataKeys[0]; + } + } + } + } + this.setState({ attestationRequestedFields, + attestationAcceptedAttestors, + attestationName, + attestationID, + attestationDataURL: { vdxfkey: attestationDataURL[0].vdxfkey, uri: attestationDataURL[0].data }, + attestationRequestedVdxfKeys }); + } + + async handleContinue() { + this.setState({ loading: true }); + + const createdAttestation = await this.createAttestationReply() + + createAlert( + "Send Selected Attestation info", + "Are you sure you want to send your attestation data to: \n" + `${this.state.signerFqn}`, + [ + { + text: "No", + onPress: () => { + resolveAlert(); + }, + }, + { + text: "Yes", + onPress: () => { + resolveAlert(); + handleAttestationDataSend(createdAttestation, this.state.attestationDataURL) + .then(() => { + this.setState({ loading: false }); + this.props.route.params.onGoBack(true); + this.props.navigation.goBack(); + this.cancel(); + }).catch((e) => { + + this.setState({ loading: false }); + createAlert("Error, Failed to Send Attestation, server may be unavailable.", e.message); + + }) + }, + }, + ], + { + cancelable: false, + } + ); + + + }; + + render() { + return LoginShareAttestationRender.call(this); + } +} + +const mapStateToProps = (state) => { + return { + activeAccount: state.authentication.activeAccount, + encryptedPersonalData: state.personal + } +}; + +export default connect(mapStateToProps)(LoginShareAttestation); \ No newline at end of file diff --git a/src/containers/DeepLink/LoginShareAttestation/LoginShareAttestation.render.js b/src/containers/DeepLink/LoginShareAttestation/LoginShareAttestation.render.js new file mode 100644 index 00000000..0253aa2d --- /dev/null +++ b/src/containers/DeepLink/LoginShareAttestation/LoginShareAttestation.render.js @@ -0,0 +1,68 @@ +import React from "react"; +import { SafeAreaView, ScrollView, View } from "react-native"; +import { Divider, List, Button, Text } from "react-native-paper"; + +import Styles from "../../../styles"; + +import Colors from '../../../globals/colors'; + +export const LoginShareAttestationRender = function (props) { + + return ( + + + + + + Agree to share the following attestation data. + + + {`Attestation Name: `}{`${this.state.attestationName}`} + + {this.state.attestationAcceptedAttestors && + {`From: `}{`${this.state.attestationAcceptedAttestors[0]}`} + } + + + {this.state.attestationRequestedFields.map(request => { + return ( + + + + + + ); + })} + + + + + + + + + + ); +}; diff --git a/src/containers/RootStack/DeepLinkStackScreens/DeepLinkStackScreens.js b/src/containers/RootStack/DeepLinkStackScreens/DeepLinkStackScreens.js index ba899fa9..f87ab756 100644 --- a/src/containers/RootStack/DeepLinkStackScreens/DeepLinkStackScreens.js +++ b/src/containers/RootStack/DeepLinkStackScreens/DeepLinkStackScreens.js @@ -8,6 +8,8 @@ import InvoicePaymentConfiguration from '../../DeepLink/InvoicePaymentConfigurat import PersonalSelectData from '../../DeepLink/PersonalSelectData/PersonalSelectData'; import ProfileStackScreens from '../ProfileStackScreens/ProfileStackScreens'; import LoginReceiveAttestation from '../../DeepLink/LoginReceiveAttestation/LoginReceiveAttestation'; +import LoginShareAttestation from '../../DeepLink/LoginShareAttestation/LoginShareAttestation'; + const DeepLinkStack = createStackNavigator(); const DeepLinkStackScreens = props => { @@ -66,6 +68,14 @@ const DeepLinkStackScreens = props => { title: "Receive Attestation" }} /> + null, + title: "Share Attestation data" + }} + /> ); }; diff --git a/src/containers/RootStack/ServicesStackScreens/ServicesStackScreens.js b/src/containers/RootStack/ServicesStackScreens/ServicesStackScreens.js index c5005baa..b6c4056c 100644 --- a/src/containers/RootStack/ServicesStackScreens/ServicesStackScreens.js +++ b/src/containers/RootStack/ServicesStackScreens/ServicesStackScreens.js @@ -6,7 +6,7 @@ import Service from '../../Services/Service/Service' import WyreServiceAccountData from '../../Services/ServiceComponents/WyreService/WyreServiceAccount/WyreServiceAccountData/WyreServiceAccountData'; import WyreServiceAddPaymentMethod from '../../Services/ServiceComponents/WyreService/WyreServiceAccount/WyreServiceAddPaymentMethod/WyreServiceAddPaymentMethod'; import WyreServiceEditPaymentMethod from '../../Services/ServiceComponents/WyreService/WyreServiceAccount/WyreServiceEditPaymentMethod/WyreServiceEditPaymentMethod'; - +import ViewAttestation from '../../Services/ServiceComponents/AttestationService/ViewAttestation/ViewAttestation'; const ServicesStack = createStackNavigator(); const ServicesStackScreens = props => { @@ -43,6 +43,13 @@ const ServicesStackScreens = props => { title: "Edit Account", }} /> + ); }; diff --git a/src/containers/Services/Service/Service.js b/src/containers/Services/Service/Service.js index 83593652..6b942ded 100644 --- a/src/containers/Services/Service/Service.js +++ b/src/containers/Services/Service/Service.js @@ -3,11 +3,11 @@ */ import React, { Component } from "react" -import { PBAAS_PRECONVERT_SERVICE_ID, VERUSID_SERVICE_ID, WYRE_SERVICE_ID } from "../../../utils/constants/services"; +import { PBAAS_PRECONVERT_SERVICE_ID, VERUSID_SERVICE_ID, WYRE_SERVICE_ID, ATTESTATION_SERVICE_ID } from "../../../utils/constants/services"; import VerusIdService from "../ServiceComponents/VerusIdService/VerusIdService"; import WyreService from "../ServiceComponents/WyreService/WyreService"; import PbaasPreconvertService from "../ServiceComponents/PbaasPreconvertService/PbaasPreconvertService"; - +import AttestationService from "../ServiceComponents/AttestationService/AttestationService"; class Service extends Component { constructor(props) { super(props); @@ -18,7 +18,8 @@ class Service extends Component { this.SERVICE_COMPONENTS = { [WYRE_SERVICE_ID]: , [VERUSID_SERVICE_ID]: , - [PBAAS_PRECONVERT_SERVICE_ID]: + [PBAAS_PRECONVERT_SERVICE_ID]: , + [ATTESTATION_SERVICE_ID]: , } } diff --git a/src/containers/Services/ServiceComponents/AttestationService/AttestationService.js b/src/containers/Services/ServiceComponents/AttestationService/AttestationService.js new file mode 100644 index 00000000..d147de65 --- /dev/null +++ b/src/containers/Services/ServiceComponents/AttestationService/AttestationService.js @@ -0,0 +1,66 @@ +import React, { Component } from "react" +import { connect } from 'react-redux' +import { setServiceLoading } from "../../../../actions/actionCreators"; +import { createAlert } from "../../../../actions/actions/alert/dispatchers/alert"; +import { requestServiceStoredData } from "../../../../utils/auth/authBox"; +import { ATTESTATION_SERVICE_ID } from "../../../../utils/constants/services"; +import { VerusAttestationRender } from "./AttestationService.render"; +import { ATTESTATIONS_PROVISIONED } from "../../../../utils/constants/attestations"; +import { requestAttestationData } from "../../../../utils/auth/authBox"; + +class AttestationService extends Component { + constructor(props) { + super(props); + this.state = { + attestations: {}, + }; + this.props.navigation.setOptions({title: 'Attestations'}); + const sdf=234 + } + + async getAttestations() { + this.props.dispatch(setServiceLoading(true, ATTESTATION_SERVICE_ID)); + + try { + const attestationData = await requestAttestationData(ATTESTATIONS_PROVISIONED); + if (attestationData) { + this.setState({ + attestations: attestationData + }); + } + } catch (e) { + createAlert('Error Loading Attestations', e.message); + } + + this.props.dispatch(setServiceLoading(false, ATTESTATION_SERVICE_ID)); + } + + componentDidMount() { + + this.getAttestations(); + } + + viewDetails = (attestation) => { + this.props.navigation.navigate("ViewAttestation", {attestation}); + } + + componentDidUpdate(lastProps) { + // if (lastProps.encryptedIds !== this.props.encryptedIds) { + // this.getLinkedIds() + // } + } + + render() { + return VerusAttestationRender.call(this); + } +} + +const mapStateToProps = state => { + + return { + loading: state.services.loading[ATTESTATION_SERVICE_ID], + attestestationdata: state.attestation + }; +}; + +export default connect(mapStateToProps)(AttestationService); \ No newline at end of file diff --git a/src/containers/Services/ServiceComponents/AttestationService/AttestationService.render.js b/src/containers/Services/ServiceComponents/AttestationService/AttestationService.render.js new file mode 100644 index 00000000..7934faf1 --- /dev/null +++ b/src/containers/Services/ServiceComponents/AttestationService/AttestationService.render.js @@ -0,0 +1,54 @@ +import React from "react"; +import { View, Text } from 'react-native' +import styles from "../../../../styles"; +import AnimatedActivityIndicator from "../../../../components/AnimatedActivityIndicator"; +import {Divider, List} from 'react-native-paper'; + +export const VerusAttestationRender = function () { + return ( + + {(this.props.loading) && ( + + + + )} + {!this.props.loading && ( + + {Object.values(this.state.attestations || {}).length === 0 && ( + No attestations present + )} + {Object.values(this.state.attestations || {}).map((attestation, index) => { + + return ( + + this.viewDetails(attestation)} + right={props => ( + + )} + /> + + + ); + + + })} + + )} + + ); +}; diff --git a/src/containers/Services/ServiceComponents/AttestationService/ViewAttestation/ViewAttestation.js b/src/containers/Services/ServiceComponents/AttestationService/ViewAttestation/ViewAttestation.js new file mode 100644 index 00000000..257fec15 --- /dev/null +++ b/src/containers/Services/ServiceComponents/AttestationService/ViewAttestation/ViewAttestation.js @@ -0,0 +1,104 @@ +import React, { Component } from "react" +import { connect } from 'react-redux' + +import { primitives } from "verusid-ts-client" + +import * as VDXF_Data from "verus-typescript-primitives/dist/vdxf/vdxfDataKeys"; + +const { ATTESTATION_NAME } = primitives; +import { IdentityVdxfidMap } from "verus-typescript-primitives/dist/vdxf/classes/IdentityData"; +import { SafeAreaView, ScrollView, View, Image } from 'react-native' + +import { Divider, List, Button, Text } from 'react-native-paper'; +import Styles from "../../../../../styles"; +import Colors from '../../../../../globals/colors'; + +class ViewAttestation extends Component { + constructor(props) { + super(props); + this.state = { + attestationData: {}, + signer: "" + }; + } + + componentDidMount() { + this.updateDisplay(); + } + + getAttestationData = (dataDescriptors) => { + + const data = {}; + dataDescriptors.forEach((dataDescriptor) => { + const label = dataDescriptor.objectdata[Object.keys(dataDescriptor.objectdata)[0]].label; + let key = ""; + + if (label === ATTESTATION_NAME.vdxfid) { + key = `Attestation name` + } else { + key = IdentityVdxfidMap[label]?.name || label; + } + + const mime = dataDescriptor.objectdata[Object.keys(dataDescriptor.objectdata)[0]].mimetype || ""; + if (mime.startsWith("text/")) { + data[key] = { "message": dataDescriptor.objectdata[Object.keys(dataDescriptor.objectdata)[0]].objectdata.message }; + } else if (mime.startsWith("image/")) { + if (mime === "image/jpeg" || mime === "image/png") { + data[key] = { "image": `data:${mime};base64,${Buffer.from(dataDescriptor.objectdata[Object.keys(dataDescriptor.objectdata)[0]].objectdata, "hex").toString("base64")}` }; + } + } + }); + + return data; + + } + + updateDisplay() { + const { attestation } = this.props.route.params + + const signatureData = attestation.data.find((dataDescriptor) => Object.keys(dataDescriptor)[0] === VDXF_Data.SignatureDataKey().vdxfid)[VDXF_Data.SignatureDataKey().vdxfid]; + const mmrData = attestation.data.find((dataDescriptor) => Object.keys(dataDescriptor)[0] === VDXF_Data.MMRDescriptorKey().vdxfid)[VDXF_Data.MMRDescriptorKey().vdxfid]; + + const containingData = this.getAttestationData(mmrData.datadescriptors); + this.setState({ attestationData: containingData, signer:attestation.signer }); + + } + + render() { + return ( + + + + + + {`From: `}{`${this.state.signer}`} + + {this.state.attestationData && Object.keys(this.state.attestationData).map(request => { + return ( + + this.state.attestationData[request]?.image ? : null} + /> + + + ); + })} + + + ) + } +} + +const mapStateToProps = (state) => { + return { + activeAccount: state.authentication.activeAccount, + encryptedPersonalData: state.personal + } +}; + +export default connect(mapStateToProps)(ViewAttestation); \ No newline at end of file diff --git a/src/images/servicelogo/index.js b/src/images/servicelogo/index.js index 0becf58f..adba8100 100644 --- a/src/images/servicelogo/index.js +++ b/src/images/servicelogo/index.js @@ -1,8 +1,9 @@ -import { VERUSID_SERVICE_ID, WYRE_SERVICE_ID } from '../../utils/constants/services' +import { VERUSID_SERVICE_ID, WYRE_SERVICE_ID, ATTESTATION_SERVICE_ID } from '../../utils/constants/services' import WYRE_LIGHT from './wyre/wyre_light.svg' import VERUSID_LIGHT from './verusid/verusid_light.svg' export default { [WYRE_SERVICE_ID]: { light: WYRE_LIGHT }, - [VERUSID_SERVICE_ID]: { light: VERUSID_LIGHT } + [VERUSID_SERVICE_ID]: { light: VERUSID_LIGHT }, + [ATTESTATION_SERVICE_ID]: { light: VERUSID_LIGHT }, } \ No newline at end of file diff --git a/src/reducers/services.js b/src/reducers/services.js index d637c2e0..ded58121 100644 --- a/src/reducers/services.js +++ b/src/reducers/services.js @@ -2,7 +2,7 @@ The personal reducer stores data used by services */ -import { VERUSID_SERVICE_ID, WYRE_SERVICE_ID, PBAAS_PRECONVERT_SERVICE_ID } from "../utils/constants/services"; +import { VERUSID_SERVICE_ID, WYRE_SERVICE_ID, PBAAS_PRECONVERT_SERVICE_ID, ATTESTATION_SERVICE_ID } from "../utils/constants/services"; import { SET_SERVICE_ACCOUNT, SIGN_OUT, @@ -24,7 +24,8 @@ export const services = ( loading: { [WYRE_SERVICE_ID]: false, [VERUSID_SERVICE_ID]: false, - [PBAAS_PRECONVERT_SERVICE_ID]: false + [PBAAS_PRECONVERT_SERVICE_ID]: false, + [ATTESTATION_SERVICE_ID]: false }, }, action, @@ -94,7 +95,8 @@ export const services = ( loading: { [WYRE_SERVICE_ID]: false, [VERUSID_SERVICE_ID]: false, - [PBAAS_PRECONVERT_SERVICE_ID]: false + [PBAAS_PRECONVERT_SERVICE_ID]: false, + [ATTESTATION_SERVICE_ID]: false }, }; default: diff --git a/src/utils/asyncStore/serviceStoredDataStorage.js b/src/utils/asyncStore/serviceStoredDataStorage.js index 09c51e13..2850d246 100644 --- a/src/utils/asyncStore/serviceStoredDataStorage.js +++ b/src/utils/asyncStore/serviceStoredDataStorage.js @@ -1,6 +1,6 @@ import AsyncStorage from "@react-native-async-storage/async-storage"; import { SERVICE_STORAGE_INTERNAL_KEY } from "../../../env/index"; -import { VERUSID_SERVICE_ID, WYRE_SERVICE_ID } from "../constants/services"; +import { VERUSID_SERVICE_ID, WYRE_SERVICE_ID, ATTESTATION_SERVICE_ID } from "../constants/services"; export const storeServiceStoredData = (data) => { if (typeof data !== "object") diff --git a/src/utils/attestations/createAttestationResponse.js b/src/utils/attestations/createAttestationResponse.js new file mode 100644 index 00000000..55382eb0 --- /dev/null +++ b/src/utils/attestations/createAttestationResponse.js @@ -0,0 +1,37 @@ +import { requestAttestationData } from "../../utils/auth/authBox"; +import { ATTESTATIONS_PROVISIONED } from "../../utils/constants/attestations"; +import * as VDXF_Data from "verus-typescript-primitives/dist/vdxf/vdxfDataKeys"; + +export const createAttestationResponse = async (attestationID, requiredKeys) => { + + const attestationData = await requestAttestationData(ATTESTATIONS_PROVISIONED); + + if (!attestationData[attestationID]) { + throw new Error(`Attestation not found in attestation data`); + } + + const attestation = attestationData[attestationID]; + const attestataionKeysToRemove = []; + const indexedDatadescriptor = {} + + for (let i = 0; i < attestation.data.length; i++) { + if (Object.keys(attestation.data[i])[0] === VDXF_Data.MMRDescriptorKey().vdxfid) { + + for (let j = 0; j < attestation.data[i][VDXF_Data.MMRDescriptorKey().vdxfid].datadescriptors.length; j++) { + + const item = attestation.data[i][VDXF_Data.MMRDescriptorKey().vdxfid].datadescriptors[j].objectdata[VDXF_Data.DataDescriptorKey().vdxfid]; + + if (requiredKeys.indexOf(item.label) === -1) { + attestataionKeysToRemove.push(j) + } else { + indexedDatadescriptor[j] = attestation.data[i][VDXF_Data.MMRDescriptorKey().vdxfid].datadescriptors[j]; + } + + } + delete attestation.data[i][VDXF_Data.MMRDescriptorKey().vdxfid].datadescriptors + attestation.data[i][VDXF_Data.MMRDescriptorKey().vdxfid].datadescriptors = indexedDatadescriptor + } + }; + + return attestation +} \ No newline at end of file diff --git a/src/utils/auth/authBox.js b/src/utils/auth/authBox.js index 037c8698..3586e03e 100644 --- a/src/utils/auth/authBox.js +++ b/src/utils/auth/authBox.js @@ -176,4 +176,29 @@ export const requestServiceStoredData = async (service) => { throw new Error("Unable to decrypt service stored data for " + service); } } -}; \ No newline at end of file +}; + +export const requestAttestationData = async (dataType) => { + const state = store.getState() + + if ( + state.authentication.activeAccount == null + ) { + throw new Error("You must be signed in to retrieve attestation data"); + } else if (state.attestation[dataType] == null) { + return {} + } else { + const password = await requestPassword() + const data = decryptkey(password, state.attestation[dataType]) + + if (data !== false) { + try { + return JSON.parse(data) + } catch(e) { + throw new Error("Unable to parse attestation data") + } + } else { + throw new Error("Unable to decrypt attestation data"); + } + } +} \ No newline at end of file diff --git a/src/utils/constants/services.js b/src/utils/constants/services.js index ba1900b1..40177952 100644 --- a/src/utils/constants/services.js +++ b/src/utils/constants/services.js @@ -3,6 +3,7 @@ import { WYRE_SERVICE } from "./intervalConstants" export const WYRE_SERVICE_ID = 'wyre_service' export const VERUSID_SERVICE_ID = 'verusid_service' export const PBAAS_PRECONVERT_SERVICE_ID = 'pbaas_preconvert' +export const ATTESTATION_SERVICE_ID = 'attestation_service' export const CONNECTED_SERVICE_DISPLAY_INFO = { [WYRE_SERVICE_ID]: { @@ -18,6 +19,11 @@ export const CONNECTED_SERVICE_DISPLAY_INFO = { title: "Preconvert Currency", description: "Participate in a PBaaS currency launch by sending your funds to the currency before it starts", decentralized: true + }, + [ATTESTATION_SERVICE_ID]: { + title: "Attestations", + description: "Stored attestations that can be used to prove information about you to services you use", + decentralized: true } } @@ -25,7 +31,7 @@ export const CONNECTED_SERVICE_CHANNELS = { [WYRE_SERVICE_ID]: WYRE_SERVICE } -export const CONNECTED_SERVICES = [VERUSID_SERVICE_ID, /*PBAAS_PRECONVERT_SERVICE_ID ,*/ WYRE_SERVICE_ID] +export const CONNECTED_SERVICES = [VERUSID_SERVICE_ID, /*PBAAS_PRECONVERT_SERVICE_ID ,*/ WYRE_SERVICE_ID, ATTESTATION_SERVICE_ID] // Wyre specific constants export const WYRE_INDIVIDUAL_NAME = 'individualLegalName' diff --git a/src/utils/deeplink/handlePersonalDataSend.js b/src/utils/deeplink/handlePersonalDataSend.js index e67a76e6..404a7cd0 100644 --- a/src/utils/deeplink/handlePersonalDataSend.js +++ b/src/utils/deeplink/handlePersonalDataSend.js @@ -3,6 +3,12 @@ import * as primitives from "verus-typescript-primitives" const handlers = { + [primitives.LOGIN_CONSENT_PERSONALINFO_WEBHOOK_VDXF_KEY.vdxfid]: async (uri, response) => { + return await axios.post( + uri, + response + ); + }, [primitives.LOGIN_CONSENT_PERSONALINFO_WEBHOOK_VDXF_KEY.vdxfid]: async (uri, response) => { return await axios.post( uri, @@ -14,5 +20,11 @@ const handlers = { export const handlePersonalDataSend = (response, redirectinfo) => { const { vdxfkey, uri } = redirectinfo + return handlers[vdxfkey] == null ? null : handlers[vdxfkey](uri, response); +} + +export const handleAttestationDataSend = (response, redirectinfo) => { + const { vdxfkey, uri } = redirectinfo + return handlers[vdxfkey] == null ? null : handlers[vdxfkey](uri, response); } \ No newline at end of file diff --git a/src/utils/nativeStore/attestationDataStorage.js b/src/utils/nativeStore/attestationDataStorage.js index 1296e5db..9847bb10 100644 --- a/src/utils/nativeStore/attestationDataStorage.js +++ b/src/utils/nativeStore/attestationDataStorage.js @@ -63,7 +63,7 @@ export const loadAttestationDataForUser = async (accountHash) => { if (allAttestationData[accountHash] == null) return { - attestations: null + attestations_provisioned: null }; else return allAttestationData[accountHash]; }; diff --git a/yarn.lock b/yarn.lock index 3c6eabd7..4bd255bf 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3175,11 +3175,6 @@ bech32@2.0.0: resolved "https://registry.yarnpkg.com/bech32/-/bech32-2.0.0.tgz#078d3686535075c8c79709f054b1b226a133b355" integrity sha512-LcknSilhIGatDAsY1ak2I8VtGaHNhgMSYVxFrGLXv+xLHytaKZKcaUJJUE7qmBr7h33o5YQwP55pMI0xmkpJwg== -bech32@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/bech32/-/bech32-2.0.0.tgz#078d3686535075c8c79709f054b1b226a133b355" - integrity sha512-LcknSilhIGatDAsY1ak2I8VtGaHNhgMSYVxFrGLXv+xLHytaKZKcaUJJUE7qmBr7h33o5YQwP55pMI0xmkpJwg== - bech32@^1.1.2, bech32@^1.1.3: version "1.1.4" resolved "https://registry.yarnpkg.com/bech32/-/bech32-1.1.4.tgz#e38c9f37bf179b8eb16ae3a772b40c356d4832e9" @@ -11587,7 +11582,6 @@ version_compare@0.0.3: "verusd-rpc-ts-client@https://github.com/VerusCoin/verusd-rpc-ts-client.git": version "0.1.0" - uid "62d260563c23ecf9b3333355e8596191c764e415" resolved "https://github.com/VerusCoin/verusd-rpc-ts-client.git#62d260563c23ecf9b3333355e8596191c764e415" dependencies: axios "1.6.5"