From e26ed0e81abe9f7fb1cb39fee38ee142f3a7b0dc Mon Sep 17 00:00:00 2001 From: Jamie Hewitt Date: Thu, 10 Jun 2021 12:06:26 +0300 Subject: [PATCH] Issue 582 market rbac integration (#597) * adding env for RBAC server url to app.config.js * creating util function for requesting auth from the rbac server * fixing typing error * testing rbac request on homepage * removing console logs * importing RBAC url from config file * creating develpment .env file * return true if no rbac url env is set * creating permissions parent component * wrapping homepage content in permission element * wrapping publish in permissions wrapper * wrapping search results in permissions wrapper * wrapping asset actions in permissions element * creating an error alert for permission denied * updating react hook dependency * passing address to rbac component * sedning address to RBAC server * wrapping asset in permission component * removing unused import of Permission component * sending request based on address * chaning default permission case to restrict access * updating eventType as consume * Adding loader icon while waiting form permission response * only sending request to RBAC if address is defined * adding wallet connection info message * changing the env name and checking for undefined * updating .env.development * Check for undefined RBAC_URL in permissions component * removing .env.development and updating .env.example * updating .env.example comment * switching alert messages and reducing return statements * removing console.log message * fixing linting issue * Revert "fixing linting issue" This reverts commit 8bcb80be3d1ae32731b8c5b81b393dd614017fdc. * Fixing linting errors * pull from origin main * Revert "pull from origin main" This reverts commit 9535e41a5f5acfa26d2841942c29695855dd65bc. --- .env.example | 3 + app.config.js | 1 + package-lock.json | 5 + .../organisms/AssetActions/index.tsx | 7 +- src/components/organisms/Permission.tsx | 62 +++++++ src/components/pages/Home.tsx | 52 +++--- src/components/pages/Publish/index.tsx | 157 +++++++++--------- src/components/templates/Page.tsx | 1 - src/components/templates/Search/index.tsx | 57 ++++--- src/pages/asset/index.tsx | 9 +- src/utils/rbac.ts | 34 ++++ 11 files changed, 257 insertions(+), 131 deletions(-) create mode 100644 src/components/organisms/Permission.tsx create mode 100644 src/utils/rbac.ts diff --git a/.env.example b/.env.example index cb5c39b35..12eac2c37 100644 --- a/.env.example +++ b/.env.example @@ -2,6 +2,9 @@ # "development", "ropsten", "rinkeby", "mainnet", "polygon", "moonbeamalpha" GATSBY_NETWORK="rinkeby" +## Define a GATSBY_RBAC_URL to implement permission based restrictions +#GATSBY_RBAC_URL="http://localhost:3000" + #GATSBY_INFURA_PROJECT_ID="xxx" #GATSBY_MARKET_FEE_ADDRESS="0xxx" #GATSBY_ANALYTICS_ID="xxx" diff --git a/app.config.js b/app.config.js index 722d0239d..ba3a87393 100644 --- a/app.config.js +++ b/app.config.js @@ -4,6 +4,7 @@ module.exports = { // networks in their wallet. // Ocean Protocol contracts are deployed for: 'mainnet', 'rinkeby', 'development' network: process.env.GATSBY_NETWORK || 'mainnet', + rbacUrl: process.env.GATSBY_RBAC_URL, infuraProjectId: process.env.GATSBY_INFURA_PROJECT_ID || 'xxx', diff --git a/package-lock.json b/package-lock.json index 95f89b505..5b1b1b9f0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -65003,6 +65003,11 @@ "clsx": "^1.1.1" } }, + "reactjs-popup": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/reactjs-popup/-/reactjs-popup-2.0.4.tgz", + "integrity": "sha512-G5jTXL2JkClKAYAdqedf+K9QvbNsFWvdbrXW1/vWiyanuCU/d7DtQzQux+uKOz2HeNVRsFQHvs7abs0Z7VLAhg==" + }, "read": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz", diff --git a/src/components/organisms/AssetActions/index.tsx b/src/components/organisms/AssetActions/index.tsx index 290dd6d4e..03c916354 100644 --- a/src/components/organisms/AssetActions/index.tsx +++ b/src/components/organisms/AssetActions/index.tsx @@ -1,4 +1,5 @@ import React, { ReactElement, useState, useEffect } from 'react' +import Permission from '../Permission' import styles from './index.module.css' import Compute from './Compute' import Consume from './Consume' @@ -111,5 +112,9 @@ export default function AssetActions(): ReactElement { } ) - return + return ( + + + + ) } diff --git a/src/components/organisms/Permission.tsx b/src/components/organisms/Permission.tsx new file mode 100644 index 000000000..022e1cd51 --- /dev/null +++ b/src/components/organisms/Permission.tsx @@ -0,0 +1,62 @@ +import React, { ReactElement, useEffect, useState } from 'react' +import { useWeb3 } from '../../providers/Web3' +import rbacRequest from '../../utils/rbac' +import Alert from '../atoms/Alert' +import Loader from '../atoms/Loader' +import appConfig from '../../../app.config' + +export default function Permission({ + eventType, + children +}: { + eventType: string + children: ReactElement +}): ReactElement { + const url = appConfig.rbacUrl + const [data, updateData] = useState() + const [errorMessage, updateError] = useState() + const [messageState, updateMessageState] = + useState<'error' | 'warning' | 'info' | 'success'>() + const { accountId } = useWeb3() + useEffect(() => { + if (url === undefined) return + const getData = async () => { + if (accountId === undefined) { + updateError('Please make sure your wallet is connected to proceed.') + updateMessageState('info') + } else { + const data = await rbacRequest(eventType, accountId) + updateData(data) + if (data === 'ERROR') { + updateError( + 'There was an error verifying your permissions. Please refresh the page or conntact your network administrator' + ) + updateMessageState('error') + } else if (data === false) { + updateError( + `Sorry, you don't have permission to ${eventType}. Please make sure you have connected your registered address.` + ) + updateMessageState('warning') + } else if (data !== true) { + updateError( + 'An unkown error occured. Please conntact your network administrator' + ) + updateMessageState('error') + } + } + } + getData() + }, [eventType, accountId, url]) + + if (url === undefined || data === true) { + return <>{children} + } + + return ( + <> + +
+ + + ) +} diff --git a/src/components/pages/Home.tsx b/src/components/pages/Home.tsx index f3a61d35f..8e62e4b76 100644 --- a/src/components/pages/Home.tsx +++ b/src/components/pages/Home.tsx @@ -12,8 +12,10 @@ import Button from '../atoms/Button' import Bookmarks from '../molecules/Bookmarks' import axios from 'axios' import { queryMetadata } from '../../utils/aquarius' +import Permission from '../organisms/Permission' import { getHighestLiquidityDIDs } from '../../utils/subgraph' import { DDO, Logger } from '@oceanprotocol/lib' + import { useWeb3 } from '../../providers/Web3' const queryLatest = { @@ -121,33 +123,35 @@ export default function HomePage(): ReactElement { }, [config.subgraphUri, loading, web3Loading]) return ( - <> - - - + + <> + + + -
-

Bookmarks

- -
+
+

Bookmarks

+ +
+ + {queryAndDids && ( + + )} - {queryAndDids && ( + All data sets and algorithms → + + } /> - )} - - - All data sets and algorithms → - - } - /> - + +
) } diff --git a/src/components/pages/Publish/index.tsx b/src/components/pages/Publish/index.tsx index 31dce4427..aaaaca3f4 100644 --- a/src/components/pages/Publish/index.tsx +++ b/src/components/pages/Publish/index.tsx @@ -1,4 +1,5 @@ import React, { ReactElement, useState, useEffect } from 'react' +import Permission from '../../organisms/Permission' import { Formik, FormikState } from 'formik' import { usePublish } from '../../../hooks/usePublish' import styles from './index.module.css' @@ -215,86 +216,92 @@ export default function PublishPage({ } return isInPurgatory && purgatoryData ? null : ( - { - // move user's focus to top of screen - window.scrollTo({ top: 0, left: 0, behavior: 'smooth' }) - // kick off publishing - publishType === 'dataset' - ? await handleSubmit(values, resetForm) - : await handleAlgorithmSubmit(values, resetForm) - }} - enableReinitialize - > - {({ values }) => { - const tabs = [ - { - title: 'Data Set', - content: - }, - { - title: 'Algorithm', - content: - } - ] + + { + // move user's focus to top of screen + window.scrollTo({ top: 0, left: 0, behavior: 'smooth' }) + // kick off publishing + publishType === 'dataset' + ? await handleSubmit(values, resetForm) + : await handleAlgorithmSubmit(values, resetForm) + }} + enableReinitialize + > + {({ values }) => { + const tabs = [ + { + title: 'Data Set', + content: + }, + { + title: 'Algorithm', + content: + } + ] - return ( - <> - - - {hasFeedback ? ( - + - ) : ( - <> - - { - setPublishType(title.toLowerCase().replace(' ', '') as any) - title === 'Algorithm' - ? setdatasetInitialValues(values) - : setAlgoInitialValues(values) + {hasFeedback ? ( + - - )} + ) : ( + <> + - {debug === true && } - - ) - }} - + { + setPublishType( + title.toLowerCase().replace(' ', '') as any + ) + title === 'Algorithm' + ? setdatasetInitialValues(values) + : setAlgoInitialValues(values) + }} + /> + + )} + + {debug === true && } + + ) + }} + + ) } diff --git a/src/components/templates/Page.tsx b/src/components/templates/Page.tsx index 12a5a3c00..1672d4dbf 100644 --- a/src/components/templates/Page.tsx +++ b/src/components/templates/Page.tsx @@ -23,7 +23,6 @@ export default function Page({ return ( <> - {title && !noPageHeader && ( -
- {(text || owner) && ( - - )} -
- - + <> +
+ {(text || owner) && ( + + )} +
+ + +
+
+
+
-
-
- -
- + + ) } diff --git a/src/pages/asset/index.tsx b/src/pages/asset/index.tsx index 61d335698..025602a3c 100644 --- a/src/pages/asset/index.tsx +++ b/src/pages/asset/index.tsx @@ -1,4 +1,5 @@ import React, { ReactElement, useEffect, useState } from 'react' +import Permission from '../../components/organisms/Permission' import { PageProps } from 'gatsby' import PageTemplateAssetDetails from '../../components/templates/PageAssetDetails' import AssetProvider from '../../providers/Asset' @@ -11,8 +12,10 @@ export default function PageGatsbyAssetDetails(props: PageProps): ReactElement { }, [props.location.pathname]) return ( - - - + + + + + ) } diff --git a/src/utils/rbac.ts b/src/utils/rbac.ts new file mode 100644 index 000000000..80b08e4f5 --- /dev/null +++ b/src/utils/rbac.ts @@ -0,0 +1,34 @@ +import fetch from 'cross-fetch' +import appConfig from '../../app.config' + +export default async function rbacRequest( + eventType: string, + address: string +): Promise { + const url = appConfig.rbacUrl + if (url === undefined) { + return true + } else { + const data = { + component: 'market', + eventType, + authService: 'address', + credentials: { + address + } + } + try { + const response = await fetch(url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(data) + }) + return await response.json() + } catch (error) { + console.error('Error parsing json: ' + error.message) + return 'ERROR' + } + } +}