diff --git a/CHANGELOG.md b/CHANGELOG.md index 1928ba6d47..2d8eb65083 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ Changes before Tatum release are not documented in this file. - Add `StreamrClient#findProxyNodes()` function for discovering proxy nodes via Operator nodes (https://github.com/streamr-dev/network/pull/3257) - Add `StreamrClient#publishRaw()` for publishing raw messages (https://github.com/streamr-dev/network/pull/3280) - Add new `keys` configuration to the `encryption` section (https://github.com/streamr-dev/network/pull/3284) +- Add new `validation` configuration section (https://github.com/streamr-dev/network/pull/3302) #### Changed diff --git a/packages/sdk/src/Config.ts b/packages/sdk/src/Config.ts index 937a9e91d0..72d302415b 100644 --- a/packages/sdk/src/Config.ts +++ b/packages/sdk/src/Config.ts @@ -412,6 +412,11 @@ export interface StreamrClientConfig { pollInterval?: number } + validation?: { + permissions?: boolean + partitions?: boolean + } + /** * Determines the telemetry metrics that are sent to the Streamr Network * at regular intervals. @@ -457,6 +462,7 @@ export type StrictStreamrClientConfig = MarkOptional, undefined> contracts: Exclude, undefined> encryption: Exclude, undefined> + validation: Exclude, undefined> cache: Exclude, undefined> /** @internal */ _timeouts: Exclude, undefined> diff --git a/packages/sdk/src/config.schema.json b/packages/sdk/src/config.schema.json index 5f2c0f0c21..1a899872d8 100644 --- a/packages/sdk/src/config.schema.json +++ b/packages/sdk/src/config.schema.json @@ -388,6 +388,21 @@ }, "default": {} }, + "validation": { + "type": "object", + "additionalProperties": false, + "properties": { + "permissions": { + "type": "boolean", + "default": true + }, + "partitions": { + "type": "boolean", + "default": true + } + }, + "default": {} + }, "metrics": { "anyOf": [ { diff --git a/packages/sdk/src/encryption/PublisherKeyExchange.ts b/packages/sdk/src/encryption/PublisherKeyExchange.ts index ede5981851..c29393772e 100644 --- a/packages/sdk/src/encryption/PublisherKeyExchange.ts +++ b/packages/sdk/src/encryption/PublisherKeyExchange.ts @@ -51,7 +51,7 @@ export class PublisherKeyExchange { private readonly identity: Identity private readonly logger: Logger private readonly erc1271Publishers = new Set() - private readonly config: Pick + private readonly config: Pick constructor( networkNodeFacade: NetworkNodeFacade, @@ -60,7 +60,7 @@ export class PublisherKeyExchange { messageSigner: MessageSigner, store: LocalGroupKeyStore, @inject(IdentityInjectionToken) identity: Identity, - @inject(ConfigInjectionToken) config: Pick, + @inject(ConfigInjectionToken) config: Pick, eventEmitter: StreamrClientEventEmitter, loggerFactory: LoggerFactory ) { @@ -110,7 +110,7 @@ export class PublisherKeyExchange { if (responseType !== ResponseType.NONE) { this.logger.debug('Handling group key request', { requestId, responseType, keyEncryptionType: AsymmetricEncryptionType[keyEncryptionType] }) - await validateStreamMessage(request, this.streamRegistry, this.signatureValidator) + await validateStreamMessage(request, this.streamRegistry, this.signatureValidator, this.config) const authenticatedUser = await this.identity.getUserId() const keys = without( await Promise.all(groupKeyIds.map((id: string) => this.store.get(id, authenticatedUser))), diff --git a/packages/sdk/src/encryption/SubscriberKeyExchange.ts b/packages/sdk/src/encryption/SubscriberKeyExchange.ts index 54b2e1e8d6..2b65d186ce 100644 --- a/packages/sdk/src/encryption/SubscriberKeyExchange.ts +++ b/packages/sdk/src/encryption/SubscriberKeyExchange.ts @@ -42,6 +42,7 @@ export class SubscriberKeyExchange { private readonly identity: Identity private readonly logger: Logger private readonly ensureStarted: () => Promise + private readonly config: Pick requestGroupKey: (groupKeyId: string, publisherId: UserID, streamPartId: StreamPartID) => Promise constructor( @@ -51,7 +52,7 @@ export class SubscriberKeyExchange { messageSigner: MessageSigner, store: LocalGroupKeyStore, subscriber: Subscriber, - @inject(ConfigInjectionToken) config: Pick, + @inject(ConfigInjectionToken) config: Pick, @inject(IdentityInjectionToken) identity: Identity, loggerFactory: LoggerFactory ) { @@ -63,6 +64,7 @@ export class SubscriberKeyExchange { this.subscriber = subscriber this.identity = identity this.logger = loggerFactory.createLogger(module) + this.config = config // Setting explicit keys disables the key-exchange if (config.encryption.keys === undefined) { this.ensureStarted = pOnce(async () => { @@ -142,7 +144,7 @@ export class SubscriberKeyExchange { if (await this.isAssignedToMe(msg.getStreamPartID(), recipientUserId, requestId)) { this.logger.debug('Handle group key response', { requestId }) this.pendingRequests.delete(requestId) - await validateStreamMessage(msg, this.streamRegistry, this.signatureValidator) + await validateStreamMessage(msg, this.streamRegistry, this.signatureValidator, this.config) await Promise.all(encryptedGroupKeys.map(async (encryptedKey) => { const key = await EncryptionUtil.decryptWithPrivateKey(encryptedKey.data, this.keyPair!.getPrivateKey(), encryptionType) await this.store.set(encryptedKey.id, msg.getPublisherId(), key) diff --git a/packages/sdk/src/generated/validateConfig.js b/packages/sdk/src/generated/validateConfig.js index 7286766842..bec91be923 100644 --- a/packages/sdk/src/generated/validateConfig.js +++ b/packages/sdk/src/generated/validateConfig.js @@ -1 +1 @@ -"use strict";module.exports = validate10;module.exports.default = validate10;const schema11 = {"$id":"config.schema.json","$schema":"http://json-schema.org/draft-07/schema#","description":"Client configuration format","type":"object","additionalProperties":false,"properties":{"environment":{"type":"string","enum":["polygon","polygonAmoy","peaq","iotex","dev2"],"description":"applies all environment-specific defaults for the given environment"},"id":{"type":"string"},"logLevel":{"type":"string","enum":["silent","fatal","error","warn","info","debug","trace"],"default":"info"},"auth":{"type":"object","additionalProperties":false,"properties":{"publicKey":{"type":"string","format":"hex-string"},"privateKey":{"type":"string","format":"hex-string"},"keyType":{"type":"string","default":"ECDSA_SECP256K1_EVM"},"ethereum":{"type":"object"},"identity":{"type":"object"}}},"orderMessages":{"type":"boolean","default":true},"gapFill":{"type":"boolean","default":true},"maxGapRequests":{"type":"number","default":5},"retryResendAfter":{"type":"number","default":5000},"gapFillTimeout":{"type":"number","default":5000},"gapFillStrategy":{"type":"string","enum":["light","full"],"default":"light"},"network":{"type":"object","additionalProperties":false,"required":[],"properties":{"controlLayer":{"type":"object","additionalProperties":false,"properties":{"entryPoints":{"type":"array","items":{"$ref":"#/definitions/peerDescriptor"}},"entryPointDiscovery":{"type":"object","additionalProperties":false,"properties":{"enabled":{"type":"boolean"},"maxEntryPoints":{"type":"number"},"maxQueryResults":{"type":"number"},"maxHeartbeatAgeHours":{"type":"number"}},"default":{"enabled":true,"maxEntryPoints":5,"maxQueryResults":50,"maxHeartbeatAgeHours":24}},"websocketPortRange":{"anyOf":[{"type":"null"},{"$ref":"#/definitions/portRange"}],"default":{"min":32200,"max":32250}},"websocketHost":{"type":"string","format":"hostname"},"peerDescriptor":{"$ref":"#/definitions/peerDescriptor"},"maxConnections":{"type":"number","default":80},"tlsCertificate":{"description":"Files to use for TLS","type":"object","required":["certFileName","privateKeyFileName"],"additionalProperties":false,"properties":{"certFileName":{"type":"string","description":"Path of certificate file"},"privateKeyFileName":{"type":"string","description":"Path of private key file"}}},"iceServers":{"type":"array","items":{"type":"object","required":["url","port"],"additionalProperties":false,"properties":{"url":{"type":"string"},"port":{"type":"number"},"username":{"type":"string"},"password":{"type":"string"},"tcp":{"type":"boolean"}}},"default":[{"url":"stun:stun.streamr.network","port":5349},{"url":"turn:turn.streamr.network","port":5349,"username":"BrubeckTurn1","password":"MIlbgtMw4nhpmbgqRrht1Q=="},{"url":"turn:turn.streamr.network","port":5349,"username":"BrubeckTurn1","password":"MIlbgtMw4nhpmbgqRrht1Q==","tcp":true}]},"webrtcAllowPrivateAddresses":{"type":"boolean","default":false},"webrtcDatachannelBufferThresholdLow":{"type":"number","default":32768},"webrtcDatachannelBufferThresholdHigh":{"type":"number","default":131072},"maxMessageSize":{"type":"number","default":1048576},"externalIp":{"type":"string","format":"ipv4"},"webrtcPortRange":{"$ref":"#/definitions/portRange","default":{"min":50000,"max":64000}},"networkConnectivityTimeout":{"type":"number","default":10000},"websocketServerEnableTls":{"type":"boolean","default":true},"autoCertifierUrl":{"type":"string","default":"https://ns1.streamr-nodes.xyz:59833"},"autoCertifierConfigFile":{"type":"string","default":"~/.streamr/certificate.json"},"geoIpDatabaseFolder":{"type":"string"}},"default":{}},"node":{"type":"object","additionalProperties":false,"properties":{"streamPartitionNeighborTargetCount":{"type":"number","default":4},"streamPartitionMinPropagationTargets":{"type":"number","default":2},"acceptProxyConnections":{"type":"boolean","default":false}},"default":{}}},"default":{}},"contracts":{"type":"object","additionalProperties":false,"properties":{"ethereumNetwork":{"type":"object","additionalProperties":false,"properties":{"chainId":{"type":"number"},"overrides":{"type":"object"},"highGasPriceStrategy":{"type":"boolean"}},"default":{}},"streamRegistryChainAddress":{"type":"string","format":"ethereum-address"},"streamStorageRegistryChainAddress":{"type":"string","format":"ethereum-address"},"storageNodeRegistryChainAddress":{"type":"string","format":"ethereum-address"},"sponsorshipFactoryChainAddress":{"type":"string","format":"ethereum-address"},"rpcs":{"type":"array","items":{"type":"object","$ref":"#/definitions/rpcProviderConfig"},"minItems":1},"rpcQuorum":{"type":"number","default":2},"theGraphUrl":{"type":"string","format":"uri"},"maxConcurrentCalls":{"type":"number","default":10},"pollInterval":{"type":"number","default":4000}},"default":{}},"encryption":{"type":"object","additionalProperties":false,"properties":{"keyRequestTimeout":{"type":"number","default":30000},"maxKeyRequestsPerSecond":{"type":"number","default":20},"rsaKeyLength":{"type":"number","default":4096},"requireQuantumResistantKeyExchange":{"type":"boolean","default":false},"requireQuantumResistantSignatures":{"type":"boolean","default":false},"requireQuantumResistantEncryption":{"type":"boolean","default":false},"keys":{"type":"object","propertyNames":{"$ref":"#/definitions/streamIdOrPath"},"additionalProperties":{"$ref":"#/definitions/encryptionKey"}}},"default":{}},"metrics":{"anyOf":[{"type":"boolean"},{"type":"object","additionalProperties":false,"properties":{"periods":{"type":"array","items":{"type":"object","required":["streamId","duration"],"properties":{"id":{"type":"string"},"duration":{"type":"number"}}}},"maxPublishDelay":{"type":"number"}}}]},"cache":{"type":"object","additionalProperties":false,"properties":{"maxSize":{"type":"number","default":10000},"maxAge":{"type":"number","default":86400000}},"default":{}},"_timeouts":{"type":"object","additionalProperties":false,"properties":{"theGraph":{"type":"object","additionalProperties":false,"properties":{"indexTimeout":{"type":"number","default":60000},"indexPollInterval":{"type":"number","default":1000},"fetchTimeout":{"type":"number","default":30000}},"default":{}},"storageNode":{"type":"object","additionalProperties":false,"properties":{"timeout":{"type":"number","default":30000},"retryInterval":{"type":"number","default":1000}},"default":{}},"ensStreamCreation":{"type":"object","additionalProperties":false,"properties":{"timeout":{"type":"number","default":180000},"retryInterval":{"type":"number","default":1000}},"default":{}},"jsonRpcTimeout":{"type":"number","default":30000}},"default":{}}},"definitions":{"rpcProviderConfig":{"type":"object","required":["url"],"properties":{"url":{"type":"string","format":"uri"}}},"peerDescriptor":{"type":"object","additionalProperties":false,"properties":{"nodeId":{"type":"string"},"id":{"type":"string","description":"legacy: remove this property and make nodeId required"},"type":{"$ref":"#/definitions/nodeType"},"websocket":{"$ref":"#/definitions/connectivityMethod"}}},"nodeType":{"type":"string","enum":["browser","nodejs"]},"connectivityMethod":{"type":"object","additionalProperties":false,"required":["host","port","tls"],"properties":{"host":{"type":"string"},"port":{"type":"number"},"tls":{"type":"boolean"}}},"portRange":{"type":"object","additionalProperties":false,"required":["min","max"],"properties":{"min":{"type":"number"},"max":{"type":"number"}}},"streamIdOrPath":{"type":"string"},"encryptionKey":{"type":"object","properties":{"id":{"type":"string"},"data":{"type":"string","format":"hex-string"}},"required":["id","data"]}}};const schema15 = {"type":"object","additionalProperties":false,"required":["min","max"],"properties":{"min":{"type":"number"},"max":{"type":"number"}}};const schema17 = {"type":"object","required":["url"],"properties":{"url":{"type":"string","format":"uri"}}};const schema18 = {"type":"string"};const schema19 = {"type":"object","properties":{"id":{"type":"string"},"data":{"type":"string","format":"hex-string"}},"required":["id","data"]};const func2 = Object.prototype.hasOwnProperty;const formats0 = /^(0x)?[a-fA-F0-9]+$/;const formats4 = /^(?=.{1,253}\.?$)[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[-0-9a-z]{0,61}[0-9a-z])?)*\.?$/i;const formats6 = /^(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)$/;const formats8 = /^0x[a-fA-F0-9]{40}$/;const formats16 = /^(?:[a-z][a-z0-9+\-.]*:)(?:\/?\/)?[^\s]*$/i;const schema12 = {"type":"object","additionalProperties":false,"properties":{"nodeId":{"type":"string"},"id":{"type":"string","description":"legacy: remove this property and make nodeId required"},"type":{"$ref":"#/definitions/nodeType"},"websocket":{"$ref":"#/definitions/connectivityMethod"}}};const schema13 = {"type":"string","enum":["browser","nodejs"]};const schema14 = {"type":"object","additionalProperties":false,"required":["host","port","tls"],"properties":{"host":{"type":"string"},"port":{"type":"number"},"tls":{"type":"boolean"}}};function validate11(data, {instancePath="", parentData, parentDataProperty, rootData=data}={}){let vErrors = null;let errors = 0;if(errors === 0){if(data && typeof data == "object" && !Array.isArray(data)){const _errs1 = errors;for(const key0 in data){if(!((((key0 === "nodeId") || (key0 === "id")) || (key0 === "type")) || (key0 === "websocket"))){validate11.errors = [{instancePath,schemaPath:"#/additionalProperties",keyword:"additionalProperties",params:{additionalProperty: key0},message:"must NOT have additional properties"}];return false;break;}}if(_errs1 === errors){if(data.nodeId !== undefined){const _errs2 = errors;if(typeof data.nodeId !== "string"){validate11.errors = [{instancePath:instancePath+"/nodeId",schemaPath:"#/properties/nodeId/type",keyword:"type",params:{type: "string"},message:"must be string"}];return false;}var valid0 = _errs2 === errors;}else {var valid0 = true;}if(valid0){if(data.id !== undefined){const _errs4 = errors;if(typeof data.id !== "string"){validate11.errors = [{instancePath:instancePath+"/id",schemaPath:"#/properties/id/type",keyword:"type",params:{type: "string"},message:"must be string"}];return false;}var valid0 = _errs4 === errors;}else {var valid0 = true;}if(valid0){if(data.type !== undefined){let data2 = data.type;const _errs6 = errors;if(typeof data2 !== "string"){validate11.errors = [{instancePath:instancePath+"/type",schemaPath:"#/definitions/nodeType/type",keyword:"type",params:{type: "string"},message:"must be string"}];return false;}if(!((data2 === "browser") || (data2 === "nodejs"))){validate11.errors = [{instancePath:instancePath+"/type",schemaPath:"#/definitions/nodeType/enum",keyword:"enum",params:{allowedValues: schema13.enum},message:"must be equal to one of the allowed values"}];return false;}var valid0 = _errs6 === errors;}else {var valid0 = true;}if(valid0){if(data.websocket !== undefined){let data3 = data.websocket;const _errs9 = errors;const _errs10 = errors;if(errors === _errs10){if(data3 && typeof data3 == "object" && !Array.isArray(data3)){let missing0;if((((data3.host === undefined) && (missing0 = "host")) || ((data3.port === undefined) && (missing0 = "port"))) || ((data3.tls === undefined) && (missing0 = "tls"))){validate11.errors = [{instancePath:instancePath+"/websocket",schemaPath:"#/definitions/connectivityMethod/required",keyword:"required",params:{missingProperty: missing0},message:"must have required property '"+missing0+"'"}];return false;}else {const _errs12 = errors;for(const key1 in data3){if(!(((key1 === "host") || (key1 === "port")) || (key1 === "tls"))){validate11.errors = [{instancePath:instancePath+"/websocket",schemaPath:"#/definitions/connectivityMethod/additionalProperties",keyword:"additionalProperties",params:{additionalProperty: key1},message:"must NOT have additional properties"}];return false;break;}}if(_errs12 === errors){if(data3.host !== undefined){const _errs13 = errors;if(typeof data3.host !== "string"){validate11.errors = [{instancePath:instancePath+"/websocket/host",schemaPath:"#/definitions/connectivityMethod/properties/host/type",keyword:"type",params:{type: "string"},message:"must be string"}];return false;}var valid3 = _errs13 === errors;}else {var valid3 = true;}if(valid3){if(data3.port !== undefined){let data5 = data3.port;const _errs15 = errors;if(!((typeof data5 == "number") && (isFinite(data5)))){validate11.errors = [{instancePath:instancePath+"/websocket/port",schemaPath:"#/definitions/connectivityMethod/properties/port/type",keyword:"type",params:{type: "number"},message:"must be number"}];return false;}var valid3 = _errs15 === errors;}else {var valid3 = true;}if(valid3){if(data3.tls !== undefined){const _errs17 = errors;if(typeof data3.tls !== "boolean"){validate11.errors = [{instancePath:instancePath+"/websocket/tls",schemaPath:"#/definitions/connectivityMethod/properties/tls/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"}];return false;}var valid3 = _errs17 === errors;}else {var valid3 = true;}}}}}}else {validate11.errors = [{instancePath:instancePath+"/websocket",schemaPath:"#/definitions/connectivityMethod/type",keyword:"type",params:{type: "object"},message:"must be object"}];return false;}}var valid0 = _errs9 === errors;}else {var valid0 = true;}}}}}}else {validate11.errors = [{instancePath,schemaPath:"#/type",keyword:"type",params:{type: "object"},message:"must be object"}];return false;}}validate11.errors = vErrors;return errors === 0;}function validate10(data, {instancePath="", parentData, parentDataProperty, rootData=data}={}){/*# sourceURL="config.schema.json" */;let vErrors = null;let errors = 0;if(errors === 0){if(data && typeof data == "object" && !Array.isArray(data)){if(data.logLevel === undefined){data.logLevel = "info";}if(data.orderMessages === undefined){data.orderMessages = true;}if(data.gapFill === undefined){data.gapFill = true;}if(data.maxGapRequests === undefined){data.maxGapRequests = 5;}if(data.retryResendAfter === undefined){data.retryResendAfter = 5000;}if(data.gapFillTimeout === undefined){data.gapFillTimeout = 5000;}if(data.gapFillStrategy === undefined){data.gapFillStrategy = "light";}if(data.network === undefined){data.network = {};}if(data.contracts === undefined){data.contracts = {};}if(data.encryption === undefined){data.encryption = {};}if(data.cache === undefined){data.cache = {};}if(data._timeouts === undefined){data._timeouts = {};}const _errs1 = errors;for(const key0 in data){if(!(func2.call(schema11.properties, key0))){validate10.errors = [{instancePath,schemaPath:"#/additionalProperties",keyword:"additionalProperties",params:{additionalProperty: key0},message:"must NOT have additional properties"}];return false;break;}}if(_errs1 === errors){if(data.environment !== undefined){let data0 = data.environment;const _errs2 = errors;if(typeof data0 !== "string"){validate10.errors = [{instancePath:instancePath+"/environment",schemaPath:"#/properties/environment/type",keyword:"type",params:{type: "string"},message:"must be string"}];return false;}if(!(((((data0 === "polygon") || (data0 === "polygonAmoy")) || (data0 === "peaq")) || (data0 === "iotex")) || (data0 === "dev2"))){validate10.errors = [{instancePath:instancePath+"/environment",schemaPath:"#/properties/environment/enum",keyword:"enum",params:{allowedValues: schema11.properties.environment.enum},message:"must be equal to one of the allowed values"}];return false;}var valid0 = _errs2 === errors;}else {var valid0 = true;}if(valid0){if(data.id !== undefined){const _errs4 = errors;if(typeof data.id !== "string"){validate10.errors = [{instancePath:instancePath+"/id",schemaPath:"#/properties/id/type",keyword:"type",params:{type: "string"},message:"must be string"}];return false;}var valid0 = _errs4 === errors;}else {var valid0 = true;}if(valid0){let data2 = data.logLevel;const _errs6 = errors;if(typeof data2 !== "string"){validate10.errors = [{instancePath:instancePath+"/logLevel",schemaPath:"#/properties/logLevel/type",keyword:"type",params:{type: "string"},message:"must be string"}];return false;}if(!(((((((data2 === "silent") || (data2 === "fatal")) || (data2 === "error")) || (data2 === "warn")) || (data2 === "info")) || (data2 === "debug")) || (data2 === "trace"))){validate10.errors = [{instancePath:instancePath+"/logLevel",schemaPath:"#/properties/logLevel/enum",keyword:"enum",params:{allowedValues: schema11.properties.logLevel.enum},message:"must be equal to one of the allowed values"}];return false;}var valid0 = _errs6 === errors;if(valid0){if(data.auth !== undefined){let data3 = data.auth;const _errs8 = errors;if(errors === _errs8){if(data3 && typeof data3 == "object" && !Array.isArray(data3)){if(data3.keyType === undefined){data3.keyType = "ECDSA_SECP256K1_EVM";}const _errs10 = errors;for(const key1 in data3){if(!(((((key1 === "publicKey") || (key1 === "privateKey")) || (key1 === "keyType")) || (key1 === "ethereum")) || (key1 === "identity"))){validate10.errors = [{instancePath:instancePath+"/auth",schemaPath:"#/properties/auth/additionalProperties",keyword:"additionalProperties",params:{additionalProperty: key1},message:"must NOT have additional properties"}];return false;break;}}if(_errs10 === errors){if(data3.publicKey !== undefined){let data4 = data3.publicKey;const _errs11 = errors;if(errors === _errs11){if(errors === _errs11){if(typeof data4 === "string"){if(!(formats0.test(data4))){validate10.errors = [{instancePath:instancePath+"/auth/publicKey",schemaPath:"#/properties/auth/properties/publicKey/format",keyword:"format",params:{format: "hex-string"},message:"must match format \""+"hex-string"+"\""}];return false;}}else {validate10.errors = [{instancePath:instancePath+"/auth/publicKey",schemaPath:"#/properties/auth/properties/publicKey/type",keyword:"type",params:{type: "string"},message:"must be string"}];return false;}}}var valid1 = _errs11 === errors;}else {var valid1 = true;}if(valid1){if(data3.privateKey !== undefined){let data5 = data3.privateKey;const _errs13 = errors;if(errors === _errs13){if(errors === _errs13){if(typeof data5 === "string"){if(!(formats0.test(data5))){validate10.errors = [{instancePath:instancePath+"/auth/privateKey",schemaPath:"#/properties/auth/properties/privateKey/format",keyword:"format",params:{format: "hex-string"},message:"must match format \""+"hex-string"+"\""}];return false;}}else {validate10.errors = [{instancePath:instancePath+"/auth/privateKey",schemaPath:"#/properties/auth/properties/privateKey/type",keyword:"type",params:{type: "string"},message:"must be string"}];return false;}}}var valid1 = _errs13 === errors;}else {var valid1 = true;}if(valid1){const _errs15 = errors;if(typeof data3.keyType !== "string"){validate10.errors = [{instancePath:instancePath+"/auth/keyType",schemaPath:"#/properties/auth/properties/keyType/type",keyword:"type",params:{type: "string"},message:"must be string"}];return false;}var valid1 = _errs15 === errors;if(valid1){if(data3.ethereum !== undefined){let data7 = data3.ethereum;const _errs17 = errors;if(!(data7 && typeof data7 == "object" && !Array.isArray(data7))){validate10.errors = [{instancePath:instancePath+"/auth/ethereum",schemaPath:"#/properties/auth/properties/ethereum/type",keyword:"type",params:{type: "object"},message:"must be object"}];return false;}var valid1 = _errs17 === errors;}else {var valid1 = true;}if(valid1){if(data3.identity !== undefined){let data8 = data3.identity;const _errs19 = errors;if(!(data8 && typeof data8 == "object" && !Array.isArray(data8))){validate10.errors = [{instancePath:instancePath+"/auth/identity",schemaPath:"#/properties/auth/properties/identity/type",keyword:"type",params:{type: "object"},message:"must be object"}];return false;}var valid1 = _errs19 === errors;}else {var valid1 = true;}}}}}}}else {validate10.errors = [{instancePath:instancePath+"/auth",schemaPath:"#/properties/auth/type",keyword:"type",params:{type: "object"},message:"must be object"}];return false;}}var valid0 = _errs8 === errors;}else {var valid0 = true;}if(valid0){const _errs21 = errors;if(typeof data.orderMessages !== "boolean"){validate10.errors = [{instancePath:instancePath+"/orderMessages",schemaPath:"#/properties/orderMessages/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"}];return false;}var valid0 = _errs21 === errors;if(valid0){const _errs23 = errors;if(typeof data.gapFill !== "boolean"){validate10.errors = [{instancePath:instancePath+"/gapFill",schemaPath:"#/properties/gapFill/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"}];return false;}var valid0 = _errs23 === errors;if(valid0){let data11 = data.maxGapRequests;const _errs25 = errors;if(!((typeof data11 == "number") && (isFinite(data11)))){validate10.errors = [{instancePath:instancePath+"/maxGapRequests",schemaPath:"#/properties/maxGapRequests/type",keyword:"type",params:{type: "number"},message:"must be number"}];return false;}var valid0 = _errs25 === errors;if(valid0){let data12 = data.retryResendAfter;const _errs27 = errors;if(!((typeof data12 == "number") && (isFinite(data12)))){validate10.errors = [{instancePath:instancePath+"/retryResendAfter",schemaPath:"#/properties/retryResendAfter/type",keyword:"type",params:{type: "number"},message:"must be number"}];return false;}var valid0 = _errs27 === errors;if(valid0){let data13 = data.gapFillTimeout;const _errs29 = errors;if(!((typeof data13 == "number") && (isFinite(data13)))){validate10.errors = [{instancePath:instancePath+"/gapFillTimeout",schemaPath:"#/properties/gapFillTimeout/type",keyword:"type",params:{type: "number"},message:"must be number"}];return false;}var valid0 = _errs29 === errors;if(valid0){let data14 = data.gapFillStrategy;const _errs31 = errors;if(typeof data14 !== "string"){validate10.errors = [{instancePath:instancePath+"/gapFillStrategy",schemaPath:"#/properties/gapFillStrategy/type",keyword:"type",params:{type: "string"},message:"must be string"}];return false;}if(!((data14 === "light") || (data14 === "full"))){validate10.errors = [{instancePath:instancePath+"/gapFillStrategy",schemaPath:"#/properties/gapFillStrategy/enum",keyword:"enum",params:{allowedValues: schema11.properties.gapFillStrategy.enum},message:"must be equal to one of the allowed values"}];return false;}var valid0 = _errs31 === errors;if(valid0){let data15 = data.network;const _errs33 = errors;if(errors === _errs33){if(data15 && typeof data15 == "object" && !Array.isArray(data15)){if(data15.controlLayer === undefined){data15.controlLayer = {};}if(data15.node === undefined){data15.node = {};}const _errs35 = errors;for(const key2 in data15){if(!((key2 === "controlLayer") || (key2 === "node"))){validate10.errors = [{instancePath:instancePath+"/network",schemaPath:"#/properties/network/additionalProperties",keyword:"additionalProperties",params:{additionalProperty: key2},message:"must NOT have additional properties"}];return false;break;}}if(_errs35 === errors){let data16 = data15.controlLayer;const _errs36 = errors;if(errors === _errs36){if(data16 && typeof data16 == "object" && !Array.isArray(data16)){if(data16.entryPointDiscovery === undefined){data16.entryPointDiscovery = {"enabled":true,"maxEntryPoints":5,"maxQueryResults":50,"maxHeartbeatAgeHours":24};}if(data16.websocketPortRange === undefined){data16.websocketPortRange = {"min":32200,"max":32250};}if(data16.maxConnections === undefined){data16.maxConnections = 80;}if(data16.iceServers === undefined){data16.iceServers = [{"url":"stun:stun.streamr.network","port":5349},{"url":"turn:turn.streamr.network","port":5349,"username":"BrubeckTurn1","password":"MIlbgtMw4nhpmbgqRrht1Q=="},{"url":"turn:turn.streamr.network","port":5349,"username":"BrubeckTurn1","password":"MIlbgtMw4nhpmbgqRrht1Q==","tcp":true}];}if(data16.webrtcAllowPrivateAddresses === undefined){data16.webrtcAllowPrivateAddresses = false;}if(data16.webrtcDatachannelBufferThresholdLow === undefined){data16.webrtcDatachannelBufferThresholdLow = 32768;}if(data16.webrtcDatachannelBufferThresholdHigh === undefined){data16.webrtcDatachannelBufferThresholdHigh = 131072;}if(data16.maxMessageSize === undefined){data16.maxMessageSize = 1048576;}if(data16.webrtcPortRange === undefined){data16.webrtcPortRange = {"min":50000,"max":64000};}if(data16.networkConnectivityTimeout === undefined){data16.networkConnectivityTimeout = 10000;}if(data16.websocketServerEnableTls === undefined){data16.websocketServerEnableTls = true;}if(data16.autoCertifierUrl === undefined){data16.autoCertifierUrl = "https://ns1.streamr-nodes.xyz:59833";}if(data16.autoCertifierConfigFile === undefined){data16.autoCertifierConfigFile = "~/.streamr/certificate.json";}const _errs38 = errors;for(const key3 in data16){if(!(func2.call(schema11.properties.network.properties.controlLayer.properties, key3))){validate10.errors = [{instancePath:instancePath+"/network/controlLayer",schemaPath:"#/properties/network/properties/controlLayer/additionalProperties",keyword:"additionalProperties",params:{additionalProperty: key3},message:"must NOT have additional properties"}];return false;break;}}if(_errs38 === errors){if(data16.entryPoints !== undefined){let data17 = data16.entryPoints;const _errs39 = errors;if(errors === _errs39){if(Array.isArray(data17)){var valid4 = true;const len0 = data17.length;for(let i0=0; i0 + config: Pick } export class MessageFactory { @@ -45,7 +45,7 @@ export class MessageFactory { private readonly groupKeyQueue: GroupKeyQueue private readonly signatureValidator: SignatureValidator private readonly messageSigner: MessageSigner - private readonly config: Pick + private readonly config: Pick private firstMessage = true constructor(opts: MessageFactoryOptions) { @@ -69,37 +69,49 @@ export class MessageFactory { explicitPartition?: number ): Promise { const publisherId = await this.getPublisherId(metadata) - const isPublisher = await this.streamRegistry.isStreamPublisher(this.streamId, publisherId) - if (!isPublisher) { - this.streamRegistry.invalidatePermissionCaches(this.streamId) - throw new StreamrClientError(`You don't have permission to publish to this stream. Using address: ${publisherId}`, 'MISSING_PERMISSION') + if (this.config.validation.permissions) { + const isPublisher = await this.streamRegistry.isStreamPublisher(this.streamId, publisherId) + if (!isPublisher) { + this.streamRegistry.invalidatePermissionCaches(this.streamId) + throw new StreamrClientError( + `You don't have permission to publish to this stream. Using address: ${publisherId}`, 'MISSING_PERMISSION' + ) + } } - - const streamMetadata = await this.streamRegistry.getStreamMetadata(this.streamId) - const partitionCount = getPartitionCount(streamMetadata) let partition - if (explicitPartition !== undefined) { - if ((explicitPartition < 0 || explicitPartition >= partitionCount)) { - throw new Error(`Partition ${explicitPartition} is out of range (0..${partitionCount - 1})`) - } - if (metadata.partitionKey !== undefined) { - throw new Error('Invalid combination of "partition" and "partitionKey"') + if (!this.config.validation.partitions) { + if (explicitPartition === undefined) { + throw new Error(`Explicit partition must be set when partition validation is disabled`) } partition = explicitPartition } else { - partition = (metadata.partitionKey !== undefined) - ? keyToArrayIndex(partitionCount, metadata.partitionKey) - : this.getDefaultPartition(partitionCount) + const streamMetadata = await this.streamRegistry.getStreamMetadata(this.streamId) + const partitionCount = getPartitionCount(streamMetadata) + if (explicitPartition !== undefined) { + if ((explicitPartition < 0 || explicitPartition >= partitionCount)) { + throw new Error(`Partition ${explicitPartition} is out of range (0..${partitionCount - 1})`) + } + if (metadata.partitionKey !== undefined) { + throw new Error('Invalid combination of "partition" and "partitionKey"') + } + partition = explicitPartition + } else { + partition = (metadata.partitionKey !== undefined) + ? keyToArrayIndex(partitionCount, metadata.partitionKey) + : this.getDefaultPartition(partitionCount) + } } - const msgChainId = metadata.msgChainId ?? await this.defaultMessageChainIds.get(partition) const msgChainKey = formLookupKey([partition, msgChainId]) const prevMsgRef = this.prevMsgRefs.get(msgChainKey) const msgRef = createMessageRef(metadata.timestamp, prevMsgRef) this.prevMsgRefs.set(msgChainKey, msgRef) const messageId = new MessageID(this.streamId, partition, msgRef.timestamp, msgRef.sequenceNumber, publisherId, msgChainId) - - const encryptionType = (await this.streamRegistry.hasPublicSubscribePermission(this.streamId)) ? EncryptionType.NONE : EncryptionType.AES + const encryptionType = this.config.validation.permissions + ? await this.streamRegistry.hasPublicSubscribePermission(this.streamId) + ? EncryptionType.NONE + : EncryptionType.AES + : EncryptionType.AES if (!isCompliantEncryptionType(encryptionType, this.config)) { throw new StreamrClientError( `Publishing to stream ${this.streamId} was prevented because configuration requires encryption!`, diff --git a/packages/sdk/src/subscribe/messagePipeline.ts b/packages/sdk/src/subscribe/messagePipeline.ts index ed3bbc22ab..486abc5c57 100644 --- a/packages/sdk/src/subscribe/messagePipeline.ts +++ b/packages/sdk/src/subscribe/messagePipeline.ts @@ -29,7 +29,7 @@ export interface MessagePipelineOptions { signatureValidator: SignatureValidator groupKeyManager: GroupKeyManager // eslint-disable-next-line max-len - config: Pick + config: Pick destroySignal: DestroySignal loggerFactory: LoggerFactory } @@ -52,7 +52,7 @@ export const createMessagePipeline = (opts: MessagePipelineOptions): PushPipelin const messageStream = new PushPipeline const msgChainUtil = new MsgChainUtil(async (msg) => { - await validateStreamMessage(msg, opts.streamRegistry, opts.signatureValidator) + await validateStreamMessage(msg, opts.streamRegistry, opts.signatureValidator, opts.config) if (msg.encryptionType !== EncryptionType.NONE && !isCompliantEncryptionType(msg.encryptionType, opts.config)) { throw new StreamrClientError(`A message in stream ${ diff --git a/packages/sdk/src/utils/validateStreamMessage.ts b/packages/sdk/src/utils/validateStreamMessage.ts index 4bc10808af..43cc9da218 100644 --- a/packages/sdk/src/utils/validateStreamMessage.ts +++ b/packages/sdk/src/utils/validateStreamMessage.ts @@ -5,13 +5,15 @@ import { SignatureValidator } from '../signature/SignatureValidator' import { getPartitionCount } from '../StreamMetadata' import { StreamrClientError } from '../StreamrClientError' import { GroupKeyRequest, GroupKeyResponse } from '@streamr/trackerless-network' +import { StrictStreamrClientConfig } from '../Config' export const validateStreamMessage = async ( msg: StreamMessage, streamRegistry: StreamRegistry, - signatureValidator: SignatureValidator + signatureValidator: SignatureValidator, + config: Pick ): Promise => { - await doValidate(msg, streamRegistry, signatureValidator).catch((err: any) => { + await doValidate(msg, streamRegistry, signatureValidator, config).catch((err: any) => { // all StreamMessageError already have this streamMessage, maybe this is // here if e.g. contract call fails? TODO is this really needed as // the onError callback in messagePipeline knows which message @@ -33,25 +35,28 @@ export const validateStreamMessage = async ( const doValidate = async ( streamMessage: StreamMessage, streamRegistry: StreamRegistry, - signatureValidator: SignatureValidator + signatureValidator: SignatureValidator, + config: Pick ): Promise => { await signatureValidator.assertSignatureIsValid(streamMessage) switch (streamMessage.messageType) { case StreamMessageType.MESSAGE: - return validateMessage(streamMessage, streamRegistry) + return validateMessage(streamMessage, streamRegistry, config) case StreamMessageType.GROUP_KEY_REQUEST: return validateGroupKeyMessage( streamMessage, toUserId(GroupKeyRequest.fromBinary(streamMessage.content).recipientId), streamMessage.getPublisherId(), - streamRegistry + streamRegistry, + config ) case StreamMessageType.GROUP_KEY_RESPONSE: return validateGroupKeyMessage( streamMessage, streamMessage.getPublisherId(), toUserId(GroupKeyResponse.fromBinary(streamMessage.content).recipientId), - streamRegistry + streamRegistry, + config ) default: throw new StreamrClientError(`Unknown message type: ${streamMessage.messageType}!`, 'ASSERTION_FAILED', streamMessage) @@ -60,22 +65,27 @@ const doValidate = async ( const validateMessage = async ( streamMessage: StreamMessage, - streamRegistry: StreamRegistry + streamRegistry: StreamRegistry, + config: Pick ): Promise => { const streamId = streamMessage.getStreamId() - const streamMetadata = await streamRegistry.getStreamMetadata(streamId) - const partitionCount = getPartitionCount(streamMetadata) - if (streamMessage.getStreamPartition() < 0 || streamMessage.getStreamPartition() >= partitionCount) { - throw new StreamrClientError( - `Partition ${streamMessage.getStreamPartition()} is out of range (0..${partitionCount - 1})`, - 'INVALID_PARTITION', - streamMessage - ) + if (config.validation.partitions) { + const streamMetadata = await streamRegistry.getStreamMetadata(streamId) + const partitionCount = getPartitionCount(streamMetadata) + if (streamMessage.getStreamPartition() < 0 || streamMessage.getStreamPartition() >= partitionCount) { + throw new StreamrClientError( + `Partition ${streamMessage.getStreamPartition()} is out of range (0..${partitionCount - 1})`, + 'INVALID_PARTITION', + streamMessage + ) + } } - const sender = streamMessage.getPublisherId() - const isPublisher = await streamRegistry.isStreamPublisher(streamId, sender) - if (!isPublisher) { - throw new StreamrClientError(`${sender} is not a publisher on stream ${streamId}`, 'MISSING_PERMISSION', streamMessage) + if (config.validation.permissions) { + const sender = streamMessage.getPublisherId() + const isPublisher = await streamRegistry.isStreamPublisher(streamId, sender) + if (!isPublisher) { + throw new StreamrClientError(`${sender} is not a publisher on stream ${streamId}`, 'MISSING_PERMISSION', streamMessage) + } } } @@ -83,15 +93,18 @@ const validateGroupKeyMessage = async ( streamMessage: StreamMessage, expectedPublisherId: UserID, expectedSubscriberId: UserID, - streamRegistry: StreamRegistry + streamRegistry: StreamRegistry, + config: Pick ): Promise => { - const streamId = streamMessage.getStreamId() - const isPublisher = await streamRegistry.isStreamPublisher(streamId, expectedPublisherId) - if (!isPublisher) { - throw new StreamrClientError(`${expectedPublisherId} is not a publisher on stream ${streamId}`, 'MISSING_PERMISSION', streamMessage) - } - const isSubscriber = await streamRegistry.isStreamSubscriber(streamId, expectedSubscriberId) - if (!isSubscriber) { - throw new StreamrClientError(`${expectedSubscriberId} is not a subscriber on stream ${streamId}`, 'MISSING_PERMISSION', streamMessage) + if (config.validation.permissions) { + const streamId = streamMessage.getStreamId() + const isPublisher = await streamRegistry.isStreamPublisher(streamId, expectedPublisherId) + if (!isPublisher) { + throw new StreamrClientError(`${expectedPublisherId} is not a publisher on stream ${streamId}`, 'MISSING_PERMISSION', streamMessage) + } + const isSubscriber = await streamRegistry.isStreamSubscriber(streamId, expectedSubscriberId) + if (!isSubscriber) { + throw new StreamrClientError(`${expectedSubscriberId} is not a subscriber on stream ${streamId}`, 'MISSING_PERMISSION', streamMessage) + } } } diff --git a/packages/sdk/test/integration/Resends.test.ts b/packages/sdk/test/integration/Resends.test.ts index 822791372a..91afeb69c5 100644 --- a/packages/sdk/test/integration/Resends.test.ts +++ b/packages/sdk/test/integration/Resends.test.ts @@ -13,6 +13,7 @@ import { SignatureValidator } from '../../src/signature/SignatureValidator' import { FakeEnvironment } from '../test-utils/fake/FakeEnvironment' import { createGroupKeyQueue, createStreamRegistry } from '../test-utils/utils' import { EthereumKeyPairIdentity } from '../../src/identity/EthereumKeyPairIdentity' +import { createStrictConfig } from '../../src/Config' describe('Resends', () => { @@ -47,7 +48,7 @@ describe('Resends', () => { groupKeyQueue: await createGroupKeyQueue(identity, groupKey), signatureValidator: mock(), messageSigner: new MessageSigner(identity), - config: {}, + config: createStrictConfig() }) // store the encryption key publisher's local group key store await publisher.updateEncryptionKey({ diff --git a/packages/sdk/test/integration/bypass-validation.test.ts b/packages/sdk/test/integration/bypass-validation.test.ts new file mode 100644 index 0000000000..bb7977c769 --- /dev/null +++ b/packages/sdk/test/integration/bypass-validation.test.ts @@ -0,0 +1,30 @@ +import 'reflect-metadata' + +import { StreamPartIDUtils } from '@streamr/utils' +import { FakeEnvironment } from '../test-utils/fake/FakeEnvironment' +import { nextValue } from '../../src/utils/iterators' + +describe('bypass validation', () => { + + it('happy path', async () => { + const environment = new FakeEnvironment() + const config = { + validation: { + permissions: false, + partitions: false + } + } + const publisher = environment.createClient(config) + const subscriber = environment.createClient(config) + const streamPartId = StreamPartIDUtils.parse('test.eth/foo/bar#4') + const subscription = await subscriber.subscribe(streamPartId) + await publisher.publish(streamPartId, { + message: 'hello' + }) + const message = await nextValue(subscription[Symbol.asyncIterator]()) + expect(message!.content).toEqual({ + message: 'hello' + }) + await environment.destroy() + }) +}) diff --git a/packages/sdk/test/integration/gap-fill.test.ts b/packages/sdk/test/integration/gap-fill.test.ts index 324085aed2..9450178495 100644 --- a/packages/sdk/test/integration/gap-fill.test.ts +++ b/packages/sdk/test/integration/gap-fill.test.ts @@ -13,6 +13,7 @@ import { createGroupKeyQueue, createStreamRegistry, createTestStream, startFaili import { Stream } from './../../src/Stream' import { MessageFactory } from './../../src/publish/MessageFactory' import { EthereumKeyPairIdentity } from '../../src/identity/EthereumKeyPairIdentity' +import { createStrictConfig } from '../../src/Config' const GROUP_KEY = GroupKey.generate() @@ -47,7 +48,7 @@ describe('gap fill', () => { groupKeyQueue: await createGroupKeyQueue(identity, GROUP_KEY), signatureValidator: mock(), messageSigner: new MessageSigner(identity), - config: {}, + config: createStrictConfig() }) }) diff --git a/packages/sdk/test/integration/parallel-key-exchange.test.ts b/packages/sdk/test/integration/parallel-key-exchange.test.ts index 2d1ee8d91b..43ddaab0b9 100644 --- a/packages/sdk/test/integration/parallel-key-exchange.test.ts +++ b/packages/sdk/test/integration/parallel-key-exchange.test.ts @@ -16,6 +16,7 @@ import { SignatureValidator } from '../../src/signature/SignatureValidator' import { createGroupKeyQueue, createStreamRegistry } from '../test-utils/utils' import { FakeEnvironment } from './../test-utils/fake/FakeEnvironment' import { EthereumKeyPairIdentity } from '../../src/identity/EthereumKeyPairIdentity' +import { createStrictConfig } from '../../src/Config' const PUBLISHER_COUNT = 50 const MESSAGE_COUNT_PER_PUBLISHER = 3 @@ -75,7 +76,7 @@ describe('parallel key exchange', () => { groupKeyQueue: await createGroupKeyQueue(identity, publisher.groupKey), signatureValidator: mock(), messageSigner: new MessageSigner(identity), - config: {}, + config: createStrictConfig() }) for (let i = 0; i < MESSAGE_COUNT_PER_PUBLISHER; i++) { const msg = await messageFactory.createMessage({ diff --git a/packages/sdk/test/test-utils/utils.ts b/packages/sdk/test/test-utils/utils.ts index 3ab7dcecfa..3993380085 100644 --- a/packages/sdk/test/test-utils/utils.ts +++ b/packages/sdk/test/test-utils/utils.ts @@ -27,7 +27,7 @@ import path from 'path' import { DependencyContainer } from 'tsyringe' import { Identity } from '../../src/identity/Identity' import { EthereumKeyPairIdentity } from '../../src/identity/EthereumKeyPairIdentity' -import { StreamrClientConfig } from '../../src/Config' +import { createStrictConfig, StreamrClientConfig } from '../../src/Config' import { CONFIG_TEST } from '../../src/ConfigTest' import { DestroySignal } from '../../src/DestroySignal' import { PersistenceManager } from '../../src/PersistenceManager' @@ -144,7 +144,7 @@ export const createMockMessage = async ( const identity = EthereumKeyPairIdentity.fromPrivateKey(opts.publisher.privateKey) const factory = new MessageFactory({ identity, - config: CONFIG_TEST, + config: createStrictConfig(CONFIG_TEST), streamId, streamRegistry: createStreamRegistry({ partitionCount: MAX_PARTITION_COUNT, diff --git a/packages/sdk/test/unit/MessageFactory.test.ts b/packages/sdk/test/unit/MessageFactory.test.ts index fbb1d81949..93c17213a4 100644 --- a/packages/sdk/test/unit/MessageFactory.test.ts +++ b/packages/sdk/test/unit/MessageFactory.test.ts @@ -15,6 +15,7 @@ import { createGroupKeyQueue, createStreamRegistry } from '../test-utils/utils' import { StreamMessage, StreamMessageType } from './../../src/protocol/StreamMessage' import { EthereumKeyPairIdentity } from '../../src/identity/EthereumKeyPairIdentity' import { EncryptionType, SignatureType, ContentType } from '@streamr/trackerless-network' +import { StrictStreamrClientConfig } from '../../src/Config' const CONTENT = { foo: 'bar' } const TIMESTAMP = Date.parse('2001-02-03T04:05:06Z') @@ -59,7 +60,12 @@ describe('MessageFactory', () => { groupKeyQueue: await createGroupKeyQueue(identity, GROUP_KEY), signatureValidator: new SignatureValidator(opts?.erc1271ContractFacade ?? mock()), messageSigner: new MessageSigner(identity), - config: {}, + config: { + validation: { + permissions: true, + partitions: true + } + } as StrictStreamrClientConfig, }, opts ) diff --git a/packages/sdk/test/unit/Publisher.test.ts b/packages/sdk/test/unit/Publisher.test.ts index 184ee91f29..e14857f098 100644 --- a/packages/sdk/test/unit/Publisher.test.ts +++ b/packages/sdk/test/unit/Publisher.test.ts @@ -24,7 +24,13 @@ describe('Publisher', () => { identity, mock(), mock(), - mock(), + { + encryption: {}, + validation: { + permissions: true, + partitions: true, + } + } as StrictStreamrClientConfig, ) const streamId = await streamIdBuilder.toStreamID('/test') await expect(async () => { diff --git a/packages/sdk/test/unit/messagePipeline.test.ts b/packages/sdk/test/unit/messagePipeline.test.ts index ad3942833b..ad7c0e5699 100644 --- a/packages/sdk/test/unit/messagePipeline.test.ts +++ b/packages/sdk/test/unit/messagePipeline.test.ts @@ -67,12 +67,16 @@ describe('messagePipeline', () => { get: async () => undefined } as any const destroySignal = new DestroySignal() - const config: Pick = { + const config = { encryption: { keyRequestTimeout: 50, maxKeyRequestsPerSecond: 0 - } as any - } + }, + validation: { + permissions: true, + partitions: true + } + } as StrictStreamrClientConfig streamRegistry = { getStreamMetadata: async () => ({ partitions: 1 }), isStreamPublisher: async () => true, @@ -94,7 +98,7 @@ describe('messagePipeline', () => { new StreamrClientEventEmitter(), destroySignal ), - config: config as any, + config, destroySignal, loggerFactory: mockLoggerFactory(), }) diff --git a/packages/sdk/test/unit/resendSubscription.test.ts b/packages/sdk/test/unit/resendSubscription.test.ts index c56668f267..a12ec4652a 100644 --- a/packages/sdk/test/unit/resendSubscription.test.ts +++ b/packages/sdk/test/unit/resendSubscription.test.ts @@ -14,6 +14,7 @@ import { initResendSubscription } from '../../src/subscribe/resendSubscription' import { PushPipeline } from '../../src/utils/PushPipeline' import { createGroupKeyQueue, createRandomIdentity, createStreamRegistry, mockLoggerFactory } from '../test-utils/utils' import { StreamMessage } from './../../src/protocol/StreamMessage' +import { createStrictConfig } from '../../src/Config' const STREAM_PART_ID = StreamPartIDUtils.parse('stream#0') const MAX_GAP_REQUESTS = 2 @@ -61,7 +62,7 @@ describe('resend subscription', () => { groupKeyQueue: await createGroupKeyQueue(identity), signatureValidator: mock(), messageSigner: new MessageSigner(identity), - config: {}, + config: createStrictConfig(), }) }) diff --git a/packages/sdk/test/unit/validateStreamMessage.test.ts b/packages/sdk/test/unit/validateStreamMessage.test.ts index e53d0f439b..851521fb8c 100644 --- a/packages/sdk/test/unit/validateStreamMessage.test.ts +++ b/packages/sdk/test/unit/validateStreamMessage.test.ts @@ -11,6 +11,7 @@ import { SignatureValidator } from '../../src/signature/SignatureValidator' import { validateStreamMessage } from '../../src/utils/validateStreamMessage' import { createMockMessage } from '../test-utils/utils' import { StreamMessage } from './../../src/protocol/StreamMessage' +import { StrictStreamrClientConfig } from '../../src/Config' const PARTITION_COUNT = 3 @@ -43,7 +44,17 @@ describe('Validator', () => { return userId === publisherWallet.address.toLowerCase() } } - await validateStreamMessage(msg, streamRegistry as any, new SignatureValidator(mock())) + await validateStreamMessage( + msg, + streamRegistry as any, + new SignatureValidator(mock()), + { + validation: { + permissions: true, + partitions: true + } + } as StrictStreamrClientConfig + ) } beforeAll(async () => { diff --git a/packages/sdk/test/unit/validateStreamMessage2.test.ts b/packages/sdk/test/unit/validateStreamMessage2.test.ts index 22dc40985b..1b570fe421 100644 --- a/packages/sdk/test/unit/validateStreamMessage2.test.ts +++ b/packages/sdk/test/unit/validateStreamMessage2.test.ts @@ -13,6 +13,7 @@ import { MOCK_CONTENT, createRandomIdentity } from '../test-utils/utils' import { MessageID } from './../../src/protocol/MessageID' import { MessageRef } from './../../src/protocol/MessageRef' import { StreamMessage, StreamMessageType } from './../../src/protocol/StreamMessage' +import { StrictStreamrClientConfig } from '../../src/Config' const groupKeyRequestToStreamMessage = async ( groupKeyRequest: GroupKeyRequest, @@ -63,11 +64,21 @@ describe('Validator2', () => { const getValidator = () => { return { - validate: (msg: StreamMessage) => validateStreamMessage(msg, { - getStreamMetadata, - isStreamPublisher: (streamId: string, userId: UserID) => isPublisher(userId, streamId), - isStreamSubscriber: (streamId: string, userId: UserID) => isSubscriber(userId, streamId) - } as any, new SignatureValidator(mock())) + validate: (msg: StreamMessage) => validateStreamMessage( + msg, + { + getStreamMetadata, + isStreamPublisher: (streamId: string, userId: UserID) => isPublisher(userId, streamId), + isStreamSubscriber: (streamId: string, userId: UserID) => isSubscriber(userId, streamId) + } as any, + new SignatureValidator(mock()), + { + validation: { + permissions: true, + partitions: true + } + } as StrictStreamrClientConfig + ) } }