1
0
mirror of https://github.com/oceanprotocol/market.git synced 2024-12-02 05:57:29 +01:00

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 8bcb80be3d.

* Fixing linting errors

* pull from origin main

* Revert "pull from origin main"

This reverts commit 9535e41a5f.
This commit is contained in:
Jamie Hewitt 2021-06-10 12:06:26 +03:00 committed by GitHub
parent b1d8a77895
commit e26ed0e81a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 257 additions and 131 deletions

View File

@ -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"

View File

@ -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',

5
package-lock.json generated
View File

@ -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",

View File

@ -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 <Tabs items={tabs} className={styles.actions} />
return (
<Permission eventType="consume">
<Tabs items={tabs} className={styles.actions} />
</Permission>
)
}

View File

@ -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<boolean | 'ERROR'>()
const [errorMessage, updateError] = useState<string>()
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 (
<>
<Alert text={errorMessage} state={messageState} />
<br />
<Loader />
</>
)
}

View File

@ -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 (
<>
<Container narrow className={styles.searchWrap}>
<SearchBar size="large" />
</Container>
<Permission eventType="browse">
<>
<Container narrow className={styles.searchWrap}>
<SearchBar size="large" />
</Container>
<section className={styles.section}>
<h3>Bookmarks</h3>
<Bookmarks />
</section>
<section className={styles.section}>
<h3>Bookmarks</h3>
<Bookmarks />
</section>
{queryAndDids && (
<SectionQueryResult
title="Highest Liquidity"
query={queryAndDids[0]}
queryData={queryAndDids[1]}
/>
)}
{queryAndDids && (
<SectionQueryResult
title="Highest Liquidity"
query={queryAndDids[0]}
queryData={queryAndDids[1]}
title="Recently Published"
query={queryLatest}
action={
<Button style="text" to="/search?sort=created&sortOrder=desc">
All data sets and algorithms
</Button>
}
/>
)}
<SectionQueryResult
title="Recently Published"
query={queryLatest}
action={
<Button style="text" to="/search?sort=created&sortOrder=desc">
All data sets and algorithms
</Button>
}
/>
</>
</>
</Permission>
)
}

View File

@ -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 : (
<Formik
initialValues={
publishType === 'dataset' ? datasetInitialValues : algoInitialValues
}
initialStatus="empty"
validationSchema={
publishType === 'dataset' ? validationSchema : validationSchemaAlgorithm
}
onSubmit={async (values, { resetForm }) => {
// 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: <TabContent values={values} publishType={publishType} />
},
{
title: 'Algorithm',
content: <TabContent values={values} publishType={publishType} />
}
]
<Permission eventType="publish">
<Formik
initialValues={
publishType === 'dataset' ? datasetInitialValues : algoInitialValues
}
initialStatus="empty"
validationSchema={
publishType === 'dataset'
? validationSchema
: validationSchemaAlgorithm
}
onSubmit={async (values, { resetForm }) => {
// 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: <TabContent values={values} publishType={publishType} />
},
{
title: 'Algorithm',
content: <TabContent values={values} publishType={publishType} />
}
]
return (
<>
<Persist
name={
publishType === 'dataset'
? formNameDatasets
: formNameAlgorithms
}
ignoreFields={['isSubmitting']}
/>
{hasFeedback ? (
<MetadataFeedback
title={title}
error={error}
success={success}
loading={publishStepText}
setError={setError}
successAction={{
name: `Go to ${
publishType === 'dataset' ? 'data set' : 'algorithm'
} `,
to: `/asset/${did}`
}}
return (
<>
<Persist
name={
publishType === 'dataset'
? formNameDatasets
: formNameAlgorithms
}
ignoreFields={['isSubmitting']}
/>
) : (
<>
<Alert
text={content.warning}
state="info"
className={styles.alert}
/>
<Tabs
className={styles.tabs}
items={tabs}
handleTabChange={(title) => {
setPublishType(title.toLowerCase().replace(' ', '') as any)
title === 'Algorithm'
? setdatasetInitialValues(values)
: setAlgoInitialValues(values)
{hasFeedback ? (
<MetadataFeedback
title={title}
error={error}
success={success}
loading={publishStepText}
setError={setError}
successAction={{
name: `Go to ${
publishType === 'dataset' ? 'data set' : 'algorithm'
} `,
to: `/asset/${did}`
}}
/>
</>
)}
) : (
<>
<Alert
text={content.warning}
state="info"
className={styles.alert}
/>
{debug === true && <Debug values={values} />}
</>
)
}}
</Formik>
<Tabs
className={styles.tabs}
items={tabs}
handleTabChange={(title) => {
setPublishType(
title.toLowerCase().replace(' ', '') as any
)
title === 'Algorithm'
? setdatasetInitialValues(values)
: setAlgoInitialValues(values)
}}
/>
</>
)}
{debug === true && <Debug values={values} />}
</>
)
}}
</Formik>
</Permission>
)
}

View File

@ -23,7 +23,6 @@ export default function Page({
return (
<>
<Seo title={title} description={description} uri={uri} />
<Container>
{title && !noPageHeader && (
<PageHeader

View File

@ -1,4 +1,5 @@
import React, { ReactElement, useState, useEffect } from 'react'
import Permission from '../../organisms/Permission'
import { QueryResult } from '@oceanprotocol/lib/dist/node/metadatacache/MetadataCache'
import SearchBar from '../../molecules/SearchBar'
import AssetList from '../../organisms/AssetList'
@ -63,34 +64,36 @@ export default function SearchPage({
}
return (
<>
<div className={styles.search}>
{(text || owner) && (
<SearchBar initialValue={(text || owner) as string} />
)}
<div className={styles.row}>
<ServiceFilter
serviceType={service}
setServiceType={setServiceType}
/>
<Sort
sortType={sortType}
sortDirection={sortDirection}
setSortType={setSortType}
setSortDirection={setSortDirection}
<Permission eventType="browse">
<>
<div className={styles.search}>
{(text || owner) && (
<SearchBar initialValue={(text || owner) as string} />
)}
<div className={styles.row}>
<ServiceFilter
serviceType={service}
setServiceType={setServiceType}
/>
<Sort
sortType={sortType}
sortDirection={sortDirection}
setSortType={setSortType}
setSortDirection={setSortDirection}
/>
</div>
</div>
<div className={styles.results}>
<AssetList
assets={queryResult?.results}
showPagination
isLoading={loading}
page={queryResult?.page}
totalPages={queryResult?.totalPages}
onPageChange={setPage}
/>
</div>
</div>
<div className={styles.results}>
<AssetList
assets={queryResult?.results}
showPagination
isLoading={loading}
page={queryResult?.page}
totalPages={queryResult?.totalPages}
onPageChange={setPage}
/>
</div>
</>
</>
</Permission>
)
}

View File

@ -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 (
<AssetProvider asset={did}>
<PageTemplateAssetDetails uri={props.location.pathname} />
</AssetProvider>
<Permission eventType="browse">
<AssetProvider asset={did}>
<PageTemplateAssetDetails uri={props.location.pathname} />
</AssetProvider>
</Permission>
)
}

34
src/utils/rbac.ts Normal file
View File

@ -0,0 +1,34 @@
import fetch from 'cross-fetch'
import appConfig from '../../app.config'
export default async function rbacRequest(
eventType: string,
address: string
): Promise<boolean | 'ERROR'> {
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'
}
}
}