diff --git a/etc/content-validator.api.md b/etc/content-validator.api.md index c22bd976..b667c3f5 100644 --- a/etc/content-validator.api.md +++ b/etc/content-validator.api.md @@ -7,6 +7,7 @@ import { AuthChain } from '@dcl/schemas'; import { Entity } from '@dcl/schemas'; import { EthAddress } from '@dcl/schemas'; +import { IConfigComponent } from '@well-known-components/interfaces'; import { ILoggerComponent } from '@well-known-components/interfaces'; import { ISubgraphComponent } from '@well-known-components/thegraph-component'; import { Variables } from '@well-known-components/thegraph-component'; @@ -27,6 +28,7 @@ export type ConditionalValidation = { // @public export type ContentValidatorComponents = { + config: IConfigComponent; logs: ILoggerComponent; theGraphClient: TheGraphClient; externalCalls: ExternalCalls; @@ -37,7 +39,7 @@ export type ContentValidatorComponents = { export const createTheGraphClient: (components: Pick) => TheGraphClient; // @public -export const createValidator: (components: Pick) => Validator; +export const createValidator: (components: Pick) => Validator; // @public export type DeploymentToValidate = { diff --git a/package.json b/package.json index 0d675960..44768b1a 100644 --- a/package.json +++ b/package.json @@ -39,6 +39,7 @@ "@types/sharp": "^0.30.2", "@typescript-eslint/eslint-plugin": "5.21.0", "@typescript-eslint/parser": "5.21.0", + "@well-known-components/env-config-provider": "^1.1.1", "eslint": "8.14.0", "eslint-config-prettier": "8.5.0", "eslint-plugin-prettier": "4.0.0", diff --git a/src/index.ts b/src/index.ts index 04ab06ba..e5fef635 100644 --- a/src/index.ts +++ b/src/index.ts @@ -9,7 +9,7 @@ export * from './validations' * @public */ export const createValidator = ( - components: Pick + components: Pick ): Validator => { const logs = components.logs.getLogger('ContentValidator') diff --git a/src/types.ts b/src/types.ts index bbea2bf3..484d344d 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,5 +1,5 @@ import { AuthChain, Entity, EthAddress } from '@dcl/schemas' -import { ILoggerComponent } from '@well-known-components/interfaces' +import { IConfigComponent, ILoggerComponent } from '@well-known-components/interfaces' import { ISubgraphComponent, Variables } from '@well-known-components/thegraph-component' import { PermissionResult } from './the-graph-client/the-graph-client' @@ -163,6 +163,7 @@ export type BlockInformation = { * @public */ export type ContentValidatorComponents = { + config: IConfigComponent logs: ILoggerComponent theGraphClient: TheGraphClient externalCalls: ExternalCalls diff --git a/src/validations/access-checker/access.ts b/src/validations/access-checker/access.ts index 59429c75..2fc822d8 100644 --- a/src/validations/access-checker/access.ts +++ b/src/validations/access-checker/access.ts @@ -7,11 +7,6 @@ import { scenes } from './scenes' import { stores } from './stores' import { wearables } from './wearables' -/** - * Whether to ignore checks for access permission using blockchain data. Useful during content development. - */ -const IGNORE_BLOCKCHAIN_ACCESS_CHECKS = process.env.IGNORE_BLOCKCHAIN_ACCESS_CHECKS === 'true' - const accessCheckers: Record = { [EntityType.PROFILE]: profiles, [EntityType.SCENE]: scenes, @@ -26,7 +21,9 @@ const accessCheckers: Record = { */ export const access: Validation = { validate: async (components, deployment: DeploymentToValidate) => { - if (IGNORE_BLOCKCHAIN_ACCESS_CHECKS) return OK + if ((await components.config.getString('IGNORE_BLOCKCHAIN_ACCESS_CHECKS')) === 'true') { + return OK + } const { externalCalls } = components const deployedBeforeDCLLaunch = deployment.entity.timestamp <= LEGACY_CONTENT_MIGRATION_TIMESTAMP diff --git a/test/setup/mock.ts b/test/setup/mock.ts index 0edc2f9f..62289c6c 100644 --- a/test/setup/mock.ts +++ b/test/setup/mock.ts @@ -1,8 +1,9 @@ -import { ILoggerComponent } from '@well-known-components/interfaces' +import { IConfigComponent, ILoggerComponent } from '@well-known-components/interfaces' import { ISubgraphComponent } from '@well-known-components/thegraph-component' import { createTheGraphClient } from '../../src' import { ContentValidatorComponents, ExternalCalls, QueryGraph, SubGraphs } from '../../src/types' import { ItemCollection } from '../../src/validations/access-checker/items/collection-asset' +import { createConfigComponent } from '@well-known-components/env-config-provider' export const buildLogger = (): ILoggerComponent => ({ getLogger: () => ({ @@ -15,6 +16,8 @@ export const buildLogger = (): ILoggerComponent => ({ }) export const buildComponents = (components?: Partial): ContentValidatorComponents => { + const config = components?.config ?? buildConfig({}) + const externalCalls = components?.externalCalls ?? buildExternalCalls() const logs = components?.logs ?? buildLogger() @@ -23,6 +26,7 @@ export const buildComponents = (components?: Partial const theGraphClient = components?.theGraphClient ?? createTheGraphClient({ logs, subGraphs, ...components }) return { + config, logs, theGraphClient, externalCalls, @@ -30,6 +34,9 @@ export const buildComponents = (components?: Partial } } +export const buildConfig = (optionMap: Partial>): IConfigComponent => + createConfigComponent({ ...optionMap }) + export const buildExternalCalls = (externalCalls?: Partial): ExternalCalls => ({ isContentStoredAlready: () => Promise.resolve(new Map()), fetchContentFileSize: () => Promise.resolve(undefined), diff --git a/test/unit/access/scenes.spec.ts b/test/unit/access/scenes.spec.ts index 5afb2a99..983890bc 100644 --- a/test/unit/access/scenes.spec.ts +++ b/test/unit/access/scenes.spec.ts @@ -1,6 +1,8 @@ import { scenes } from '../../../src/validations/access-checker/scenes' +import { access } from '../../../src/validations/access-checker/access' import { buildSceneDeployment } from '../../setup/deployments' -import { buildComponents, buildExternalCalls } from '../../setup/mock' +import { buildComponents, buildConfig, buildExternalCalls } from '../../setup/mock' +import { throws } from 'assert' describe('Access: scenes', () => { it('When a non-decentraland address tries to deploy a default scene, then an error is returned', async () => { @@ -44,4 +46,40 @@ describe('Access: scenes', () => { 'Scene pointers should only contain two integers separated by a comma, for example (10,10) or (120,-45). Invalid pointer: invalid-pointer' ) }) + + describe('blockchain access check validations', () => { + it('When IGNORE_BLOCKCHAIN_ACCESS_CHECKS=false, then ownerAddress function is invoked', async () => { + const pointers = ['Default10'] + const deployment = buildSceneDeployment(pointers) + const config = buildConfig({ + IGNORE_BLOCKCHAIN_ACCESS_CHECKS: 'false' + }) + const ownerAddress = jest.fn() + ownerAddress.mockResolvedValue('0xAddress') + const externalCalls = buildExternalCalls({ + isAddressOwnedByDecentraland: () => true, + ownerAddress + }) + + const response = await access.validate(buildComponents({ config, externalCalls }), deployment) + expect(response.ok).toBeFalsy() + expect(ownerAddress).toHaveBeenCalled() + }) + + it('When IGNORE_BLOCKCHAIN_ACCESS_CHECKS=false, then ownerAddress function is not invoked', async () => { + const pointers = ['Default10'] + const deployment = buildSceneDeployment(pointers) + const config = buildConfig({ + IGNORE_BLOCKCHAIN_ACCESS_CHECKS: 'true' + }) + const ownerAddress = jest.fn() + const externalCalls = buildExternalCalls({ + ownerAddress + }) + + const response = await access.validate(buildComponents({ config, externalCalls }), deployment) + expect(response.ok).toBeTruthy() + expect(ownerAddress).not.toHaveBeenCalled() + }) + }) }) diff --git a/yarn.lock b/yarn.lock index d32a9127..ce2facae 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1751,6 +1751,13 @@ "@typescript-eslint/types" "5.29.0" eslint-visitor-keys "^3.3.0" +"@well-known-components/env-config-provider@^1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@well-known-components/env-config-provider/-/env-config-provider-1.1.1.tgz#d04215847fcab83a1ab05e375df8e2141dae23cd" + integrity sha512-toj2HcxYZRf9UuBqEC9mrej9yyQlGU/15oGtShrDtNC9/OVLBNY3q8cNgLLKbPOMLaRJkjdQvIvpjb+Q+E+PFQ== + dependencies: + dotenv "^8.2.0" + "@well-known-components/http-server@^1.1.0": version "1.1.2" resolved "https://registry.yarnpkg.com/@well-known-components/http-server/-/http-server-1.1.2.tgz#39d038da07efd3c1a3bc9a7edab5418c8162910f" @@ -2535,6 +2542,11 @@ doctrine@^3.0.0: dependencies: esutils "^2.0.2" +dotenv@^8.2.0: + version "8.6.0" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.6.0.tgz#061af664d19f7f4d8fc6e4ff9b584ce237adcb8b" + integrity sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g== + ee-first@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"