Skip to content

Commit d41e57f

Browse files
add some access/credentials tests + emit events if unauthorized (#879)
* add some access/credentials tests + emit events if unauthorized * debug it * try fix test * try fix test * try fix test * try fix test * try fix test, fix order * try fix test, fix order * add generic function for unauthprized events * try something * try something * restor hooks * try another approach * debug * try global list to save deployments info * try global list to save deployments info * deploy 3 accessLists, test config * deploy 3 accessLists, test config, fix import * some refactor + debug * some refactor + debug * refactor * more tests * add 2 ore tests * fix test... we want the NON authorized here * fix explanation * bigger refactor, emit event on auth specific emitter * fix linter * fix import * restore package & debug more * undo prior changes * small fix * cleanup * refactor check on ddo handler * refactor check on processor, for auth publishers list, use common fn * cleanup * debug faiing credential test * fix undefined access to .ddo in test * remove unauthorized events * remove AUTH_CREDENTIALS_EVENT_EMITTER * Remove unused listeners --------- Co-authored-by: giurgiur99 <giurgiur99@gmail.com>
1 parent 607b674 commit d41e57f

8 files changed

Lines changed: 335 additions & 71 deletions

File tree

src/components/Indexer/processor.ts

Lines changed: 12 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ import ERC721Template from '@oceanprotocol/contracts/artifacts/contracts/templat
1919
import ERC20Template from '@oceanprotocol/contracts/artifacts/contracts/templates/ERC20TemplateEnterprise.sol/ERC20TemplateEnterprise.json' assert { type: 'json' }
2020
import Dispenser from '@oceanprotocol/contracts/artifacts/contracts/pools/dispenser/Dispenser.sol/Dispenser.json' assert { type: 'json' }
2121
import FixedRateExchange from '@oceanprotocol/contracts/artifacts/contracts/pools/fixedRate/FixedRateExchange.sol/FixedRateExchange.json' assert { type: 'json' }
22-
import AccessListContract from '@oceanprotocol/contracts/artifacts/contracts/accesslists/AccessList.sol/AccessList.json' assert { type: 'json' }
2322
import { getDatabase } from '../../utils/database.js'
2423
import {
2524
PROTOCOL_COMMANDS,
@@ -52,6 +51,7 @@ import { create256Hash } from '../../utils/crypt.js'
5251
import { URLUtils } from '../../utils/url.js'
5352
import { makeDid } from '../core/utils/validateDdoHandler.js'
5453
import { PolicyServer } from '../policyServer/index.js'
54+
import { checkCredentialOnAccessList } from '../../utils/credentials.js'
5555
class BaseEventProcessor {
5656
protected networkId: number
5757

@@ -459,30 +459,17 @@ export class MetadataEventProcessor extends BaseEventProcessor {
459459
}
460460
if (authorizedPublishersList) {
461461
// check accessList
462-
const chainsListed = Object.keys(authorizedPublishersList)
463-
const chain = String(chainId)
464-
// check the access lists for this chain
465-
if (chainsListed.length > 0 && chainsListed.includes(chain)) {
466-
let isAuthorized = false
467-
for (const accessListAddress of authorizedPublishersList[chain]) {
468-
const accessListContract = new ethers.Contract(
469-
accessListAddress,
470-
AccessListContract.abi,
471-
signer
472-
)
473-
// if has at least 1 token than is is authorized
474-
const balance = await accessListContract.balanceOf(owner)
475-
if (Number(balance) > 0) {
476-
isAuthorized = true
477-
break
478-
}
479-
}
480-
if (!isAuthorized) {
481-
INDEXER_LOGGER.error(
482-
`DDO owner ${owner} is NOT part of the ${ENVIRONMENT_VARIABLES.AUTHORIZED_PUBLISHERS_LIST.name} access group.`
483-
)
484-
return
485-
}
462+
const isAuthorized = await checkCredentialOnAccessList(
463+
authorizedPublishersList,
464+
String(chainId),
465+
owner,
466+
signer
467+
)
468+
if (!isAuthorized) {
469+
INDEXER_LOGGER.error(
470+
`DDO owner ${owner} is NOT part of the ${ENVIRONMENT_VARIABLES.AUTHORIZED_PUBLISHERS_LIST.name} access group.`
471+
)
472+
return
486473
}
487474
}
488475

src/components/Indexer/utils.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,9 @@ export const processChunkLogs = async (
307307
isAllowed = true // no rules for this specific chain, so ignore this
308308
}
309309
// move on to the next (do not process this event)
310-
if (isAllowed === false) continue
310+
if (isAllowed === false) {
311+
continue
312+
}
311313
} // end if (allowedValidatorsList) {
312314
} // end if if (checkMetadataValidated) {
313315
}

src/components/core/handler/ddoHandler.ts

Lines changed: 19 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ import { CORE_LOGGER } from '../../../utils/logging/common.js'
1717
import { Blockchain } from '../../../utils/blockchain.js'
1818
import { ethers, isAddress } from 'ethers'
1919
import ERC721Template from '@oceanprotocol/contracts/artifacts/contracts/templates/ERC721Template.sol/ERC721Template.json' assert { type: 'json' }
20-
import AccessListContract from '@oceanprotocol/contracts/artifacts/contracts/accesslists/AccessList.sol/AccessList.json' assert { type: 'json' }
2120
// import lzma from 'lzma-native'
2221
import lzmajs from 'lzma-purejs-requirejs'
2322
import {
@@ -44,6 +43,7 @@ import {
4443
wasNFTDeployedByOurFactory
4544
} from '../../Indexer/utils.js'
4645
import { deleteIndexedMetadataIfExists, validateDDOHash } from '../../../utils/asset.js'
46+
import { checkCredentialOnAccessList } from '../../../utils/credentials.js'
4747

4848
const MAX_NUM_PROVIDERS = 5
4949
// after 60 seconds it returns whatever info we have available
@@ -200,44 +200,25 @@ export class DecryptDdoHandler extends CommandHandler {
200200
}
201201
}
202202

203-
// access lit checks, needs blockchain connection
203+
// access list checks, needs blockchain connection
204204
const { authorizedDecryptersList } = config
205-
if (authorizedDecryptersList && Object.keys(authorizedDecryptersList).length > 0) {
206-
// check accessList
207-
const chainsListed = Object.keys(authorizedDecryptersList)
208-
// check the access lists for this chain
209-
if (chainsListed.length > 0 && chainsListed.includes(chainId)) {
210-
let isAllowed = false
211-
for (const accessListAddress of authorizedDecryptersList[chainId]) {
212-
// instantiate contract and check balanceOf
213-
const accessListContract = new ethers.Contract(
214-
accessListAddress,
215-
AccessListContract.abi,
216-
blockchain.getSigner()
217-
)
218-
219-
// check access list contract
220-
const balance = await accessListContract.balanceOf(
221-
await blockchain.getSigner().getAddress()
222-
)
223-
if (Number(balance) > 0) {
224-
isAllowed = true
225-
break
226-
}
227-
}
228205

229-
if (!isAllowed) {
230-
CORE_LOGGER.logMessage(
231-
'Decrypt DDO: Decrypter not authorized per access list',
232-
true
233-
)
234-
return {
235-
stream: null,
236-
status: {
237-
httpStatus: 403,
238-
error: 'Decrypt DDO: Decrypter not authorized per access list'
239-
}
240-
}
206+
const isAllowed = await checkCredentialOnAccessList(
207+
authorizedDecryptersList,
208+
chainId,
209+
decrypterAddress,
210+
signer
211+
)
212+
if (!isAllowed) {
213+
CORE_LOGGER.logMessage(
214+
'Decrypt DDO: Decrypter not authorized per access list',
215+
true
216+
)
217+
return {
218+
stream: null,
219+
status: {
220+
httpStatus: 403,
221+
error: `Decrypt DDO: Decrypter ${decrypterAddress} not authorized per access list`
241222
}
242223
}
243224
}
@@ -282,6 +263,7 @@ export class DecryptDdoHandler extends CommandHandler {
282263
try {
283264
encryptedDocument = ethers.getBytes(task.encryptedDocument)
284265
flags = Number(task.flags)
266+
// eslint-disable-next-line prefer-destructuring
285267
documentHash = task.documentHash
286268
} catch (error) {
287269
CORE_LOGGER.logMessage(`Decrypt DDO: error ${error}`, true)
Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
import {
2+
buildEnvOverrideConfig,
3+
getMockSupportedNetworks,
4+
OverrideEnvConfig,
5+
setupEnvironment,
6+
tearDownEnvironment,
7+
TEST_ENV_CONFIG_FILE
8+
} from '../utils/utils.js'
9+
import { JsonRpcProvider, Signer } from 'ethers'
10+
import { Blockchain } from '../../utils/blockchain.js'
11+
import { RPCS, SupportedNetwork } from '../../@types/blockchain.js'
12+
import { DEVELOPMENT_CHAIN_ID } from '../../utils/address.js'
13+
import { ENVIRONMENT_VARIABLES } from '../../utils/constants.js'
14+
import { deployAndGetAccessListConfig, EXISTING_ACCESSLISTS } from '../utils/contracts.js'
15+
import { AccessListContract, OceanNodeConfig } from '../../@types/OceanNode.js'
16+
import { homedir } from 'os'
17+
import { getConfiguration } from '../../utils/config.js'
18+
import { assert, expect } from 'chai'
19+
import { findAccessListCredentials } from '../../utils/credentials.js'
20+
21+
describe('Should deploy some accessLists before all other tests.', () => {
22+
let config: OceanNodeConfig
23+
let provider: JsonRpcProvider
24+
const mockSupportedNetworks: RPCS = getMockSupportedNetworks()
25+
26+
let previousConfiguration: OverrideEnvConfig[]
27+
28+
let blockchain: Blockchain
29+
// let contractAcessList: Contract
30+
let owner: Signer
31+
32+
let wallets: Signer[] = []
33+
34+
let allAccessListsDefinitions: AccessListContract[] = []
35+
36+
before(async () => {
37+
provider = new JsonRpcProvider('http://127.0.0.1:8545')
38+
config = await getConfiguration() // Force reload the configuration
39+
40+
wallets = [
41+
(await provider.getSigner(0)) as Signer,
42+
(await provider.getSigner(1)) as Signer,
43+
(await provider.getSigner(2)) as Signer,
44+
(await provider.getSigner(3)) as Signer
45+
]
46+
47+
const rpcs: RPCS = config.supportedNetworks
48+
const chain: SupportedNetwork = rpcs[String(DEVELOPMENT_CHAIN_ID)]
49+
blockchain = new Blockchain(
50+
chain.rpc,
51+
chain.network,
52+
chain.chainId,
53+
chain.fallbackRPCs
54+
)
55+
56+
owner = blockchain.getSigner()
57+
58+
// ENVIRONMENT_VARIABLES.AUTHORIZED_PUBLISHERS_LIST
59+
const accessListPublishers = await deployAndGetAccessListConfig(
60+
owner,
61+
provider,
62+
wallets
63+
)
64+
EXISTING_ACCESSLISTS.set(
65+
ENVIRONMENT_VARIABLES.AUTHORIZED_PUBLISHERS_LIST.name,
66+
accessListPublishers
67+
)
68+
const accessListValidators = await deployAndGetAccessListConfig(
69+
owner,
70+
provider,
71+
wallets
72+
)
73+
// ENVIRONMENT_VARIABLES.ALLOWED_VALIDATORS_LIST
74+
EXISTING_ACCESSLISTS.set(
75+
ENVIRONMENT_VARIABLES.ALLOWED_VALIDATORS_LIST.name,
76+
accessListValidators
77+
)
78+
79+
const accessListDecrypters = await deployAndGetAccessListConfig(
80+
owner,
81+
provider,
82+
wallets
83+
)
84+
// ENVIRONMENT_VARIABLES.AUTHORIZED_DECRYPTERS_LIST
85+
EXISTING_ACCESSLISTS.set(
86+
ENVIRONMENT_VARIABLES.AUTHORIZED_DECRYPTERS_LIST.name,
87+
accessListDecrypters
88+
)
89+
90+
// put them all here
91+
allAccessListsDefinitions = [
92+
accessListPublishers,
93+
accessListValidators,
94+
accessListDecrypters
95+
]
96+
97+
// override and save configuration (always before calling getConfig())
98+
previousConfiguration = await setupEnvironment(
99+
TEST_ENV_CONFIG_FILE,
100+
buildEnvOverrideConfig(
101+
[
102+
ENVIRONMENT_VARIABLES.RPCS,
103+
ENVIRONMENT_VARIABLES.INDEXER_NETWORKS,
104+
ENVIRONMENT_VARIABLES.PRIVATE_KEY,
105+
ENVIRONMENT_VARIABLES.AUTHORIZED_DECRYPTERS,
106+
ENVIRONMENT_VARIABLES.ALLOWED_ADMINS,
107+
ENVIRONMENT_VARIABLES.AUTHORIZED_PUBLISHERS,
108+
ENVIRONMENT_VARIABLES.ADDRESS_FILE,
109+
// ACCESS_LISTS
110+
ENVIRONMENT_VARIABLES.AUTHORIZED_PUBLISHERS_LIST,
111+
ENVIRONMENT_VARIABLES.ALLOWED_VALIDATORS_LIST,
112+
ENVIRONMENT_VARIABLES.AUTHORIZED_DECRYPTERS_LIST
113+
],
114+
[
115+
JSON.stringify(mockSupportedNetworks),
116+
JSON.stringify([8996]),
117+
'0xc594c6e5def4bab63ac29eed19a134c130388f74f019bc74b8f4389df2837a58',
118+
JSON.stringify(['0xe2DD09d719Da89e5a3D0F2549c7E24566e947260']),
119+
JSON.stringify(['0xe2DD09d719Da89e5a3D0F2549c7E24566e947260']),
120+
JSON.stringify([
121+
await owner.getAddress() // the node
122+
]),
123+
`${homedir}/.ocean/ocean-contracts/artifacts/address.json`,
124+
JSON.stringify(
125+
EXISTING_ACCESSLISTS.get(
126+
ENVIRONMENT_VARIABLES.AUTHORIZED_PUBLISHERS_LIST.name
127+
)
128+
),
129+
JSON.stringify(
130+
EXISTING_ACCESSLISTS.get(ENVIRONMENT_VARIABLES.ALLOWED_VALIDATORS_LIST.name)
131+
),
132+
JSON.stringify(
133+
EXISTING_ACCESSLISTS.get(
134+
ENVIRONMENT_VARIABLES.AUTHORIZED_DECRYPTERS_LIST.name
135+
)
136+
)
137+
]
138+
)
139+
)
140+
141+
config = await getConfiguration()
142+
})
143+
144+
it('should have some access lists', () => {
145+
expect(EXISTING_ACCESSLISTS.size > 0, 'Should have at least 1 accessList')
146+
})
147+
148+
it(`should have ${ENVIRONMENT_VARIABLES.AUTHORIZED_PUBLISHERS_LIST.name} access lists`, () => {
149+
assert(
150+
config.authorizedPublishersList !== null,
151+
`${ENVIRONMENT_VARIABLES.AUTHORIZED_PUBLISHERS_LIST.name} accessList is not defined`
152+
)
153+
console.log(config.authorizedPublishersList)
154+
})
155+
156+
it(`should have ${ENVIRONMENT_VARIABLES.ALLOWED_VALIDATORS_LIST.name} access lists`, () => {
157+
assert(
158+
config.allowedValidatorsList !== null,
159+
`${ENVIRONMENT_VARIABLES.ALLOWED_VALIDATORS_LIST.name} accessList is not defined`
160+
)
161+
console.log(config.allowedValidatorsList)
162+
})
163+
164+
it(`should have ${ENVIRONMENT_VARIABLES.AUTHORIZED_DECRYPTERS_LIST.name} access lists`, () => {
165+
assert(
166+
config.authorizedPublishersList !== null,
167+
`${ENVIRONMENT_VARIABLES.AUTHORIZED_DECRYPTERS_LIST.name} accessList is not defined`
168+
)
169+
console.log(config.authorizedPublishersList)
170+
})
171+
172+
it('should check if wallets are on accessList (with Access)', async function () {
173+
for (let z = 0; z < allAccessListsDefinitions.length; z++) {
174+
const accessListAddress = allAccessListsDefinitions[z][DEVELOPMENT_CHAIN_ID][0] // we have only 1 accesslist per config
175+
for (let i = 0; i < wallets.length; i++) {
176+
const account = await wallets[i].getAddress()
177+
expect(
178+
(await findAccessListCredentials(owner, account, accessListAddress)) === true,
179+
`Address ${account} has no balance on Access List ${accessListAddress}, so its not Authorized`
180+
)
181+
}
182+
}
183+
})
184+
185+
it('should check that wallets are NOT on accessList (without Access)', async function () {
186+
for (let z = 0; z < allAccessListsDefinitions.length; z++) {
187+
const accessListAddress = allAccessListsDefinitions[z][DEVELOPMENT_CHAIN_ID][0] // we have only 1 accesslist per config
188+
for (let i = wallets.length; i < 4; i++) {
189+
const account = await (await provider.getSigner(i)).getAddress()
190+
expect(
191+
(await findAccessListCredentials(owner, account, accessListAddress)) === false,
192+
`Address ${account} should not be part Access List ${accessListAddress}, therefore its not Authorized`
193+
)
194+
}
195+
}
196+
})
197+
198+
after(async () => {
199+
await tearDownEnvironment(previousConfiguration)
200+
})
201+
})

0 commit comments

Comments
 (0)