@actsecurity/iam-lens 0.1.156
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.txt +661 -0
- package/README.md +203 -0
- package/dist/cjs/cli.d.ts +3 -0
- package/dist/cjs/cli.d.ts.map +1 -0
- package/dist/cjs/cli.js +206 -0
- package/dist/cjs/cli.js.map +1 -0
- package/dist/cjs/collect/client.d.ts +550 -0
- package/dist/cjs/collect/client.d.ts.map +1 -0
- package/dist/cjs/collect/client.js +1119 -0
- package/dist/cjs/collect/client.js.map +1 -0
- package/dist/cjs/collect/collect.d.ts +44 -0
- package/dist/cjs/collect/collect.d.ts.map +1 -0
- package/dist/cjs/collect/collect.js +55 -0
- package/dist/cjs/collect/collect.js.map +1 -0
- package/dist/cjs/collect/inMemoryClient.d.ts +6 -0
- package/dist/cjs/collect/inMemoryClient.d.ts.map +1 -0
- package/dist/cjs/collect/inMemoryClient.js +11 -0
- package/dist/cjs/collect/inMemoryClient.js.map +1 -0
- package/dist/cjs/index.d.ts +13 -0
- package/dist/cjs/index.d.ts.map +1 -0
- package/dist/cjs/index.js +28 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/package.json +3 -0
- package/dist/cjs/principalCan/permission.d.ts +125 -0
- package/dist/cjs/principalCan/permission.d.ts.map +1 -0
- package/dist/cjs/principalCan/permission.js +1229 -0
- package/dist/cjs/principalCan/permission.js.map +1 -0
- package/dist/cjs/principalCan/permissionSet.d.ts +140 -0
- package/dist/cjs/principalCan/permissionSet.d.ts.map +1 -0
- package/dist/cjs/principalCan/permissionSet.js +437 -0
- package/dist/cjs/principalCan/permissionSet.js.map +1 -0
- package/dist/cjs/principalCan/principalCan.d.ts +26 -0
- package/dist/cjs/principalCan/principalCan.d.ts.map +1 -0
- package/dist/cjs/principalCan/principalCan.js +195 -0
- package/dist/cjs/principalCan/principalCan.js.map +1 -0
- package/dist/cjs/principalCan/principalConstraints.d.ts +147 -0
- package/dist/cjs/principalCan/principalConstraints.d.ts.map +1 -0
- package/dist/cjs/principalCan/principalConstraints.js +472 -0
- package/dist/cjs/principalCan/principalConstraints.js.map +1 -0
- package/dist/cjs/principalCan/resources/actions.d.ts +9 -0
- package/dist/cjs/principalCan/resources/actions.d.ts.map +1 -0
- package/dist/cjs/principalCan/resources/actions.js +27 -0
- package/dist/cjs/principalCan/resources/actions.js.map +1 -0
- package/dist/cjs/principalCan/resources/resourceTypes/iamRoles.d.ts +31 -0
- package/dist/cjs/principalCan/resources/resourceTypes/iamRoles.d.ts.map +1 -0
- package/dist/cjs/principalCan/resources/resourceTypes/iamRoles.js +114 -0
- package/dist/cjs/principalCan/resources/resourceTypes/iamRoles.js.map +1 -0
- package/dist/cjs/principalCan/resources/resourceTypes/kmsKeys.d.ts +30 -0
- package/dist/cjs/principalCan/resources/resourceTypes/kmsKeys.d.ts.map +1 -0
- package/dist/cjs/principalCan/resources/resourceTypes/kmsKeys.js +110 -0
- package/dist/cjs/principalCan/resources/resourceTypes/kmsKeys.js.map +1 -0
- package/dist/cjs/principalCan/resources/resourceTypes/s3Buckets.d.ts +18 -0
- package/dist/cjs/principalCan/resources/resourceTypes/s3Buckets.d.ts.map +1 -0
- package/dist/cjs/principalCan/resources/resourceTypes/s3Buckets.js +123 -0
- package/dist/cjs/principalCan/resources/resourceTypes/s3Buckets.js.map +1 -0
- package/dist/cjs/principalCan/resources/statements.d.ts +27 -0
- package/dist/cjs/principalCan/resources/statements.d.ts.map +1 -0
- package/dist/cjs/principalCan/resources/statements.js +119 -0
- package/dist/cjs/principalCan/resources/statements.js.map +1 -0
- package/dist/cjs/principalIndex/makePrincipalIndex.d.ts +8 -0
- package/dist/cjs/principalIndex/makePrincipalIndex.d.ts.map +1 -0
- package/dist/cjs/principalIndex/makePrincipalIndex.js +105 -0
- package/dist/cjs/principalIndex/makePrincipalIndex.js.map +1 -0
- package/dist/cjs/principals.d.ts +54 -0
- package/dist/cjs/principals.d.ts.map +1 -0
- package/dist/cjs/principals.js +143 -0
- package/dist/cjs/principals.js.map +1 -0
- package/dist/cjs/resources.d.ts +32 -0
- package/dist/cjs/resources.d.ts.map +1 -0
- package/dist/cjs/resources.js +71 -0
- package/dist/cjs/resources.js.map +1 -0
- package/dist/cjs/simulate/contextKeys.d.ts +68 -0
- package/dist/cjs/simulate/contextKeys.d.ts.map +1 -0
- package/dist/cjs/simulate/contextKeys.js +293 -0
- package/dist/cjs/simulate/contextKeys.js.map +1 -0
- package/dist/cjs/simulate/simulate.d.ts +72 -0
- package/dist/cjs/simulate/simulate.d.ts.map +1 -0
- package/dist/cjs/simulate/simulate.js +296 -0
- package/dist/cjs/simulate/simulate.js.map +1 -0
- package/dist/cjs/test-datasets/testClient.d.ts +17 -0
- package/dist/cjs/test-datasets/testClient.d.ts.map +1 -0
- package/dist/cjs/test-datasets/testClient.js +58 -0
- package/dist/cjs/test-datasets/testClient.js.map +1 -0
- package/dist/cjs/utils/arn.d.ts +22 -0
- package/dist/cjs/utils/arn.d.ts.map +1 -0
- package/dist/cjs/utils/arn.js +49 -0
- package/dist/cjs/utils/arn.js.map +1 -0
- package/dist/cjs/utils/bitset.d.ts +45 -0
- package/dist/cjs/utils/bitset.d.ts.map +1 -0
- package/dist/cjs/utils/bitset.js +149 -0
- package/dist/cjs/utils/bitset.js.map +1 -0
- package/dist/cjs/utils/dynamicImport.d.ts +31 -0
- package/dist/cjs/utils/dynamicImport.d.ts.map +1 -0
- package/dist/cjs/utils/dynamicImport.js +64 -0
- package/dist/cjs/utils/dynamicImport.js.map +1 -0
- package/dist/cjs/utils/packageVersion.d.ts +5 -0
- package/dist/cjs/utils/packageVersion.d.ts.map +1 -0
- package/dist/cjs/utils/packageVersion.js +25 -0
- package/dist/cjs/utils/packageVersion.js.map +1 -0
- package/dist/cjs/utils/readPackageFile.d.ts +3 -0
- package/dist/cjs/utils/readPackageFile.d.ts.map +1 -0
- package/dist/cjs/utils/readPackageFile.js +16 -0
- package/dist/cjs/utils/readPackageFile.js.map +1 -0
- package/dist/cjs/utils/s3Abac.d.ts +3 -0
- package/dist/cjs/utils/s3Abac.d.ts.map +1 -0
- package/dist/cjs/utils/s3Abac.js +5 -0
- package/dist/cjs/utils/s3Abac.js.map +1 -0
- package/dist/cjs/utils/stringOrFileArgument.d.ts +9 -0
- package/dist/cjs/utils/stringOrFileArgument.d.ts.map +1 -0
- package/dist/cjs/utils/stringOrFileArgument.js +25 -0
- package/dist/cjs/utils/stringOrFileArgument.js.map +1 -0
- package/dist/cjs/utils/sts.d.ts +2 -0
- package/dist/cjs/utils/sts.d.ts.map +1 -0
- package/dist/cjs/utils/sts.js +9 -0
- package/dist/cjs/utils/sts.js.map +1 -0
- package/dist/cjs/utils/testUtils.d.ts +56 -0
- package/dist/cjs/utils/testUtils.d.ts.map +1 -0
- package/dist/cjs/utils/testUtils.js +95 -0
- package/dist/cjs/utils/testUtils.js.map +1 -0
- package/dist/cjs/utils/workerScript.d.ts +8 -0
- package/dist/cjs/utils/workerScript.d.ts.map +1 -0
- package/dist/cjs/utils/workerScript.js +23 -0
- package/dist/cjs/utils/workerScript.js.map +1 -0
- package/dist/cjs/whoCan/WhoCanMainThreadWorker.d.ts +73 -0
- package/dist/cjs/whoCan/WhoCanMainThreadWorker.d.ts.map +1 -0
- package/dist/cjs/whoCan/WhoCanMainThreadWorker.js +80 -0
- package/dist/cjs/whoCan/WhoCanMainThreadWorker.js.map +1 -0
- package/dist/cjs/whoCan/WhoCanProcessor.d.ts +400 -0
- package/dist/cjs/whoCan/WhoCanProcessor.d.ts.map +1 -0
- package/dist/cjs/whoCan/WhoCanProcessor.js +1088 -0
- package/dist/cjs/whoCan/WhoCanProcessor.js.map +1 -0
- package/dist/cjs/whoCan/WhoCanWorker.d.ts +87 -0
- package/dist/cjs/whoCan/WhoCanWorker.d.ts.map +1 -0
- package/dist/cjs/whoCan/WhoCanWorker.js +201 -0
- package/dist/cjs/whoCan/WhoCanWorker.js.map +1 -0
- package/dist/cjs/whoCan/WhoCanWorkerThreadWorker.d.ts +2 -0
- package/dist/cjs/whoCan/WhoCanWorkerThreadWorker.d.ts.map +1 -0
- package/dist/cjs/whoCan/WhoCanWorkerThreadWorker.js +189 -0
- package/dist/cjs/whoCan/WhoCanWorkerThreadWorker.js.map +1 -0
- package/dist/cjs/whoCan/principalArnFilter.d.ts +84 -0
- package/dist/cjs/whoCan/principalArnFilter.d.ts.map +1 -0
- package/dist/cjs/whoCan/principalArnFilter.js +258 -0
- package/dist/cjs/whoCan/principalArnFilter.js.map +1 -0
- package/dist/cjs/whoCan/principalScope.d.ts +35 -0
- package/dist/cjs/whoCan/principalScope.d.ts.map +1 -0
- package/dist/cjs/whoCan/principalScope.js +104 -0
- package/dist/cjs/whoCan/principalScope.js.map +1 -0
- package/dist/cjs/whoCan/requestAnalysis.d.ts +73 -0
- package/dist/cjs/whoCan/requestAnalysis.d.ts.map +1 -0
- package/dist/cjs/whoCan/requestAnalysis.js +87 -0
- package/dist/cjs/whoCan/requestAnalysis.js.map +1 -0
- package/dist/cjs/whoCan/untrustingActions.d.ts +7 -0
- package/dist/cjs/whoCan/untrustingActions.d.ts.map +1 -0
- package/dist/cjs/whoCan/untrustingActions.js +30 -0
- package/dist/cjs/whoCan/untrustingActions.js.map +1 -0
- package/dist/cjs/whoCan/whoCan.d.ts +295 -0
- package/dist/cjs/whoCan/whoCan.d.ts.map +1 -0
- package/dist/cjs/whoCan/whoCan.js +574 -0
- package/dist/cjs/whoCan/whoCan.js.map +1 -0
- package/dist/cjs/whoCan/workerBootstrapPlugin.d.ts +17 -0
- package/dist/cjs/whoCan/workerBootstrapPlugin.d.ts.map +1 -0
- package/dist/cjs/whoCan/workerBootstrapPlugin.js +3 -0
- package/dist/cjs/whoCan/workerBootstrapPlugin.js.map +1 -0
- package/dist/cjs/workers/ArrayStreamingWorkQueue.d.ts +15 -0
- package/dist/cjs/workers/ArrayStreamingWorkQueue.d.ts.map +1 -0
- package/dist/cjs/workers/ArrayStreamingWorkQueue.js +35 -0
- package/dist/cjs/workers/ArrayStreamingWorkQueue.js.map +1 -0
- package/dist/cjs/workers/JobRunner.d.ts +47 -0
- package/dist/cjs/workers/JobRunner.d.ts.map +1 -0
- package/dist/cjs/workers/JobRunner.js +101 -0
- package/dist/cjs/workers/JobRunner.js.map +1 -0
- package/dist/cjs/workers/RingQueue.d.ts +14 -0
- package/dist/cjs/workers/RingQueue.d.ts.map +1 -0
- package/dist/cjs/workers/RingQueue.js +43 -0
- package/dist/cjs/workers/RingQueue.js.map +1 -0
- package/dist/cjs/workers/SharedArrayBufferMainCache.d.ts +10 -0
- package/dist/cjs/workers/SharedArrayBufferMainCache.d.ts.map +1 -0
- package/dist/cjs/workers/SharedArrayBufferMainCache.js +37 -0
- package/dist/cjs/workers/SharedArrayBufferMainCache.js.map +1 -0
- package/dist/cjs/workers/SharedArrayBufferWorkerCache.d.ts +10 -0
- package/dist/cjs/workers/SharedArrayBufferWorkerCache.d.ts.map +1 -0
- package/dist/cjs/workers/SharedArrayBufferWorkerCache.js +49 -0
- package/dist/cjs/workers/SharedArrayBufferWorkerCache.js.map +1 -0
- package/dist/cjs/workers/StreamingWorkQueue.d.ts +18 -0
- package/dist/cjs/workers/StreamingWorkQueue.d.ts.map +1 -0
- package/dist/cjs/workers/StreamingWorkQueue.js +67 -0
- package/dist/cjs/workers/StreamingWorkQueue.js.map +1 -0
- package/dist/cjs/workers/buffers.d.ts +4 -0
- package/dist/cjs/workers/buffers.d.ts.map +1 -0
- package/dist/cjs/workers/buffers.js +26 -0
- package/dist/cjs/workers/buffers.js.map +1 -0
- package/dist/esm/cli.d.ts +3 -0
- package/dist/esm/cli.d.ts.map +1 -0
- package/dist/esm/cli.js +204 -0
- package/dist/esm/cli.js.map +1 -0
- package/dist/esm/collect/client.d.ts +550 -0
- package/dist/esm/collect/client.d.ts.map +1 -0
- package/dist/esm/collect/client.js +1114 -0
- package/dist/esm/collect/client.js.map +1 -0
- package/dist/esm/collect/collect.d.ts +44 -0
- package/dist/esm/collect/collect.d.ts.map +1 -0
- package/dist/esm/collect/collect.js +51 -0
- package/dist/esm/collect/collect.js.map +1 -0
- package/dist/esm/collect/inMemoryClient.d.ts +6 -0
- package/dist/esm/collect/inMemoryClient.d.ts.map +1 -0
- package/dist/esm/collect/inMemoryClient.js +8 -0
- package/dist/esm/collect/inMemoryClient.js.map +1 -0
- package/dist/esm/index.d.ts +13 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +11 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/package.json +3 -0
- package/dist/esm/principalCan/permission.d.ts +125 -0
- package/dist/esm/principalCan/permission.d.ts.map +1 -0
- package/dist/esm/principalCan/permission.js +1212 -0
- package/dist/esm/principalCan/permission.js.map +1 -0
- package/dist/esm/principalCan/permissionSet.d.ts +140 -0
- package/dist/esm/principalCan/permissionSet.d.ts.map +1 -0
- package/dist/esm/principalCan/permissionSet.js +429 -0
- package/dist/esm/principalCan/permissionSet.js.map +1 -0
- package/dist/esm/principalCan/principalCan.d.ts +26 -0
- package/dist/esm/principalCan/principalCan.d.ts.map +1 -0
- package/dist/esm/principalCan/principalCan.js +193 -0
- package/dist/esm/principalCan/principalCan.js.map +1 -0
- package/dist/esm/principalCan/principalConstraints.d.ts +147 -0
- package/dist/esm/principalCan/principalConstraints.d.ts.map +1 -0
- package/dist/esm/principalCan/principalConstraints.js +456 -0
- package/dist/esm/principalCan/principalConstraints.js.map +1 -0
- package/dist/esm/principalCan/resources/actions.d.ts +9 -0
- package/dist/esm/principalCan/resources/actions.d.ts.map +1 -0
- package/dist/esm/principalCan/resources/actions.js +24 -0
- package/dist/esm/principalCan/resources/actions.js.map +1 -0
- package/dist/esm/principalCan/resources/resourceTypes/iamRoles.d.ts +31 -0
- package/dist/esm/principalCan/resources/resourceTypes/iamRoles.d.ts.map +1 -0
- package/dist/esm/principalCan/resources/resourceTypes/iamRoles.js +110 -0
- package/dist/esm/principalCan/resources/resourceTypes/iamRoles.js.map +1 -0
- package/dist/esm/principalCan/resources/resourceTypes/kmsKeys.d.ts +30 -0
- package/dist/esm/principalCan/resources/resourceTypes/kmsKeys.d.ts.map +1 -0
- package/dist/esm/principalCan/resources/resourceTypes/kmsKeys.js +106 -0
- package/dist/esm/principalCan/resources/resourceTypes/kmsKeys.js.map +1 -0
- package/dist/esm/principalCan/resources/resourceTypes/s3Buckets.d.ts +18 -0
- package/dist/esm/principalCan/resources/resourceTypes/s3Buckets.d.ts.map +1 -0
- package/dist/esm/principalCan/resources/resourceTypes/s3Buckets.js +120 -0
- package/dist/esm/principalCan/resources/resourceTypes/s3Buckets.js.map +1 -0
- package/dist/esm/principalCan/resources/statements.d.ts +27 -0
- package/dist/esm/principalCan/resources/statements.d.ts.map +1 -0
- package/dist/esm/principalCan/resources/statements.js +117 -0
- package/dist/esm/principalCan/resources/statements.js.map +1 -0
- package/dist/esm/principalIndex/makePrincipalIndex.d.ts +8 -0
- package/dist/esm/principalIndex/makePrincipalIndex.d.ts.map +1 -0
- package/dist/esm/principalIndex/makePrincipalIndex.js +104 -0
- package/dist/esm/principalIndex/makePrincipalIndex.js.map +1 -0
- package/dist/esm/principals.d.ts +54 -0
- package/dist/esm/principals.d.ts.map +1 -0
- package/dist/esm/principals.js +133 -0
- package/dist/esm/principals.js.map +1 -0
- package/dist/esm/resources.d.ts +32 -0
- package/dist/esm/resources.d.ts.map +1 -0
- package/dist/esm/resources.js +69 -0
- package/dist/esm/resources.js.map +1 -0
- package/dist/esm/simulate/contextKeys.d.ts +68 -0
- package/dist/esm/simulate/contextKeys.d.ts.map +1 -0
- package/dist/esm/simulate/contextKeys.js +287 -0
- package/dist/esm/simulate/contextKeys.js.map +1 -0
- package/dist/esm/simulate/simulate.d.ts +72 -0
- package/dist/esm/simulate/simulate.d.ts.map +1 -0
- package/dist/esm/simulate/simulate.js +295 -0
- package/dist/esm/simulate/simulate.js.map +1 -0
- package/dist/esm/test-datasets/testClient.d.ts +17 -0
- package/dist/esm/test-datasets/testClient.d.ts.map +1 -0
- package/dist/esm/test-datasets/testClient.js +56 -0
- package/dist/esm/test-datasets/testClient.js.map +1 -0
- package/dist/esm/utils/arn.d.ts +22 -0
- package/dist/esm/utils/arn.d.ts.map +1 -0
- package/dist/esm/utils/arn.js +43 -0
- package/dist/esm/utils/arn.js.map +1 -0
- package/dist/esm/utils/bitset.d.ts +45 -0
- package/dist/esm/utils/bitset.d.ts.map +1 -0
- package/dist/esm/utils/bitset.js +141 -0
- package/dist/esm/utils/bitset.js.map +1 -0
- package/dist/esm/utils/dynamicImport.d.ts +31 -0
- package/dist/esm/utils/dynamicImport.d.ts.map +1 -0
- package/dist/esm/utils/dynamicImport.js +60 -0
- package/dist/esm/utils/dynamicImport.js.map +1 -0
- package/dist/esm/utils/packageVersion.d.ts +5 -0
- package/dist/esm/utils/packageVersion.d.ts.map +1 -0
- package/dist/esm/utils/packageVersion.js +22 -0
- package/dist/esm/utils/packageVersion.js.map +1 -0
- package/dist/esm/utils/readPackageFile.d.ts +3 -0
- package/dist/esm/utils/readPackageFile.d.ts.map +1 -0
- package/dist/esm/utils/readPackageFile.js +15 -0
- package/dist/esm/utils/readPackageFile.js.map +1 -0
- package/dist/esm/utils/readPackageFileEsm.d.ts.map +1 -0
- package/dist/esm/utils/s3Abac.d.ts +3 -0
- package/dist/esm/utils/s3Abac.d.ts.map +1 -0
- package/dist/esm/utils/s3Abac.js +2 -0
- package/dist/esm/utils/s3Abac.js.map +1 -0
- package/dist/esm/utils/stringOrFileArgument.d.ts +9 -0
- package/dist/esm/utils/stringOrFileArgument.d.ts.map +1 -0
- package/dist/esm/utils/stringOrFileArgument.js +22 -0
- package/dist/esm/utils/stringOrFileArgument.js.map +1 -0
- package/dist/esm/utils/sts.d.ts +2 -0
- package/dist/esm/utils/sts.d.ts.map +1 -0
- package/dist/esm/utils/sts.js +6 -0
- package/dist/esm/utils/sts.js.map +1 -0
- package/dist/esm/utils/testUtils.d.ts +56 -0
- package/dist/esm/utils/testUtils.d.ts.map +1 -0
- package/dist/esm/utils/testUtils.js +90 -0
- package/dist/esm/utils/testUtils.js.map +1 -0
- package/dist/esm/utils/workerScript.d.ts +8 -0
- package/dist/esm/utils/workerScript.d.ts.map +1 -0
- package/dist/esm/utils/workerScript.js +18 -0
- package/dist/esm/utils/workerScript.js.map +1 -0
- package/dist/esm/utils/workerScriptEsm.d.ts.map +1 -0
- package/dist/esm/whoCan/WhoCanMainThreadWorker.d.ts +73 -0
- package/dist/esm/whoCan/WhoCanMainThreadWorker.d.ts.map +1 -0
- package/dist/esm/whoCan/WhoCanMainThreadWorker.js +81 -0
- package/dist/esm/whoCan/WhoCanMainThreadWorker.js.map +1 -0
- package/dist/esm/whoCan/WhoCanProcessor.d.ts +400 -0
- package/dist/esm/whoCan/WhoCanProcessor.d.ts.map +1 -0
- package/dist/esm/whoCan/WhoCanProcessor.js +1080 -0
- package/dist/esm/whoCan/WhoCanProcessor.js.map +1 -0
- package/dist/esm/whoCan/WhoCanWorker.d.ts +87 -0
- package/dist/esm/whoCan/WhoCanWorker.d.ts.map +1 -0
- package/dist/esm/whoCan/WhoCanWorker.js +198 -0
- package/dist/esm/whoCan/WhoCanWorker.js.map +1 -0
- package/dist/esm/whoCan/WhoCanWorkerThreadWorker.d.ts +2 -0
- package/dist/esm/whoCan/WhoCanWorkerThreadWorker.d.ts.map +1 -0
- package/dist/esm/whoCan/WhoCanWorkerThreadWorker.js +193 -0
- package/dist/esm/whoCan/WhoCanWorkerThreadWorker.js.map +1 -0
- package/dist/esm/whoCan/principalArnFilter.d.ts +84 -0
- package/dist/esm/whoCan/principalArnFilter.d.ts.map +1 -0
- package/dist/esm/whoCan/principalArnFilter.js +253 -0
- package/dist/esm/whoCan/principalArnFilter.js.map +1 -0
- package/dist/esm/whoCan/principalScope.d.ts +35 -0
- package/dist/esm/whoCan/principalScope.d.ts.map +1 -0
- package/dist/esm/whoCan/principalScope.js +102 -0
- package/dist/esm/whoCan/principalScope.js.map +1 -0
- package/dist/esm/whoCan/requestAnalysis.d.ts +73 -0
- package/dist/esm/whoCan/requestAnalysis.d.ts.map +1 -0
- package/dist/esm/whoCan/requestAnalysis.js +83 -0
- package/dist/esm/whoCan/requestAnalysis.js.map +1 -0
- package/dist/esm/whoCan/untrustingActions.d.ts +7 -0
- package/dist/esm/whoCan/untrustingActions.d.ts.map +1 -0
- package/dist/esm/whoCan/untrustingActions.js +27 -0
- package/dist/esm/whoCan/untrustingActions.js.map +1 -0
- package/dist/esm/whoCan/whoCan.d.ts +295 -0
- package/dist/esm/whoCan/whoCan.d.ts.map +1 -0
- package/dist/esm/whoCan/whoCan.js +571 -0
- package/dist/esm/whoCan/whoCan.js.map +1 -0
- package/dist/esm/whoCan/workerBootstrapPlugin.d.ts +17 -0
- package/dist/esm/whoCan/workerBootstrapPlugin.d.ts.map +1 -0
- package/dist/esm/whoCan/workerBootstrapPlugin.js +2 -0
- package/dist/esm/whoCan/workerBootstrapPlugin.js.map +1 -0
- package/dist/esm/workers/ArrayStreamingWorkQueue.d.ts +15 -0
- package/dist/esm/workers/ArrayStreamingWorkQueue.d.ts.map +1 -0
- package/dist/esm/workers/ArrayStreamingWorkQueue.js +31 -0
- package/dist/esm/workers/ArrayStreamingWorkQueue.js.map +1 -0
- package/dist/esm/workers/JobRunner.d.ts +47 -0
- package/dist/esm/workers/JobRunner.d.ts.map +1 -0
- package/dist/esm/workers/JobRunner.js +94 -0
- package/dist/esm/workers/JobRunner.js.map +1 -0
- package/dist/esm/workers/RingQueue.d.ts +14 -0
- package/dist/esm/workers/RingQueue.d.ts.map +1 -0
- package/dist/esm/workers/RingQueue.js +36 -0
- package/dist/esm/workers/RingQueue.js.map +1 -0
- package/dist/esm/workers/SharedArrayBufferMainCache.d.ts +10 -0
- package/dist/esm/workers/SharedArrayBufferMainCache.d.ts.map +1 -0
- package/dist/esm/workers/SharedArrayBufferMainCache.js +34 -0
- package/dist/esm/workers/SharedArrayBufferMainCache.js.map +1 -0
- package/dist/esm/workers/SharedArrayBufferWorkerCache.d.ts +10 -0
- package/dist/esm/workers/SharedArrayBufferWorkerCache.d.ts.map +1 -0
- package/dist/esm/workers/SharedArrayBufferWorkerCache.js +46 -0
- package/dist/esm/workers/SharedArrayBufferWorkerCache.js.map +1 -0
- package/dist/esm/workers/StreamingWorkQueue.d.ts +18 -0
- package/dist/esm/workers/StreamingWorkQueue.d.ts.map +1 -0
- package/dist/esm/workers/StreamingWorkQueue.js +61 -0
- package/dist/esm/workers/StreamingWorkQueue.js.map +1 -0
- package/dist/esm/workers/buffers.d.ts +4 -0
- package/dist/esm/workers/buffers.d.ts.map +1 -0
- package/dist/esm/workers/buffers.js +21 -0
- package/dist/esm/workers/buffers.js.map +1 -0
- package/package.json +132 -0
|
@@ -0,0 +1,1229 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Permission = void 0;
|
|
4
|
+
exports.unionConditions = unionConditions;
|
|
5
|
+
exports.intersectConditions = intersectConditions;
|
|
6
|
+
exports.normalizeConditionKeys = normalizeConditionKeys;
|
|
7
|
+
exports.invertConditions = invertConditions;
|
|
8
|
+
exports.applyDenyConditionsToAllow = applyDenyConditionsToAllow;
|
|
9
|
+
const principalConstraints_js_1 = require("./principalConstraints.js");
|
|
10
|
+
/**
|
|
11
|
+
* Convert an AWS wildcard ARN pattern (e.g. "arn:aws:s3:::bucket/*") into a RegExp.
|
|
12
|
+
*/
|
|
13
|
+
function wildcardToRegex(pattern) {
|
|
14
|
+
const parts = pattern.split('*').map((s) => s.replace(/[-/\\^$+?.()|[\]{}]/g, '\\$&'));
|
|
15
|
+
return new RegExp('^' + parts.join('.*') + '$');
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* An immutable representation of a single permission for a specific action.
|
|
19
|
+
*
|
|
20
|
+
* This will eventually have methods like "merge with another permission",
|
|
21
|
+
* "check if overlaps with another permission", "subtract a deny permission",
|
|
22
|
+
* etc and those will all return a new Permission instance.
|
|
23
|
+
*/
|
|
24
|
+
class Permission {
|
|
25
|
+
effect;
|
|
26
|
+
service;
|
|
27
|
+
action;
|
|
28
|
+
resource;
|
|
29
|
+
notResource;
|
|
30
|
+
conditions;
|
|
31
|
+
principal;
|
|
32
|
+
notPrincipal;
|
|
33
|
+
constructor(effect, service, action, resource, notResource, conditions, principal = undefined, notPrincipal = undefined) {
|
|
34
|
+
this.effect = effect;
|
|
35
|
+
this.service = service;
|
|
36
|
+
this.action = action;
|
|
37
|
+
this.resource = resource;
|
|
38
|
+
this.notResource = notResource;
|
|
39
|
+
this.conditions = conditions;
|
|
40
|
+
this.principal = principal;
|
|
41
|
+
this.notPrincipal = notPrincipal;
|
|
42
|
+
if (resource !== undefined && notResource !== undefined) {
|
|
43
|
+
throw new Error('Permission must have a resource or notResource, not both.');
|
|
44
|
+
}
|
|
45
|
+
else if (resource === undefined && notResource === undefined) {
|
|
46
|
+
throw new Error('Permission must have a resource or notResource, one must be defined.');
|
|
47
|
+
}
|
|
48
|
+
(0, principalConstraints_js_1.normalizePrincipalConstraint)(principal, notPrincipal);
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Return this permission's principal constraint normalized for set algebra.
|
|
52
|
+
*
|
|
53
|
+
* @returns The normalized principal constraint.
|
|
54
|
+
*/
|
|
55
|
+
principalConstraint() {
|
|
56
|
+
return (0, principalConstraints_js_1.normalizePrincipalConstraint)(this.principal, this.notPrincipal);
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Returns true if this Permission completely includes the other Permission.
|
|
60
|
+
* Only supports merging of "Allow" permissions (same effect, service, action).
|
|
61
|
+
*/
|
|
62
|
+
includes(other) {
|
|
63
|
+
// 1. Effects, service, and action must match
|
|
64
|
+
if (this.effect !== other.effect ||
|
|
65
|
+
this.service !== other.service ||
|
|
66
|
+
this.action !== other.action) {
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
if (!(0, principalConstraints_js_1.principalIncludes)(this.principalConstraint(), other.principalConstraint())) {
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
// 2. Conditions: every condition in this must be implied by the other permission’s conditions
|
|
73
|
+
// That is, for each operator and context key in this.conditions, other.conditions must have it,
|
|
74
|
+
// and the values must satisfy inclusion logic per operator.
|
|
75
|
+
const condsA = normalizeConditionKeys(this.conditions || {});
|
|
76
|
+
const condsB = normalizeConditionKeys(other.conditions || {});
|
|
77
|
+
for (const op of Object.keys(condsA)) {
|
|
78
|
+
if (!(op in condsB))
|
|
79
|
+
return false;
|
|
80
|
+
const keysA = Object.keys(condsA[op]);
|
|
81
|
+
const keysB = Object.keys(condsB[op]);
|
|
82
|
+
// Every key in A must appear in B
|
|
83
|
+
for (const key of keysA) {
|
|
84
|
+
if (!keysB.includes(key))
|
|
85
|
+
return false;
|
|
86
|
+
const valsA = condsA[op][key];
|
|
87
|
+
const valsB = condsB[op][key];
|
|
88
|
+
const baseOp = conditionBaseOperator(op);
|
|
89
|
+
switch (baseOp) {
|
|
90
|
+
case 'stringequals':
|
|
91
|
+
case 'stringlike':
|
|
92
|
+
case 'arnequals':
|
|
93
|
+
case 'arnlike':
|
|
94
|
+
// other must be at least as restrictive: B_vals ⊆ A_vals
|
|
95
|
+
if (!valsB.every((v) => valsA.includes(v)))
|
|
96
|
+
return false;
|
|
97
|
+
break;
|
|
98
|
+
case 'stringnotequals':
|
|
99
|
+
case 'stringnotlike':
|
|
100
|
+
case 'arnnotequals':
|
|
101
|
+
case 'arnnotlike':
|
|
102
|
+
// other must exclude at least what A excludes: A_vals ⊆ B_vals
|
|
103
|
+
if (!valsA.every((v) => valsB.includes(v)))
|
|
104
|
+
return false;
|
|
105
|
+
break;
|
|
106
|
+
case 'numericlessthan':
|
|
107
|
+
case 'numericlessthanequals':
|
|
108
|
+
// other boundary <= this boundary
|
|
109
|
+
const numA = Number(valsA[0]);
|
|
110
|
+
const numB = Number(valsB[0]);
|
|
111
|
+
if (isNaN(numA) || isNaN(numB))
|
|
112
|
+
return false;
|
|
113
|
+
if (numB > numA)
|
|
114
|
+
return false;
|
|
115
|
+
break;
|
|
116
|
+
case 'numericgreaterthan':
|
|
117
|
+
case 'numericgreaterthanequals':
|
|
118
|
+
// other boundary >= this boundary
|
|
119
|
+
const ngA = Number(valsA[0]);
|
|
120
|
+
const ngB = Number(valsB[0]);
|
|
121
|
+
if (isNaN(ngA) || isNaN(ngB))
|
|
122
|
+
return false;
|
|
123
|
+
if (ngB < ngA)
|
|
124
|
+
return false;
|
|
125
|
+
break;
|
|
126
|
+
case 'bool':
|
|
127
|
+
// other must have the same boolean value
|
|
128
|
+
if (valsA[0] !== valsB[0])
|
|
129
|
+
return false;
|
|
130
|
+
break;
|
|
131
|
+
case 'ipaddress':
|
|
132
|
+
case 'notipaddress':
|
|
133
|
+
// every CIDR in B must be contained in some CIDR in A
|
|
134
|
+
for (const cidrB of valsB) {
|
|
135
|
+
if (!valsA.some((cidrA) => cidrA === cidrB)) {
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
break;
|
|
140
|
+
case 'datelessthan':
|
|
141
|
+
case 'datelessthanequals':
|
|
142
|
+
// other date <= this date lexically (ISO)
|
|
143
|
+
const dA = valsA[0];
|
|
144
|
+
const dB = valsB[0];
|
|
145
|
+
if (dB > dA)
|
|
146
|
+
return false;
|
|
147
|
+
break;
|
|
148
|
+
case 'dategreaterthan':
|
|
149
|
+
case 'dategreaterthanequals':
|
|
150
|
+
// other date >= this date
|
|
151
|
+
const dgA = valsA[0];
|
|
152
|
+
const dgB = valsB[0];
|
|
153
|
+
if (dgB < dgA)
|
|
154
|
+
return false;
|
|
155
|
+
break;
|
|
156
|
+
default:
|
|
157
|
+
return false;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
// 3. Resources / NotResources
|
|
162
|
+
const thisResource = this.resource;
|
|
163
|
+
const thisNotResource = this.notResource;
|
|
164
|
+
const otherResource = other.resource;
|
|
165
|
+
const otherNotResource = other.notResource;
|
|
166
|
+
// 3a. If both have resource[]
|
|
167
|
+
if (thisResource !== undefined && otherResource !== undefined) {
|
|
168
|
+
return otherResource.every((r2) => thisResource.some((r1) => wildcardToRegex(r1).test(r2)));
|
|
169
|
+
}
|
|
170
|
+
// 3b. Both have notResource[]
|
|
171
|
+
if (thisNotResource !== undefined && otherNotResource !== undefined) {
|
|
172
|
+
return thisNotResource.every((n1) => otherNotResource.some((n2) => wildcardToRegex(n1).test(n2)));
|
|
173
|
+
}
|
|
174
|
+
// 3c. A.resource & B.notResource -> B allows almost all, A allows only R1 -> true iff every N2 is matched by some R1
|
|
175
|
+
if (thisResource !== undefined && otherNotResource !== undefined) {
|
|
176
|
+
return otherNotResource.every((n2) => thisResource.some((r1) => wildcardToRegex(r1).test(n2)));
|
|
177
|
+
}
|
|
178
|
+
// 3d. A.notResource & B.resource -> every r2 ∉ N1
|
|
179
|
+
if (thisNotResource !== undefined && otherResource !== undefined) {
|
|
180
|
+
return otherResource.every((r2) => !thisNotResource.some((n1) => wildcardToRegex(n1).test(r2)));
|
|
181
|
+
}
|
|
182
|
+
return false;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Returns the union of this Permission with another.
|
|
186
|
+
* If one includes the other, return the including Permission.
|
|
187
|
+
* Otherwise, attempt to merge conditions and resource/notResource.
|
|
188
|
+
* If merge yields a single Permission, return it; else return both.
|
|
189
|
+
*/
|
|
190
|
+
union(other) {
|
|
191
|
+
// 1. Ensure same effect, service, and action
|
|
192
|
+
if (this.effect !== other.effect ||
|
|
193
|
+
this.service !== other.service ||
|
|
194
|
+
this.action !== other.action) {
|
|
195
|
+
return [this, other];
|
|
196
|
+
}
|
|
197
|
+
// 2. If one includes the other, return the including one
|
|
198
|
+
if (this.includes(other)) {
|
|
199
|
+
return [this];
|
|
200
|
+
}
|
|
201
|
+
if (other.includes(this)) {
|
|
202
|
+
return [other];
|
|
203
|
+
}
|
|
204
|
+
// 3. Attempt to combine conditions
|
|
205
|
+
const condsA = this.conditions || {};
|
|
206
|
+
const condsB = other.conditions || {};
|
|
207
|
+
const mergedConds = unionConditions(condsA, condsB);
|
|
208
|
+
if (mergedConds === null) {
|
|
209
|
+
return [this, other];
|
|
210
|
+
}
|
|
211
|
+
const principalUnions = (0, principalConstraints_js_1.unionPrincipalConstraints)(this.principalConstraint(), other.principalConstraint());
|
|
212
|
+
if (principalUnions.length !== 1) {
|
|
213
|
+
return [this, other];
|
|
214
|
+
}
|
|
215
|
+
const principalArgs = (0, principalConstraints_js_1.principalArgsForConstraint)(principalUnions[0]);
|
|
216
|
+
// 4. Combine resource/notResource (constructor enforces exclusivity)
|
|
217
|
+
const thisResource = this.resource;
|
|
218
|
+
const thisNotResource = this.notResource;
|
|
219
|
+
const otherResource = other.resource;
|
|
220
|
+
const otherNotResource = other.notResource;
|
|
221
|
+
const eff = this.effect;
|
|
222
|
+
const svc = this.service;
|
|
223
|
+
const act = this.action;
|
|
224
|
+
const conds = Object.keys(mergedConds).length > 0 ? mergedConds : undefined;
|
|
225
|
+
// Both have resource[]
|
|
226
|
+
if (thisResource !== undefined && otherResource !== undefined) {
|
|
227
|
+
const union = Array.from(new Set([...thisResource, ...otherResource]));
|
|
228
|
+
return [
|
|
229
|
+
new Permission(eff, svc, act, union, undefined, conds, principalArgs.principal, principalArgs.notPrincipal)
|
|
230
|
+
];
|
|
231
|
+
}
|
|
232
|
+
// Both have notResource[]
|
|
233
|
+
if (thisNotResource !== undefined && otherNotResource !== undefined) {
|
|
234
|
+
// Intersection of both notResource arrays
|
|
235
|
+
const intersection = thisNotResource.filter((n) => otherNotResource.includes(n));
|
|
236
|
+
return [
|
|
237
|
+
new Permission(eff, svc, act, undefined, intersection, conds, principalArgs.principal, principalArgs.notPrincipal)
|
|
238
|
+
];
|
|
239
|
+
}
|
|
240
|
+
// One has resource, other has notResource
|
|
241
|
+
if (thisResource !== undefined && otherNotResource !== undefined) {
|
|
242
|
+
return [
|
|
243
|
+
new Permission(eff, svc, act, thisResource, undefined, conds, principalArgs.principal, principalArgs.notPrincipal),
|
|
244
|
+
new Permission(eff, svc, act, undefined, otherNotResource, conds, principalArgs.principal, principalArgs.notPrincipal)
|
|
245
|
+
];
|
|
246
|
+
}
|
|
247
|
+
if (otherResource !== undefined && thisNotResource !== undefined) {
|
|
248
|
+
return [
|
|
249
|
+
new Permission(eff, svc, act, otherResource, undefined, conds, principalArgs.principal, principalArgs.notPrincipal),
|
|
250
|
+
new Permission(eff, svc, act, undefined, thisNotResource, conds, principalArgs.principal, principalArgs.notPrincipal)
|
|
251
|
+
];
|
|
252
|
+
}
|
|
253
|
+
// Otherwise cannot combine, return both
|
|
254
|
+
return [this, other];
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Returns all representable intersections of this Permission with another.
|
|
258
|
+
*
|
|
259
|
+
* @param other The other Permission to intersect with.
|
|
260
|
+
* @returns New Permissions representing the overlap, or an empty array if there is no intersection.
|
|
261
|
+
*/
|
|
262
|
+
intersections(other) {
|
|
263
|
+
const principalIntersections = (0, principalConstraints_js_1.intersectPrincipalConstraints)(this.principalConstraint(), other.principalConstraint());
|
|
264
|
+
const results = [];
|
|
265
|
+
for (const principalIntersection of principalIntersections) {
|
|
266
|
+
const principalArgs = (0, principalConstraints_js_1.principalArgsForConstraint)(principalIntersection);
|
|
267
|
+
const permissionWithIntersectionPrincipal = new Permission(this.effect, this.service, this.action, this.resource, this.notResource, this.conditions, principalArgs.principal, principalArgs.notPrincipal);
|
|
268
|
+
const intersection = permissionWithIntersectionPrincipal.intersection(other);
|
|
269
|
+
if (intersection) {
|
|
270
|
+
results.push(intersection);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
return results;
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Returns one intersection of this Permission with another.
|
|
277
|
+
*
|
|
278
|
+
* Prefer {@link intersections} when correctness depends on all possible overlaps. This
|
|
279
|
+
* compatibility method returns at most one Permission and may omit additional representable
|
|
280
|
+
* intersections if future principal algebra produces multiple principal residuals.
|
|
281
|
+
*
|
|
282
|
+
* @param other The other Permission to intersect with.
|
|
283
|
+
* @returns A new Permission representing one intersection of other and this, or undefined if there is no intersection.
|
|
284
|
+
*/
|
|
285
|
+
intersection(other) {
|
|
286
|
+
// 1. Must match effect, service, and action
|
|
287
|
+
if (this.effect !== other.effect ||
|
|
288
|
+
this.service !== other.service ||
|
|
289
|
+
this.action !== other.action) {
|
|
290
|
+
// No overlap at all—return a "zero-resource" permission
|
|
291
|
+
return undefined;
|
|
292
|
+
}
|
|
293
|
+
const principalIntersections = (0, principalConstraints_js_1.intersectPrincipalConstraints)(this.principalConstraint(), other.principalConstraint());
|
|
294
|
+
if (principalIntersections.length === 0) {
|
|
295
|
+
return undefined;
|
|
296
|
+
}
|
|
297
|
+
const principalArgs = (0, principalConstraints_js_1.principalArgsForConstraint)(principalIntersections[0]);
|
|
298
|
+
if (this.resource != undefined && other.resource != undefined) {
|
|
299
|
+
// 2. If one includes the other, return the narrower one unless both are NotResource
|
|
300
|
+
if (this.includes(other)) {
|
|
301
|
+
return other;
|
|
302
|
+
}
|
|
303
|
+
if (other.includes(this)) {
|
|
304
|
+
return this;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
// 3. Attempt to intersect/merge conditions
|
|
308
|
+
const a = normalizeConditionKeys(this.conditions || {});
|
|
309
|
+
const b = normalizeConditionKeys(other.conditions || {});
|
|
310
|
+
const allOps = Array.from(new Set([...Object.keys(a), ...Object.keys(b)]));
|
|
311
|
+
const intersectedConds = {};
|
|
312
|
+
for (const op of allOps) {
|
|
313
|
+
const condA = a[op] || {};
|
|
314
|
+
const condB = b[op] || {};
|
|
315
|
+
const allKeys = Array.from(new Set([...Object.keys(condA), ...Object.keys(condB)]));
|
|
316
|
+
intersectedConds[op] = {};
|
|
317
|
+
for (const key of allKeys) {
|
|
318
|
+
const valsA = condA[key] || [];
|
|
319
|
+
const valsB = condB[key] || [];
|
|
320
|
+
// If key appears in both, intersect or combine based on operator
|
|
321
|
+
if (key in condA && key in condB) {
|
|
322
|
+
switch (conditionBaseOperator(op)) {
|
|
323
|
+
case 'stringequals':
|
|
324
|
+
case 'stringlike':
|
|
325
|
+
case 'arnequals':
|
|
326
|
+
case 'arnlike': {
|
|
327
|
+
// Intersection of string lists
|
|
328
|
+
const common = valsA.filter((v) => valsB.includes(v));
|
|
329
|
+
if (common.length === 0) {
|
|
330
|
+
return undefined;
|
|
331
|
+
}
|
|
332
|
+
intersectedConds[op][key] = common;
|
|
333
|
+
break;
|
|
334
|
+
}
|
|
335
|
+
case 'stringnotequals':
|
|
336
|
+
case 'stringnotlike':
|
|
337
|
+
case 'arnnotequals':
|
|
338
|
+
case 'arnnotlike': {
|
|
339
|
+
// Union of exclusions
|
|
340
|
+
intersectedConds[op][key] = Array.from(new Set([...valsA, ...valsB]));
|
|
341
|
+
break;
|
|
342
|
+
}
|
|
343
|
+
case 'numericlessthan':
|
|
344
|
+
case 'numericlessthanequals': {
|
|
345
|
+
const numA = Number(valsA[0]);
|
|
346
|
+
const numB = Number(valsB[0]);
|
|
347
|
+
if (isNaN(numA) || isNaN(numB)) {
|
|
348
|
+
return undefined;
|
|
349
|
+
}
|
|
350
|
+
const boundary = Math.min(numA, numB);
|
|
351
|
+
intersectedConds[op][key] = [String(boundary)];
|
|
352
|
+
break;
|
|
353
|
+
}
|
|
354
|
+
case 'numericgreaterthan':
|
|
355
|
+
case 'numericgreaterthanequals': {
|
|
356
|
+
const ngA = Number(valsA[0]);
|
|
357
|
+
const ngB = Number(valsB[0]);
|
|
358
|
+
if (isNaN(ngA) || isNaN(ngB)) {
|
|
359
|
+
return undefined;
|
|
360
|
+
}
|
|
361
|
+
const boundary = Math.max(ngA, ngB);
|
|
362
|
+
intersectedConds[op][key] = [String(boundary)];
|
|
363
|
+
break;
|
|
364
|
+
}
|
|
365
|
+
case 'bool': {
|
|
366
|
+
if (valsA[0] !== valsB[0]) {
|
|
367
|
+
return undefined;
|
|
368
|
+
}
|
|
369
|
+
intersectedConds[op][key] = [valsA[0]];
|
|
370
|
+
break;
|
|
371
|
+
}
|
|
372
|
+
case 'ipaddress':
|
|
373
|
+
case 'notipaddress': {
|
|
374
|
+
const common = valsA.filter((cidr) => valsB.includes(cidr));
|
|
375
|
+
if (common.length === 0) {
|
|
376
|
+
return undefined;
|
|
377
|
+
}
|
|
378
|
+
intersectedConds[op][key] = common;
|
|
379
|
+
break;
|
|
380
|
+
}
|
|
381
|
+
case 'datelessthan':
|
|
382
|
+
case 'datelessthanequals': {
|
|
383
|
+
const dA = valsA[0];
|
|
384
|
+
const dB = valsB[0];
|
|
385
|
+
intersectedConds[op][key] = [dA < dB ? dA : dB];
|
|
386
|
+
break;
|
|
387
|
+
}
|
|
388
|
+
case 'dategreaterthan':
|
|
389
|
+
case 'dategreaterthanequals': {
|
|
390
|
+
const dgA = valsA[0];
|
|
391
|
+
const dgB = valsB[0];
|
|
392
|
+
intersectedConds[op][key] = [dgA > dgB ? dgA : dgB];
|
|
393
|
+
break;
|
|
394
|
+
}
|
|
395
|
+
default:
|
|
396
|
+
return undefined;
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
else {
|
|
400
|
+
// Key only in one side: carry it through
|
|
401
|
+
intersectedConds[op][key] = key in condA ? Array.from(valsA) : Array.from(valsB);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
// 4. Combine resource/notResource:
|
|
406
|
+
const thisResource = this.resource;
|
|
407
|
+
const thisNotResource = this.notResource;
|
|
408
|
+
const otherResource = other.resource;
|
|
409
|
+
const otherNotResource = other.notResource;
|
|
410
|
+
const eff = this.effect;
|
|
411
|
+
const svc = this.service;
|
|
412
|
+
const act = this.action;
|
|
413
|
+
const conds = Object.keys(intersectedConds).length > 0 ? intersectedConds : undefined;
|
|
414
|
+
// Both have resource[] => intersect patterns
|
|
415
|
+
if (thisResource !== undefined && otherResource !== undefined) {
|
|
416
|
+
// Keep any R1 that matches something in R2, and any R2 that matches something in R1
|
|
417
|
+
const part1 = thisResource.filter((r1) => otherResource.some((r2) => wildcardToRegex(r2).test(r1)));
|
|
418
|
+
const part2 = otherResource.filter((r2) => thisResource.some((r1) => wildcardToRegex(r1).test(r2)));
|
|
419
|
+
const intersectR = Array.from(new Set([...part1, ...part2]));
|
|
420
|
+
if (intersectR.length === 0) {
|
|
421
|
+
return undefined;
|
|
422
|
+
}
|
|
423
|
+
return new Permission(eff, svc, act, intersectR, undefined, conds, principalArgs.principal, principalArgs.notPrincipal);
|
|
424
|
+
}
|
|
425
|
+
// Both have notResource[] => union of exclusions (more restrictive), but remove subsumed patterns
|
|
426
|
+
if (thisNotResource !== undefined && otherNotResource !== undefined) {
|
|
427
|
+
// Compute union of both exclusion lists
|
|
428
|
+
const combined = Array.from(new Set([...thisNotResource, ...otherNotResource]));
|
|
429
|
+
// Remove any pattern that is subsumed by a more general pattern
|
|
430
|
+
const filtered = combined.filter((pat) => !combined.some((otherPat) => otherPat !== pat && wildcardToRegex(otherPat).test(pat)));
|
|
431
|
+
return new Permission(eff, svc, act, undefined, filtered, conds, principalArgs.principal, principalArgs.notPrincipal);
|
|
432
|
+
}
|
|
433
|
+
// One has resource, other has notResource
|
|
434
|
+
const resource = thisResource || otherResource;
|
|
435
|
+
const notResource = thisNotResource || otherNotResource;
|
|
436
|
+
if (resource !== undefined || notResource !== undefined) {
|
|
437
|
+
const filtered = resource.filter((r1) => !notResource.some((n2) => wildcardToRegex(n2).test(r1)));
|
|
438
|
+
if (filtered.length === 0) {
|
|
439
|
+
return undefined;
|
|
440
|
+
}
|
|
441
|
+
return new Permission(eff, svc, act, filtered, undefined, conds, principalArgs.principal, principalArgs.notPrincipal);
|
|
442
|
+
}
|
|
443
|
+
// This should never happen
|
|
444
|
+
return undefined;
|
|
445
|
+
}
|
|
446
|
+
/**
|
|
447
|
+
* Subtract a Deny permission from this Allow permission.
|
|
448
|
+
*
|
|
449
|
+
* Returns the resulting permissions, this can be:
|
|
450
|
+
* - An empty array if the Allow is fully denied by the Deny
|
|
451
|
+
* - A modified Allow permission or multiple Allow permissions
|
|
452
|
+
* - It could also return the original Allow and Deny permission if subtraction cannot be expressed purely in Allow statements
|
|
453
|
+
*
|
|
454
|
+
* @param other the Deny permission to subtract
|
|
455
|
+
*/
|
|
456
|
+
subtract(other) {
|
|
457
|
+
// Only subtract Deny from Allow for the same service/action
|
|
458
|
+
if (this.effect !== 'Allow' ||
|
|
459
|
+
other.effect !== 'Deny' ||
|
|
460
|
+
this.service !== other.service ||
|
|
461
|
+
this.action !== other.action) {
|
|
462
|
+
// No subtraction applies
|
|
463
|
+
return [this];
|
|
464
|
+
}
|
|
465
|
+
const allowCondsNorm = normalizeConditionKeys(this.conditions || {});
|
|
466
|
+
const denyCondsNorm = normalizeConditionKeys(other.conditions || {});
|
|
467
|
+
const conditionsMatch = JSON.stringify(allowCondsNorm) === JSON.stringify(denyCondsNorm);
|
|
468
|
+
const allowPrincipalConstraint = this.principalConstraint();
|
|
469
|
+
const denyPrincipalConstraint = other.principalConstraint();
|
|
470
|
+
if (!(0, principalConstraints_js_1.principalConstraintsOverlap)(allowPrincipalConstraint, denyPrincipalConstraint)) {
|
|
471
|
+
return [this];
|
|
472
|
+
}
|
|
473
|
+
const allowResource = this.resource;
|
|
474
|
+
const allowNotResource = this.notResource;
|
|
475
|
+
const denyResource = other.resource;
|
|
476
|
+
const denyNotResource = other.notResource;
|
|
477
|
+
const eff = this.effect;
|
|
478
|
+
const svc = this.service;
|
|
479
|
+
const act = this.action;
|
|
480
|
+
const principalResiduals = (0, principalConstraints_js_1.subtractPrincipalConstraint)(allowPrincipalConstraint, denyPrincipalConstraint);
|
|
481
|
+
const sameResourceShape = JSON.stringify([...(allowResource ?? [])].sort()) ===
|
|
482
|
+
JSON.stringify([...(denyResource ?? [])].sort()) &&
|
|
483
|
+
JSON.stringify([...(allowNotResource ?? [])].sort()) ===
|
|
484
|
+
JSON.stringify([...(denyNotResource ?? [])].sort());
|
|
485
|
+
const denyFullyCoversPrincipal = (0, principalConstraints_js_1.principalIncludes)(denyPrincipalConstraint, allowPrincipalConstraint);
|
|
486
|
+
const allowPrincipalArgs = (0, principalConstraints_js_1.principalArgsForConstraint)(allowPrincipalConstraint);
|
|
487
|
+
const denyPrincipalArgs = (0, principalConstraints_js_1.principalArgsForConstraint)(denyPrincipalConstraint);
|
|
488
|
+
if (!denyFullyCoversPrincipal && sameResourceShape && conditionsMatch) {
|
|
489
|
+
const principalResidualsEqualAllow = principalResiduals.length === 1 &&
|
|
490
|
+
JSON.stringify((0, principalConstraints_js_1.principalArgsForConstraint)(principalResiduals[0])) ===
|
|
491
|
+
JSON.stringify(allowPrincipalArgs);
|
|
492
|
+
if (principalResidualsEqualAllow) {
|
|
493
|
+
return [this, other];
|
|
494
|
+
}
|
|
495
|
+
return principalResiduals.map((constraint) => {
|
|
496
|
+
const args = (0, principalConstraints_js_1.principalArgsForConstraint)(constraint);
|
|
497
|
+
return new Permission(eff, svc, act, allowResource, allowNotResource, this.conditions, args.principal, args.notPrincipal);
|
|
498
|
+
});
|
|
499
|
+
}
|
|
500
|
+
if (!denyFullyCoversPrincipal) {
|
|
501
|
+
return [this, other];
|
|
502
|
+
}
|
|
503
|
+
// Case: Allow.resource & Deny.resource
|
|
504
|
+
if (allowResource !== undefined && denyResource !== undefined) {
|
|
505
|
+
const overlappingResources = allowResource.some((a) => {
|
|
506
|
+
return denyResource.some((d) => {
|
|
507
|
+
return wildcardToRegex(d).test(a) || wildcardToRegex(a).test(d);
|
|
508
|
+
});
|
|
509
|
+
});
|
|
510
|
+
// If the resources in the allow and deny do not overlap, return the allow as is
|
|
511
|
+
if (!overlappingResources) {
|
|
512
|
+
return [this];
|
|
513
|
+
}
|
|
514
|
+
// Categories for allows, a single allow could be more than one, because the deny could have multiple
|
|
515
|
+
// Without Conditions:
|
|
516
|
+
//1. Exactly the same as a deny - remove the allow
|
|
517
|
+
//2. A subset of a deny - remove the allow
|
|
518
|
+
//3. A superset of a deny - keep the allow and the deny
|
|
519
|
+
//4. No overlap with any deny - keep the allow as is
|
|
520
|
+
//
|
|
521
|
+
// With Conditions:
|
|
522
|
+
//1. Exactly the same as a deny - invert the conditions and keep the allow
|
|
523
|
+
//2. A subset of a deny - invert the conditions and keep the allow
|
|
524
|
+
//3. A superset of a deny - keep the allow and the deny
|
|
525
|
+
//4. No overlap with any deny - keep the allow as is
|
|
526
|
+
const allowMatches = [];
|
|
527
|
+
const allowSubsets = [];
|
|
528
|
+
const allowSupersets = [];
|
|
529
|
+
const allowNoOverlap = [];
|
|
530
|
+
const denySubsets = [];
|
|
531
|
+
for (const allowedResource of allowResource) {
|
|
532
|
+
let isMatch = false;
|
|
533
|
+
let isSubset = false;
|
|
534
|
+
let isSuperset = false;
|
|
535
|
+
for (const deniedResource of denyResource) {
|
|
536
|
+
if (deniedResource === allowedResource) {
|
|
537
|
+
isMatch = true;
|
|
538
|
+
break;
|
|
539
|
+
}
|
|
540
|
+
if (wildcardToRegex(deniedResource).test(allowedResource)) {
|
|
541
|
+
isSubset = true;
|
|
542
|
+
break;
|
|
543
|
+
}
|
|
544
|
+
if (wildcardToRegex(allowedResource).test(deniedResource)) {
|
|
545
|
+
isSuperset = true;
|
|
546
|
+
denySubsets.push(deniedResource);
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
if (isMatch) {
|
|
550
|
+
allowMatches.push(allowedResource);
|
|
551
|
+
}
|
|
552
|
+
else if (isSubset) {
|
|
553
|
+
allowSubsets.push(allowedResource);
|
|
554
|
+
}
|
|
555
|
+
else if (isSuperset) {
|
|
556
|
+
allowSupersets.push(allowedResource);
|
|
557
|
+
}
|
|
558
|
+
else {
|
|
559
|
+
allowNoOverlap.push(allowedResource);
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
const permissionsToReturn = [];
|
|
563
|
+
if (allowNoOverlap.length > 0) {
|
|
564
|
+
permissionsToReturn.push(new Permission(eff, svc, act, allowNoOverlap, undefined, this.conditions, allowPrincipalArgs.principal, allowPrincipalArgs.notPrincipal));
|
|
565
|
+
}
|
|
566
|
+
if (allowSupersets.length > 0) {
|
|
567
|
+
permissionsToReturn.push(new Permission(eff, svc, act, allowSupersets, undefined, this.conditions, allowPrincipalArgs.principal, allowPrincipalArgs.notPrincipal));
|
|
568
|
+
}
|
|
569
|
+
if (allowMatches.length > 0 || allowSubsets.length > 0) {
|
|
570
|
+
// If conditions are identical, these are fully dropped from the Allow. If not, they need to be kept with inverted conditions
|
|
571
|
+
if (!conditionsMatch) {
|
|
572
|
+
const newAllow = new Permission(eff, svc, act, [...allowMatches, ...allowSubsets], undefined, this.conditions, allowPrincipalArgs.principal, allowPrincipalArgs.notPrincipal);
|
|
573
|
+
permissionsToReturn.push(...applyDenyConditionsToAllow(newAllow, other));
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
if (denySubsets.length > 0) {
|
|
577
|
+
permissionsToReturn.push(new Permission('Deny', svc, act, denySubsets, undefined, other.conditions, denyPrincipalArgs.principal, denyPrincipalArgs.notPrincipal));
|
|
578
|
+
}
|
|
579
|
+
return permissionsToReturn;
|
|
580
|
+
}
|
|
581
|
+
// Case: Allow.resource & Deny.notResource
|
|
582
|
+
// =======================================================================
|
|
583
|
+
// SEMANTICS:
|
|
584
|
+
// Deny.notResource means: "deny everything EXCEPT these patterns"
|
|
585
|
+
// So the deny APPLIES to resources that do NOT match denyNotResource patterns
|
|
586
|
+
// And the deny does NOT apply to resources that DO match denyNotResource patterns
|
|
587
|
+
// =======================================================================
|
|
588
|
+
if (allowResource !== undefined && denyNotResource !== undefined) {
|
|
589
|
+
// STEP 1: Categorize each allow resource based on relationship to denyNotResource patterns
|
|
590
|
+
//
|
|
591
|
+
// Categories:
|
|
592
|
+
// ExcludedFromDeny - Matches a denyNotResource pattern (deny doesn't apply to these)
|
|
593
|
+
// AffectedByDeny - Does NOT match any denyNotResource (deny applies to these)
|
|
594
|
+
// Superset - Covers (is broader than) a denyNotResource pattern
|
|
595
|
+
//
|
|
596
|
+
// Also track which denyNotResource patterns are covered by superset allow resources
|
|
597
|
+
const excludedFromDeny = [];
|
|
598
|
+
const affectedByDeny = [];
|
|
599
|
+
const supersets = [];
|
|
600
|
+
const coveredDenyNotResourcePatterns = [];
|
|
601
|
+
for (const allowedResource of allowResource) {
|
|
602
|
+
let isExcluded = false;
|
|
603
|
+
let isSuperset = false;
|
|
604
|
+
for (const deniedNotResource of denyNotResource) {
|
|
605
|
+
// Check if allowResource exactly matches or is covered by denyNotResource pattern
|
|
606
|
+
if (allowedResource === deniedNotResource ||
|
|
607
|
+
wildcardToRegex(deniedNotResource).test(allowedResource)) {
|
|
608
|
+
isExcluded = true;
|
|
609
|
+
break;
|
|
610
|
+
}
|
|
611
|
+
// Check if allowResource covers (is broader than) denyNotResource pattern
|
|
612
|
+
if (wildcardToRegex(allowedResource).test(deniedNotResource)) {
|
|
613
|
+
isSuperset = true;
|
|
614
|
+
if (!coveredDenyNotResourcePatterns.includes(deniedNotResource)) {
|
|
615
|
+
coveredDenyNotResourcePatterns.push(deniedNotResource);
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
if (isExcluded) {
|
|
620
|
+
excludedFromDeny.push(allowedResource);
|
|
621
|
+
}
|
|
622
|
+
else if (isSuperset) {
|
|
623
|
+
supersets.push(allowedResource);
|
|
624
|
+
}
|
|
625
|
+
else {
|
|
626
|
+
affectedByDeny.push(allowedResource);
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
// STEP 2: Early exit - if all allow resources are excluded from deny, return unchanged
|
|
630
|
+
if (excludedFromDeny.length === allowResource.length) {
|
|
631
|
+
return [this];
|
|
632
|
+
}
|
|
633
|
+
const denyHasConditions = other.conditions && Object.keys(other.conditions).length > 0;
|
|
634
|
+
// STEP 3: Build output permissions by category
|
|
635
|
+
const permissionsToReturn = [];
|
|
636
|
+
// ExcludedFromDeny: Keep as-is with original conditions (deny doesn't touch these)
|
|
637
|
+
if (excludedFromDeny.length > 0) {
|
|
638
|
+
permissionsToReturn.push(new Permission(eff, svc, act, excludedFromDeny, undefined, this.conditions, allowPrincipalArgs.principal, allowPrincipalArgs.notPrincipal));
|
|
639
|
+
}
|
|
640
|
+
// Superset: Allow resource is broader than denyNotResource patterns
|
|
641
|
+
// - The covered denyNotResource patterns are excluded from deny (allow unconditionally)
|
|
642
|
+
// - The superset allow resources are affected by deny (apply inverted conditions)
|
|
643
|
+
if (supersets.length > 0) {
|
|
644
|
+
// First: Allow the covered patterns unconditionally (they're excluded from deny)
|
|
645
|
+
if (coveredDenyNotResourcePatterns.length > 0) {
|
|
646
|
+
permissionsToReturn.push(new Permission(eff, svc, act, coveredDenyNotResourcePatterns, undefined, this.conditions, allowPrincipalArgs.principal, allowPrincipalArgs.notPrincipal));
|
|
647
|
+
}
|
|
648
|
+
// Second: Apply inverted deny conditions to the superset resources
|
|
649
|
+
if (denyHasConditions && !conditionsMatch) {
|
|
650
|
+
const supersetAllow = new Permission(eff, svc, act, supersets, undefined, this.conditions, allowPrincipalArgs.principal, allowPrincipalArgs.notPrincipal);
|
|
651
|
+
permissionsToReturn.push(...applyDenyConditionsToAllow(supersetAllow, other));
|
|
652
|
+
}
|
|
653
|
+
// If no conditions or conditions match, the superset is fully denied (nothing to add)
|
|
654
|
+
}
|
|
655
|
+
// AffectedByDeny: These resources are hit by the deny
|
|
656
|
+
if (affectedByDeny.length > 0) {
|
|
657
|
+
// If there are no conditions on deny - these are fully denied (drop them)
|
|
658
|
+
// If the conditions match - these are fully denied (drop them)
|
|
659
|
+
if (denyHasConditions && !conditionsMatch) {
|
|
660
|
+
// Different conditions - keep with inverted deny conditions
|
|
661
|
+
const newAllow = new Permission(eff, svc, act, affectedByDeny, undefined, this.conditions, allowPrincipalArgs.principal, allowPrincipalArgs.notPrincipal);
|
|
662
|
+
permissionsToReturn.push(...applyDenyConditionsToAllow(newAllow, other));
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
return permissionsToReturn;
|
|
666
|
+
}
|
|
667
|
+
// Scenario 3: Allow.notResource & Deny.resource
|
|
668
|
+
if (allowNotResource !== undefined && denyResource !== undefined) {
|
|
669
|
+
// STEP 1: Categorize relationships and track which denyResources are already covered
|
|
670
|
+
const coveredDenyResources = new Set(); // denyResources already excluded by allowNotResource
|
|
671
|
+
const uncoveredDenyResources = []; // denyResources that affect allowed resources
|
|
672
|
+
const subsetReplacements = [];
|
|
673
|
+
// For each denyResource, check if it's covered by any allowNotResource
|
|
674
|
+
for (const denyPattern of denyResource) {
|
|
675
|
+
let isCovered = false;
|
|
676
|
+
for (const allowPattern of allowNotResource) {
|
|
677
|
+
// ExactMatch or Superset - denyResource is already excluded
|
|
678
|
+
if (allowPattern === denyPattern || wildcardToRegex(allowPattern).test(denyPattern)) {
|
|
679
|
+
isCovered = true;
|
|
680
|
+
coveredDenyResources.add(denyPattern);
|
|
681
|
+
break;
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
if (!isCovered) {
|
|
685
|
+
uncoveredDenyResources.push(denyPattern);
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
// Check for Subset patterns (denyResource covers allowNotResource)
|
|
689
|
+
for (const allowPattern of allowNotResource) {
|
|
690
|
+
for (const denyPattern of denyResource) {
|
|
691
|
+
if (wildcardToRegex(denyPattern).test(allowPattern) && allowPattern !== denyPattern) {
|
|
692
|
+
subsetReplacements.push({ allowPattern, denyPattern });
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
// Filter out subset deny patterns from uncoveredDenyResources to get true NoOverlap patterns
|
|
697
|
+
const subsetDenyPatternsSet = new Set(subsetReplacements.map((s) => s.denyPattern));
|
|
698
|
+
const noOverlapDenyResources = uncoveredDenyResources.filter((dr) => !subsetDenyPatternsSet.has(dr));
|
|
699
|
+
// STEP 2: If all denyResources are covered, deny has no effect
|
|
700
|
+
if (noOverlapDenyResources.length === 0 && subsetReplacements.length === 0) {
|
|
701
|
+
return [this];
|
|
702
|
+
}
|
|
703
|
+
// STEP 3: Build output permissions
|
|
704
|
+
const denyHasConditions = other.conditions && Object.keys(other.conditions).length > 0;
|
|
705
|
+
// Build the expanded notResource (original + noOverlap deny resources + subset replacements)
|
|
706
|
+
const subsetAllowPatterns = new Set(subsetReplacements.map((s) => s.allowPattern));
|
|
707
|
+
const keptPatterns = allowNotResource.filter((p) => !subsetAllowPatterns.has(p));
|
|
708
|
+
const subsetDenyPatterns = Array.from(new Set(subsetReplacements.map((s) => s.denyPattern)));
|
|
709
|
+
const expandedNotResource = Array.from(new Set([...keptPatterns, ...subsetDenyPatterns, ...noOverlapDenyResources]));
|
|
710
|
+
// Same conditions or no deny conditions: simply expand notResource
|
|
711
|
+
if (conditionsMatch || !denyHasConditions) {
|
|
712
|
+
return [
|
|
713
|
+
new Permission(eff, svc, act, undefined, expandedNotResource, this.conditions, allowPrincipalArgs.principal, allowPrincipalArgs.notPrincipal)
|
|
714
|
+
];
|
|
715
|
+
}
|
|
716
|
+
// Different conditions: handle Subset and NoOverlap cases separately
|
|
717
|
+
const permissionsToReturn = [];
|
|
718
|
+
const hasSubsetReplacements = subsetReplacements.length > 0;
|
|
719
|
+
const hasNoOverlapAdditions = noOverlapDenyResources.length > 0;
|
|
720
|
+
// Part 1: Original allowNotResource with inverted deny conditions
|
|
721
|
+
// (when deny condition is NOT met, original allow applies)
|
|
722
|
+
const originalAllow = new Permission(eff, svc, act, undefined, allowNotResource, this.conditions, allowPrincipalArgs.principal, allowPrincipalArgs.notPrincipal);
|
|
723
|
+
permissionsToReturn.push(...applyDenyConditionsToAllow(originalAllow, other));
|
|
724
|
+
// Part 2a: For SUBSET replacements - expanded notResource WITH deny conditions
|
|
725
|
+
// (replacing smaller exclusion with larger one, only valid when condition is met)
|
|
726
|
+
if (hasSubsetReplacements) {
|
|
727
|
+
// Build notResource with just the subset replacements (not the noOverlap additions)
|
|
728
|
+
const subsetExpandedNotResource = Array.from(new Set([...keptPatterns, ...subsetDenyPatterns]));
|
|
729
|
+
const subsetConditions = intersectConditions(this.conditions || {}, other.conditions || {});
|
|
730
|
+
permissionsToReturn.push(new Permission(eff, svc, act, undefined, subsetExpandedNotResource, subsetConditions || other.conditions, allowPrincipalArgs.principal, allowPrincipalArgs.notPrincipal));
|
|
731
|
+
}
|
|
732
|
+
// Part 2b: For NO OVERLAP additions - expanded notResource WITHOUT conditions
|
|
733
|
+
// (adding new exclusion is always safe, no condition needed)
|
|
734
|
+
if (hasNoOverlapAdditions) {
|
|
735
|
+
permissionsToReturn.push(new Permission(eff, svc, act, undefined, expandedNotResource, this.conditions, allowPrincipalArgs.principal, allowPrincipalArgs.notPrincipal));
|
|
736
|
+
}
|
|
737
|
+
return permissionsToReturn;
|
|
738
|
+
}
|
|
739
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
740
|
+
// SCENARIO 4: Allow.NotResource & Deny.NotResource
|
|
741
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
742
|
+
//
|
|
743
|
+
// Semantics:
|
|
744
|
+
// Allow.notResource = [A]: allow ALL resources EXCEPT those matching A
|
|
745
|
+
// Deny.notResource = [D]: deny ALL resources EXCEPT those matching D
|
|
746
|
+
//
|
|
747
|
+
// The deny blocks everything except D (the "safe zone").
|
|
748
|
+
// The allow permits everything except A.
|
|
749
|
+
//
|
|
750
|
+
// Surviving resources = (allowed) ∩ (not denied)
|
|
751
|
+
// = (NOT in A) ∩ (in D)
|
|
752
|
+
// = resources in D that are not covered by A
|
|
753
|
+
//
|
|
754
|
+
// Result: resource: [D patterns not covered by A]
|
|
755
|
+
//
|
|
756
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
757
|
+
if (allowNotResource !== undefined && denyNotResource !== undefined) {
|
|
758
|
+
const denyHasConditions = other.conditions && Object.keys(other.conditions).length > 0;
|
|
759
|
+
// Helper: Check if pattern A covers pattern B (A is superset of or equal to B)
|
|
760
|
+
const patternCovers = (a, b) => {
|
|
761
|
+
return a === b || wildcardToRegex(a).test(b);
|
|
762
|
+
};
|
|
763
|
+
// Find D patterns that survive (not covered by any A pattern)
|
|
764
|
+
// These are the resources that are both allowed AND protected from deny
|
|
765
|
+
const survivingResources = denyNotResource.filter((dPattern) => !allowNotResource.some((aPattern) => patternCovers(aPattern, dPattern)));
|
|
766
|
+
// If nothing survives, return empty
|
|
767
|
+
if (survivingResources.length === 0) {
|
|
768
|
+
return [];
|
|
769
|
+
}
|
|
770
|
+
// Handle conditions
|
|
771
|
+
if (!denyHasConditions || conditionsMatch) {
|
|
772
|
+
// No deny conditions or same conditions: apply directly
|
|
773
|
+
return [
|
|
774
|
+
new Permission(eff, svc, act, survivingResources, undefined, this.conditions, allowPrincipalArgs.principal, allowPrincipalArgs.notPrincipal)
|
|
775
|
+
];
|
|
776
|
+
}
|
|
777
|
+
// Different conditions: split into two parts
|
|
778
|
+
const permissionsToReturn = [];
|
|
779
|
+
// Part 1: When deny condition is NOT met → original allow applies
|
|
780
|
+
const originalAllow = new Permission(eff, svc, act, undefined, allowNotResource, this.conditions, allowPrincipalArgs.principal, allowPrincipalArgs.notPrincipal);
|
|
781
|
+
permissionsToReturn.push(...applyDenyConditionsToAllow(originalAllow, other));
|
|
782
|
+
// Part 2: When deny condition IS met → surviving resources with deny's condition
|
|
783
|
+
const denyConditionCount = Object.values(other.conditions || {}).reduce((sum, keyMap) => sum + Object.keys(keyMap).length, 0);
|
|
784
|
+
const part2Conditions = denyConditionCount === 1 ? other.conditions : undefined;
|
|
785
|
+
permissionsToReturn.push(new Permission(eff, svc, act, survivingResources, undefined, part2Conditions, allowPrincipalArgs.principal, allowPrincipalArgs.notPrincipal));
|
|
786
|
+
return permissionsToReturn;
|
|
787
|
+
}
|
|
788
|
+
// This should never happen
|
|
789
|
+
throw new Error('Permission.subtract: This should never happen—invalid state.');
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
exports.Permission = Permission;
|
|
793
|
+
/**
|
|
794
|
+
* Attempt to union two sets of permission conditions.
|
|
795
|
+
*
|
|
796
|
+
* If the conditions can be merged into a single block that allows all cases allowed by either,
|
|
797
|
+
* returns the merged conditions. If they cannot be merged cleanly (e.g., differing operators
|
|
798
|
+
* or incompatible numeric boundaries), returns null.
|
|
799
|
+
*
|
|
800
|
+
* @param a First set of conditions
|
|
801
|
+
* @param b Second set of conditions
|
|
802
|
+
* @returns Merged conditions or null if they cannot be merged
|
|
803
|
+
*/
|
|
804
|
+
function unionConditions(a, b) {
|
|
805
|
+
// 1. If the set of operators in 'a' differs from the set in 'b', return null.
|
|
806
|
+
a = normalizeConditionKeys(a);
|
|
807
|
+
b = normalizeConditionKeys(b);
|
|
808
|
+
const opsA = Object.keys(a).sort();
|
|
809
|
+
const opsB = Object.keys(b).sort();
|
|
810
|
+
if (JSON.stringify(opsA) !== JSON.stringify(opsB)) {
|
|
811
|
+
return null;
|
|
812
|
+
}
|
|
813
|
+
const merged = {};
|
|
814
|
+
// 2. For each operator op that appears in both:
|
|
815
|
+
for (const op of opsA) {
|
|
816
|
+
const keysA = Object.keys(a[op]).sort();
|
|
817
|
+
const keysB = Object.keys(b[op]).sort();
|
|
818
|
+
// If the set of context‐keys under this operator differs, we can't merge as one block
|
|
819
|
+
if (JSON.stringify(keysA) !== JSON.stringify(keysB)) {
|
|
820
|
+
return null;
|
|
821
|
+
}
|
|
822
|
+
// Now we know op and its context keys align. Build the merged set for this operator:
|
|
823
|
+
merged[op] = {};
|
|
824
|
+
for (const key of keysA) {
|
|
825
|
+
const valsA = a[op][key];
|
|
826
|
+
const valsB = b[op][key];
|
|
827
|
+
// How we combine depends on operator semantics:
|
|
828
|
+
switch (conditionBaseOperator(op)) {
|
|
829
|
+
case 'stringequals':
|
|
830
|
+
case 'stringlike':
|
|
831
|
+
case 'stringnotequals':
|
|
832
|
+
case 'stringnotlike':
|
|
833
|
+
case 'arnequals':
|
|
834
|
+
case 'arnlike':
|
|
835
|
+
case 'arnnotequals':
|
|
836
|
+
case 'arnnotlike':
|
|
837
|
+
// String‐based operators: just union the value arrays
|
|
838
|
+
merged[op][key] = Array.from(new Set([...valsA, ...valsB]));
|
|
839
|
+
break;
|
|
840
|
+
case 'numericlessthan':
|
|
841
|
+
case 'numericlessthanequals':
|
|
842
|
+
case 'numericgreaterthan':
|
|
843
|
+
case 'numericgreaterthanequals':
|
|
844
|
+
case 'numericequals':
|
|
845
|
+
case 'numericnotequals':
|
|
846
|
+
// Numeric operators: pick the “widest” comparison that still covers both sets
|
|
847
|
+
// For simplicity, convert all valsA/valsB to numbers; find the min or max
|
|
848
|
+
const numsA = valsA.map((v) => Number(v));
|
|
849
|
+
const numsB = valsB.map((v) => Number(v));
|
|
850
|
+
if (numsA.some(isNaN) || numsB.some(isNaN)) {
|
|
851
|
+
// Malformed number—cannot merge
|
|
852
|
+
return null;
|
|
853
|
+
}
|
|
854
|
+
if (op === 'numericlessthan' || op === 'numericlessthanequals') {
|
|
855
|
+
// We want the largest boundary
|
|
856
|
+
const candidate = Math.max(...numsA, ...numsB);
|
|
857
|
+
merged[op][key] = [String(candidate)];
|
|
858
|
+
}
|
|
859
|
+
else if (op === 'numericgreaterthan' || op === 'numericgreaterthanequals') {
|
|
860
|
+
// We want the smallest boundary
|
|
861
|
+
const candidate = Math.min(...numsA, ...numsB);
|
|
862
|
+
merged[op][key] = [String(candidate)];
|
|
863
|
+
}
|
|
864
|
+
else if (op === 'numericequals' || op === 'numericnotequals') {
|
|
865
|
+
// Union the sets of allowed/not‐allowed numbers
|
|
866
|
+
merged[op][key] = Array.from(new Set([...valsA.map(String), ...valsB.map(String)]));
|
|
867
|
+
}
|
|
868
|
+
break;
|
|
869
|
+
case 'datelessthan':
|
|
870
|
+
case 'datelessthanequals':
|
|
871
|
+
case 'dategreaterthan':
|
|
872
|
+
case 'dategreaterthanequals':
|
|
873
|
+
// Similar idea: choose the “widest” date limit
|
|
874
|
+
// Assume ISO‐8601 strings so lex‐compare works
|
|
875
|
+
if (op === 'datelessthan' || op === 'datelessthanequals') {
|
|
876
|
+
// pick the LARGEST date (latest) because “< latest” covers “< earlier”
|
|
877
|
+
const candidate = [...valsA, ...valsB].sort().reverse()[0];
|
|
878
|
+
merged[op][key] = [candidate];
|
|
879
|
+
}
|
|
880
|
+
else {
|
|
881
|
+
// "DateGreaterThan"/"DateGreaterThanEquals": pick the EARLIEST date
|
|
882
|
+
const candidate = [...valsA, ...valsB].sort()[0];
|
|
883
|
+
merged[op][key] = [candidate];
|
|
884
|
+
}
|
|
885
|
+
break;
|
|
886
|
+
case 'bool':
|
|
887
|
+
// Typically valsA and valsB are ["true"] or ["false"].
|
|
888
|
+
// If either contains "true", then the union is ["true","false"]? No—
|
|
889
|
+
// Bool doesn't make sense with an array. In IAM, Bool only works with a single value.
|
|
890
|
+
// If values differ (one says ["true"], the other says ["false"]), you cannot
|
|
891
|
+
// express (Bool==true OR Bool==false) as a single Bool. You’d need two separate
|
|
892
|
+
// statements. So bail out.
|
|
893
|
+
if (valsA[0] === valsB[0]) {
|
|
894
|
+
merged[op][key] = [valsA[0]];
|
|
895
|
+
}
|
|
896
|
+
else {
|
|
897
|
+
return null;
|
|
898
|
+
}
|
|
899
|
+
break;
|
|
900
|
+
case 'ipaddress':
|
|
901
|
+
case 'notipaddress':
|
|
902
|
+
// You can pass multiple CIDR blocks under a single IpAddress. So union them.
|
|
903
|
+
merged[op][key] = Array.from(new Set([...valsA, ...valsB]));
|
|
904
|
+
break;
|
|
905
|
+
// Any other operators (e.g., “ArnNotLike” etc.) behave similarly to their base type
|
|
906
|
+
default:
|
|
907
|
+
// If we don’t explicitly handle the operator, reject merging
|
|
908
|
+
return null;
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
return merged;
|
|
913
|
+
}
|
|
914
|
+
/**
|
|
915
|
+
* Intersect two sets of permission conditions.
|
|
916
|
+
*
|
|
917
|
+
* Attempt to find the intersection of two sets of IAM condition clauses. This will
|
|
918
|
+
* combine condition operators and context keys, retaining only values that satisfy
|
|
919
|
+
* both sets of conditions. If the intersection is empty or cannot be expressed
|
|
920
|
+
* cleanly, returns null.
|
|
921
|
+
*
|
|
922
|
+
* @param conditionsA First set of conditions
|
|
923
|
+
* @param conditionsB Second set of conditions
|
|
924
|
+
* @returns Intersected conditions or null if intersection is empty or cannot be expressed
|
|
925
|
+
*/
|
|
926
|
+
function intersectConditions(a, b) {
|
|
927
|
+
// Normalize both condition sets to lowercase operators and keys
|
|
928
|
+
const normalizedA = normalizeConditionKeys(a);
|
|
929
|
+
const normalizedB = normalizeConditionKeys(b);
|
|
930
|
+
// Collect all unique operators from both sides
|
|
931
|
+
const allOperators = Array.from(new Set([...Object.keys(normalizedA), ...Object.keys(normalizedB)]));
|
|
932
|
+
const result = {};
|
|
933
|
+
for (const operator of allOperators) {
|
|
934
|
+
const keysA = normalizedA[operator] || {};
|
|
935
|
+
const keysB = normalizedB[operator] || {};
|
|
936
|
+
// Collect all unique context keys for this operator
|
|
937
|
+
const allContextKeys = Array.from(new Set([...Object.keys(keysA), ...Object.keys(keysB)]));
|
|
938
|
+
result[operator] = {};
|
|
939
|
+
for (const contextKey of allContextKeys) {
|
|
940
|
+
const valsA = keysA[contextKey];
|
|
941
|
+
const valsB = keysB[contextKey];
|
|
942
|
+
// If key exists in both sides, apply intersection logic based on operator type
|
|
943
|
+
if (valsA !== undefined && valsB !== undefined) {
|
|
944
|
+
const intersectedValues = intersectValuesForOperator(operator, valsA, valsB);
|
|
945
|
+
if (intersectedValues === null) {
|
|
946
|
+
// Empty intersection means no overlap - return null
|
|
947
|
+
return null;
|
|
948
|
+
}
|
|
949
|
+
result[operator][contextKey] = intersectedValues;
|
|
950
|
+
}
|
|
951
|
+
else {
|
|
952
|
+
// Key only exists in one side - carry it through (both conditions must be satisfied)
|
|
953
|
+
result[operator][contextKey] = valsA !== undefined ? Array.from(valsA) : Array.from(valsB);
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
// Remove empty operator objects
|
|
957
|
+
if (Object.keys(result[operator]).length === 0) {
|
|
958
|
+
delete result[operator];
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
const merged = mergeComplementaryConditions(result);
|
|
962
|
+
// Check if any values array became empty after merging complementary conditions
|
|
963
|
+
// (e.g., StringEquals: ['a'] merged with StringNotEquals: ['a'] results in StringEquals: [])
|
|
964
|
+
for (const [, keyMap] of Object.entries(merged)) {
|
|
965
|
+
for (const [, values] of Object.entries(keyMap)) {
|
|
966
|
+
if (values.length === 0) {
|
|
967
|
+
return null;
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
return merged;
|
|
972
|
+
}
|
|
973
|
+
/**
|
|
974
|
+
* Intersect values for a specific operator type.
|
|
975
|
+
*
|
|
976
|
+
* Returns the intersected values, or null if the intersection is empty
|
|
977
|
+
* (meaning the conditions are mutually exclusive).
|
|
978
|
+
*/
|
|
979
|
+
function intersectValuesForOperator(operator, valsA, valsB) {
|
|
980
|
+
const baseOp = conditionBaseOperator(operator);
|
|
981
|
+
switch (baseOp) {
|
|
982
|
+
// String/ARN equality operators: intersection of allowed values
|
|
983
|
+
case 'stringequals':
|
|
984
|
+
case 'stringlike':
|
|
985
|
+
case 'arnequals':
|
|
986
|
+
case 'arnlike': {
|
|
987
|
+
const common = valsA.filter((v) => valsB.includes(v));
|
|
988
|
+
return common.length > 0 ? common : null;
|
|
989
|
+
}
|
|
990
|
+
// String/ARN negation operators: union of exclusions (more restrictive)
|
|
991
|
+
case 'stringnotequals':
|
|
992
|
+
case 'stringnotlike':
|
|
993
|
+
case 'arnnotequals':
|
|
994
|
+
case 'arnnotlike': {
|
|
995
|
+
return Array.from(new Set([...valsA, ...valsB]));
|
|
996
|
+
}
|
|
997
|
+
// Numeric less-than operators: take the minimum (more restrictive)
|
|
998
|
+
case 'numericlessthan':
|
|
999
|
+
case 'numericlessthanequals': {
|
|
1000
|
+
const numA = Number(valsA[0]);
|
|
1001
|
+
const numB = Number(valsB[0]);
|
|
1002
|
+
if (isNaN(numA) || isNaN(numB)) {
|
|
1003
|
+
return null;
|
|
1004
|
+
}
|
|
1005
|
+
return [String(Math.min(numA, numB))];
|
|
1006
|
+
}
|
|
1007
|
+
// Numeric greater-than operators: take the maximum (more restrictive)
|
|
1008
|
+
case 'numericgreaterthan':
|
|
1009
|
+
case 'numericgreaterthanequals': {
|
|
1010
|
+
const numA = Number(valsA[0]);
|
|
1011
|
+
const numB = Number(valsB[0]);
|
|
1012
|
+
if (isNaN(numA) || isNaN(numB)) {
|
|
1013
|
+
return null;
|
|
1014
|
+
}
|
|
1015
|
+
return [String(Math.max(numA, numB))];
|
|
1016
|
+
}
|
|
1017
|
+
// Boolean operators: values must match exactly
|
|
1018
|
+
case 'bool':
|
|
1019
|
+
case 'null': {
|
|
1020
|
+
if (valsA[0]?.toLowerCase() !== valsB[0]?.toLowerCase()) {
|
|
1021
|
+
return null;
|
|
1022
|
+
}
|
|
1023
|
+
return [valsA[0]];
|
|
1024
|
+
}
|
|
1025
|
+
// IP address operators: intersection of CIDR blocks
|
|
1026
|
+
case 'ipaddress':
|
|
1027
|
+
case 'notipaddress': {
|
|
1028
|
+
const common = valsA.filter((cidr) => valsB.includes(cidr));
|
|
1029
|
+
return common.length > 0 ? common : null;
|
|
1030
|
+
}
|
|
1031
|
+
// Date less-than operators: take the earlier date (more restrictive)
|
|
1032
|
+
case 'datelessthan':
|
|
1033
|
+
case 'datelessthanequals': {
|
|
1034
|
+
const dateA = valsA[0];
|
|
1035
|
+
const dateB = valsB[0];
|
|
1036
|
+
return [dateA < dateB ? dateA : dateB];
|
|
1037
|
+
}
|
|
1038
|
+
// Date greater-than operators: take the later date (more restrictive)
|
|
1039
|
+
case 'dategreaterthan':
|
|
1040
|
+
case 'dategreaterthanequals': {
|
|
1041
|
+
const dateA = valsA[0];
|
|
1042
|
+
const dateB = valsB[0];
|
|
1043
|
+
return [dateA > dateB ? dateA : dateB];
|
|
1044
|
+
}
|
|
1045
|
+
// Unknown operator - cannot handle
|
|
1046
|
+
default:
|
|
1047
|
+
return null;
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
/**
|
|
1051
|
+
* Checks if an IAM condition operator ends with "IfExists".
|
|
1052
|
+
*
|
|
1053
|
+
* @param op the IAM condition operator, e.g., "StringEqualsIfExists"
|
|
1054
|
+
* @returns true if the operator ends with "IfExists", false otherwise.
|
|
1055
|
+
*/
|
|
1056
|
+
function isIfExists(op) {
|
|
1057
|
+
// Check if the operator ends with "IfExists"
|
|
1058
|
+
return op.toLowerCase().endsWith('ifexists');
|
|
1059
|
+
}
|
|
1060
|
+
/**
|
|
1061
|
+
* Get the set operator from an IAM condition operator such as "ForAllValues" or "ForAnyValue".
|
|
1062
|
+
*
|
|
1063
|
+
* @param op the IAM condition operator, e.g., "ForAllValues:StringEquals"
|
|
1064
|
+
* @returns the set operator, e.g., "forallvalues" or "foranyvalue", or undefined if no set operator is present.
|
|
1065
|
+
*/
|
|
1066
|
+
function conditionSetOperator(op) {
|
|
1067
|
+
return op.includes(':') ? op.split(':')[0].toLowerCase() : undefined;
|
|
1068
|
+
}
|
|
1069
|
+
/**
|
|
1070
|
+
* Gets the base operator name from an IAM condition operator. Removes any set operator prefix or
|
|
1071
|
+
* "IfExists" suffix.
|
|
1072
|
+
*
|
|
1073
|
+
* @param op the IAM condition operator, e.g., "ForAllValues:StringEqualsIfExists"
|
|
1074
|
+
* @returns the base operator name, e.g., "stringequals" or "arnequals".
|
|
1075
|
+
*/
|
|
1076
|
+
function conditionBaseOperator(op) {
|
|
1077
|
+
// Return the base operator name for IAM condition operators
|
|
1078
|
+
return op
|
|
1079
|
+
.split(':')
|
|
1080
|
+
.at(-1)
|
|
1081
|
+
.toLowerCase()
|
|
1082
|
+
.replace(/ifexists$/, '');
|
|
1083
|
+
}
|
|
1084
|
+
/**
|
|
1085
|
+
* Returns a new PermissionConditions object with all operator and context keys lowercased.
|
|
1086
|
+
*/
|
|
1087
|
+
function normalizeConditionKeys(conds) {
|
|
1088
|
+
const result = {};
|
|
1089
|
+
for (const [op, keyMap] of Object.entries(conds)) {
|
|
1090
|
+
const lowerOp = op.toLowerCase();
|
|
1091
|
+
result[lowerOp] = {};
|
|
1092
|
+
for (const [contextKey, values] of Object.entries(keyMap)) {
|
|
1093
|
+
const lowerContextKey = contextKey.toLowerCase();
|
|
1094
|
+
result[lowerOp][lowerContextKey] = Array.from(values);
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
return result;
|
|
1098
|
+
}
|
|
1099
|
+
const invertOperatorMap = {
|
|
1100
|
+
stringequals: 'StringNotEquals',
|
|
1101
|
+
stringlike: 'StringNotLike',
|
|
1102
|
+
arnequals: 'ArnNotEquals',
|
|
1103
|
+
arnlike: 'ArnNotLike',
|
|
1104
|
+
stringnotequals: 'StringEquals',
|
|
1105
|
+
stringnotlike: 'StringLike',
|
|
1106
|
+
arnnotequals: 'ArnEquals',
|
|
1107
|
+
arnnotlike: 'ArnLike',
|
|
1108
|
+
numericlessthan: 'NumericGreaterThanEquals',
|
|
1109
|
+
numericlessthanequals: 'NumericGreaterThan',
|
|
1110
|
+
numericgreaterthan: 'NumericLessThanEquals',
|
|
1111
|
+
numericgreaterthanequals: 'NumericLessThan',
|
|
1112
|
+
numericequals: 'NumericNotEquals',
|
|
1113
|
+
numericnotequals: 'NumericEquals',
|
|
1114
|
+
datelessthan: 'DateGreaterThanEquals',
|
|
1115
|
+
datelessthanequals: 'DateGreaterThan',
|
|
1116
|
+
dategreaterthan: 'DateLessThanEquals',
|
|
1117
|
+
dategreaterthanequals: 'DateLessThan',
|
|
1118
|
+
bool: 'Bool',
|
|
1119
|
+
ipaddress: 'NotIpAddress',
|
|
1120
|
+
notipaddress: 'IpAddress'
|
|
1121
|
+
};
|
|
1122
|
+
const invertedSetOperatorMap = {
|
|
1123
|
+
forallvalues: 'ForAnyValue',
|
|
1124
|
+
foranyvalue: 'ForAllValues'
|
|
1125
|
+
};
|
|
1126
|
+
/**
|
|
1127
|
+
* Invert a set of IAM condition clauses for Deny → allow inversion.
|
|
1128
|
+
* Preserves ForAllValues:/ForAnyValue: prefixes and IfExists suffixes.
|
|
1129
|
+
*
|
|
1130
|
+
* @param conds the condition clauses to invert
|
|
1131
|
+
* @return a new set of inverted conditions
|
|
1132
|
+
*/
|
|
1133
|
+
function invertConditions(conds) {
|
|
1134
|
+
const normalized = normalizeConditionKeys(conds);
|
|
1135
|
+
const inverted = {};
|
|
1136
|
+
for (const [op, keyMap] of Object.entries(normalized)) {
|
|
1137
|
+
const setOperator = conditionSetOperator(op) || undefined;
|
|
1138
|
+
const setOperatorPrefix = setOperator ? invertedSetOperatorMap[setOperator] + ':' : '';
|
|
1139
|
+
const hasIfExists = isIfExists(op);
|
|
1140
|
+
const coreOp = conditionBaseOperator(op);
|
|
1141
|
+
const invertedCore = invertOperatorMap[coreOp] || coreOp;
|
|
1142
|
+
const invertedOp = `${setOperatorPrefix}${invertedCore}${hasIfExists ? 'IfExists' : ''}`.toLowerCase();
|
|
1143
|
+
inverted[invertedOp] = {};
|
|
1144
|
+
for (const [key, vals] of Object.entries(keyMap)) {
|
|
1145
|
+
if (coreOp === 'bool' || coreOp === 'null') {
|
|
1146
|
+
inverted[invertedOp][key] = vals.map((v) => (v.toLowerCase() === 'true' ? 'false' : 'true'));
|
|
1147
|
+
}
|
|
1148
|
+
else {
|
|
1149
|
+
inverted[invertedOp][key] = Array.from(vals);
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1152
|
+
}
|
|
1153
|
+
return inverted;
|
|
1154
|
+
}
|
|
1155
|
+
function mergeComplementaryConditions(c) {
|
|
1156
|
+
const complement = {
|
|
1157
|
+
stringequals: 'stringnotequals',
|
|
1158
|
+
stringlike: 'stringnotlike',
|
|
1159
|
+
arnequals: 'arnnotequals',
|
|
1160
|
+
arnlike: 'arnnotlike',
|
|
1161
|
+
numericequals: 'numericnotequals',
|
|
1162
|
+
numericnotequals: 'numericequals',
|
|
1163
|
+
numericlessthan: 'numericgreaterthanequals',
|
|
1164
|
+
numericgreaterthanequals: 'numericlessthan',
|
|
1165
|
+
numericlessthanequals: 'numericgreaterthan',
|
|
1166
|
+
numericgreaterthan: 'numericlessthanequals',
|
|
1167
|
+
datelessthan: 'dategreaterthanequals',
|
|
1168
|
+
dategreaterthanequals: 'datelessthan',
|
|
1169
|
+
datelessthanequals: 'dategreaterthan',
|
|
1170
|
+
dategreaterthan: 'datelessthanequals',
|
|
1171
|
+
ipaddress: 'notipaddress',
|
|
1172
|
+
notipaddress: 'ipaddress'
|
|
1173
|
+
// bool: 'bool'
|
|
1174
|
+
};
|
|
1175
|
+
const out = structuredClone(c);
|
|
1176
|
+
for (const [base, comp] of Object.entries(complement)) {
|
|
1177
|
+
if (out[base] && out[comp]) {
|
|
1178
|
+
for (const key of Object.keys(out[base])) {
|
|
1179
|
+
if (key in out[comp]) {
|
|
1180
|
+
out[base][key] = out[base][key].filter((v) => !out[comp][key].includes(v));
|
|
1181
|
+
delete out[comp][key];
|
|
1182
|
+
}
|
|
1183
|
+
}
|
|
1184
|
+
if (out[comp] && Object.keys(out[comp]).length === 0)
|
|
1185
|
+
delete out[comp];
|
|
1186
|
+
}
|
|
1187
|
+
}
|
|
1188
|
+
return out;
|
|
1189
|
+
}
|
|
1190
|
+
/**
|
|
1191
|
+
* Apply Deny conditions to an Allow permission.
|
|
1192
|
+
*
|
|
1193
|
+
* A Deny permission with conditions (whether multiple operators or multiple keys under one
|
|
1194
|
+
* operator) acts as an AND, meaning the Allow needs to escape ANY one of them (OR when inverted).
|
|
1195
|
+
* Each condition key-value pair is inverted and creates a separate Allow permission.
|
|
1196
|
+
*
|
|
1197
|
+
* It is possible for any given condition to fully deny the Allow, in which case
|
|
1198
|
+
* that condition will produce no resulting Allow permission. The result is an array
|
|
1199
|
+
* of Allow permissions that apply after each Deny condition is applied.
|
|
1200
|
+
*
|
|
1201
|
+
* This may result in multiple Allow permission or an empty array if all are denied.
|
|
1202
|
+
*
|
|
1203
|
+
* @param allow the Allow permission
|
|
1204
|
+
* @param deny the Deny permission
|
|
1205
|
+
* @returns an array of resulting Allow permissions after applying Deny conditions
|
|
1206
|
+
*/
|
|
1207
|
+
function applyDenyConditionsToAllow(allow, deny) {
|
|
1208
|
+
// If Deny has no conditions, it fully denies the Allow
|
|
1209
|
+
if (!deny.conditions || Object.keys(deny.conditions).length === 0) {
|
|
1210
|
+
return [allow];
|
|
1211
|
+
}
|
|
1212
|
+
const results = [];
|
|
1213
|
+
// Each Deny condition key-value pair creates a separate inverted condition for Allow
|
|
1214
|
+
// (multiple keys under the same operator are each inverted separately)
|
|
1215
|
+
for (const [operator, keyMap] of Object.entries(deny.conditions || {})) {
|
|
1216
|
+
for (const [contextKey, values] of Object.entries(keyMap)) {
|
|
1217
|
+
// Invert this specific condition
|
|
1218
|
+
const singleCondition = { [operator]: { [contextKey]: values } };
|
|
1219
|
+
const invertedCondition = invertConditions(singleCondition);
|
|
1220
|
+
// Merge with the original Allow conditions
|
|
1221
|
+
const mergedConditions = intersectConditions(allow.conditions || {}, invertedCondition);
|
|
1222
|
+
if (mergedConditions !== null) {
|
|
1223
|
+
results.push(new Permission(allow.effect, allow.service, allow.action, allow.resource, allow.notResource, mergedConditions, allow.principal, allow.notPrincipal));
|
|
1224
|
+
}
|
|
1225
|
+
}
|
|
1226
|
+
}
|
|
1227
|
+
return results;
|
|
1228
|
+
}
|
|
1229
|
+
//# sourceMappingURL=permission.js.map
|