Skip to content

Commit 12dc610

Browse files
Add integration tests for analytics engine index-level authorization
Adds AnalyticsEngineSecurityIT which validates that the analytics engine's FGAC check (indices:data/read/analytics/query) is enforced end-to-end through the production SQL plugin PPL endpoint (/_plugins/_ppl) when querying composite (analytics-engine-backed) indices. Tests: - Authorized user with indices:data/read* can query a composite index - Unauthorized user (no index permissions) gets 403 - Authorized user cannot access an index outside their permissions (403) - User with indices:data/read/search* but NOT indices:data/read/analytics/query gets 403, proving the specific analytics action permission is evaluated The test cluster installs the full analytics plugin stack (analytics-engine, arrow-base, arrow-flight-rpc, analytics-backend-lucene, analytics-backend-datafusion, parquet-data-format, composite-engine) plus the security and SQL plugins. Run locally with local plugin zips: ./gradlew :integ-test:analyticsEngineSecurityIT \ -PanalyticsEngineZip=/path/to/analytics-engine.zip \ -ParrowBaseZip=/path/to/arrow-base.zip \ -ParrowFlightRpcZip=/path/to/arrow-flight-rpc.zip \ -PanalyticsBackendLuceneZip=/path/to/analytics-backend-lucene.zip \ -PanalyticsBackendDatafusionZip=/path/to/analytics-backend-datafusion.zip \ -PparquetDataFormatZip=/path/to/parquet-data-format.zip \ -PcompositeEngineZip=/path/to/composite-engine.zip \ -PnativeLibPath=/path/to/rust/target/release Signed-off-by: carrofin <carrofin@amazon.com> Signed-off-by: Finn Carroll <carrofin@amazon.com>
1 parent 8113f69 commit 12dc610

2 files changed

Lines changed: 444 additions & 0 deletions

File tree

integ-test/build.gradle

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,12 @@ ext.pluginVersion = opensearch_version.tokenize('-')[0]
276276
ext.featureBuildBase = "https://ci.opensearch.org/ci/dbc/feature-build-opensearch/feature-datafusion/latest/linux/x64/tar/builds/opensearch/plugins"
277277
ext.analyticsEngineZipDest = "${buildDir}/distributions/analytics-engine-${pluginVersion}-SNAPSHOT.zip"
278278
ext.arrowFlightRpcZipDest = "${buildDir}/distributions/arrow-flight-rpc-${pluginVersion}-SNAPSHOT.zip"
279+
ext.arrowBaseZipDest = "${buildDir}/distributions/arrow-base-${pluginVersion}-SNAPSHOT.zip"
280+
ext.testPplFrontendZipDest = "${buildDir}/distributions/test-ppl-frontend-${pluginVersion}-SNAPSHOT.zip"
281+
ext.analyticsBackendLuceneZipDest = "${buildDir}/distributions/analytics-backend-lucene-${pluginVersion}-SNAPSHOT.zip"
282+
ext.parquetDataFormatZipDest = "${buildDir}/distributions/parquet-data-format-${pluginVersion}-SNAPSHOT.zip"
283+
ext.compositeEngineZipDest = "${buildDir}/distributions/composite-engine-${pluginVersion}-SNAPSHOT.zip"
284+
ext.analyticsBackendDatafusionZipDest = "${buildDir}/distributions/analytics-backend-datafusion-${pluginVersion}-SNAPSHOT.zip"
279285

280286
task downloadAnalyticsEngineZip(type: Download) {
281287
src "${featureBuildBase}/1-analytics-engine-${pluginVersion}.zip"
@@ -293,6 +299,54 @@ task downloadArrowFlightRpcZip(type: Download) {
293299
onlyIf { !project.findProperty('arrowFlightRpcZip') }
294300
}
295301

302+
task downloadArrowBaseZip(type: Download) {
303+
src "${featureBuildBase}/0-arrow-base-${pluginVersion}.zip"
304+
dest arrowBaseZipDest
305+
overwrite false
306+
onlyIfModified true
307+
onlyIf { !project.findProperty('arrowBaseZip') }
308+
}
309+
310+
task downloadTestPplFrontendZip(type: Download) {
311+
src "${featureBuildBase}/1-test-ppl-frontend-${pluginVersion}.zip"
312+
dest testPplFrontendZipDest
313+
overwrite false
314+
onlyIfModified true
315+
onlyIf { !project.findProperty('testPplFrontendZip') }
316+
}
317+
318+
task downloadAnalyticsBackendLuceneZip(type: Download) {
319+
src "${featureBuildBase}/1-analytics-backend-lucene-${pluginVersion}.zip"
320+
dest analyticsBackendLuceneZipDest
321+
overwrite false
322+
onlyIfModified true
323+
onlyIf { !project.findProperty('analyticsBackendLuceneZip') }
324+
}
325+
326+
task downloadParquetDataFormatZip(type: Download) {
327+
src "${featureBuildBase}/1-parquet-data-format-${pluginVersion}.zip"
328+
dest parquetDataFormatZipDest
329+
overwrite false
330+
onlyIfModified true
331+
onlyIf { !project.findProperty('parquetDataFormatZip') }
332+
}
333+
334+
task downloadCompositeEngineZip(type: Download) {
335+
src "${featureBuildBase}/1-composite-engine-${pluginVersion}.zip"
336+
dest compositeEngineZipDest
337+
overwrite false
338+
onlyIfModified true
339+
onlyIf { !project.findProperty('compositeEngineZip') }
340+
}
341+
342+
task downloadAnalyticsBackendDatafusionZip(type: Download) {
343+
src "${featureBuildBase}/1-analytics-backend-datafusion-${pluginVersion}.zip"
344+
dest analyticsBackendDatafusionZipDest
345+
overwrite false
346+
onlyIfModified true
347+
onlyIf { !project.findProperty('analyticsBackendDatafusionZip') }
348+
}
349+
296350
def getAnalyticsEnginePlugin() {
297351
provider { (RegularFile) (() -> file(project.findProperty('analyticsEngineZip') ?: analyticsEngineZipDest)) }
298352
}
@@ -301,6 +355,30 @@ def getArrowFlightRpcPlugin() {
301355
provider { (RegularFile) (() -> file(project.findProperty('arrowFlightRpcZip') ?: arrowFlightRpcZipDest)) }
302356
}
303357

358+
def getArrowBasePlugin() {
359+
provider { (RegularFile) (() -> file(project.findProperty('arrowBaseZip') ?: arrowBaseZipDest)) }
360+
}
361+
362+
def getTestPplFrontendPlugin() {
363+
provider { (RegularFile) (() -> file(project.findProperty('testPplFrontendZip') ?: testPplFrontendZipDest)) }
364+
}
365+
366+
def getAnalyticsBackendLucenePlugin() {
367+
provider { (RegularFile) (() -> file(project.findProperty('analyticsBackendLuceneZip') ?: analyticsBackendLuceneZipDest)) }
368+
}
369+
370+
def getParquetDataFormatPlugin() {
371+
provider { (RegularFile) (() -> file(project.findProperty('parquetDataFormatZip') ?: parquetDataFormatZipDest)) }
372+
}
373+
374+
def getCompositeEnginePlugin() {
375+
provider { (RegularFile) (() -> file(project.findProperty('compositeEngineZip') ?: compositeEngineZipDest)) }
376+
}
377+
378+
def getAnalyticsBackendDatafusionPlugin() {
379+
provider { (RegularFile) (() -> file(project.findProperty('analyticsBackendDatafusionZip') ?: analyticsBackendDatafusionZipDest)) }
380+
}
381+
304382
testClusters {
305383
integTest {
306384
testDistribution = 'archive'
@@ -399,6 +477,52 @@ task analyticsEngineCompatIT(type: RestIntegTestTask) {
399477
}
400478
}
401479

480+
task analyticsEngineSecurityIT(type: RestIntegTestTask) {
481+
dependsOn downloadAnalyticsEngineZip, downloadArrowFlightRpcZip, downloadArrowBaseZip, downloadAnalyticsBackendLuceneZip, downloadParquetDataFormatZip, downloadCompositeEngineZip, downloadAnalyticsBackendDatafusionZip
482+
dependsOn ':opensearch-sql-plugin:bundlePlugin'
483+
484+
systemProperty 'tests.security.manager', 'false'
485+
systemProperty 'project.root', project.projectDir.absolutePath
486+
487+
doFirst {
488+
systemProperty "https", "false"
489+
systemProperty "user", "admin"
490+
systemProperty "password", "admin"
491+
}
492+
493+
filter {
494+
includeTestsMatching 'org.opensearch.sql.security.AnalyticsEngineSecurityIT'
495+
}
496+
}
497+
498+
testClusters.analyticsEngineSecurityIT {
499+
testDistribution = 'archive'
500+
plugin(getJobSchedulerPlugin())
501+
plugin(getArrowBasePlugin())
502+
plugin(getArrowFlightRpcPlugin())
503+
plugin(getAnalyticsEnginePlugin())
504+
plugin(getAnalyticsBackendLucenePlugin())
505+
plugin(getAnalyticsBackendDatafusionPlugin())
506+
plugin(getParquetDataFormatPlugin())
507+
plugin(getCompositeEnginePlugin())
508+
plugin ":opensearch-sql-plugin"
509+
// Arrow Flight / streaming transport requirements
510+
jvmArgs '--add-opens=java.base/java.nio=ALL-UNNAMED'
511+
jvmArgs '--enable-native-access=ALL-UNNAMED'
512+
systemProperty 'io.netty.allocator.numDirectArenas', '1'
513+
systemProperty 'io.netty.noUnsafe', 'false'
514+
systemProperty 'io.netty.tryUnsafe', 'true'
515+
systemProperty 'io.netty.tryReflectionSetAccessible', 'true'
516+
systemProperty 'opensearch.experimental.feature.pluggable.dataformat.enabled', 'true'
517+
systemProperty 'opensearch.experimental.feature.transport.stream.enabled', 'true'
518+
// Native library path for DataFusion/parquet — pass via -PnativeLibPath=/path/to/release/
519+
if (project.findProperty('nativeLibPath')) {
520+
systemProperty 'java.library.path', project.findProperty('nativeLibPath')
521+
}
522+
}
523+
524+
configureSecurityPlugin(testClusters.analyticsEngineSecurityIT)
525+
402526
task integJdbcTest(type: RestIntegTestTask) {
403527
testClusters.findAll {c -> c.clusterName == "integJdbcTest"}.first().with {
404528
plugin ":opensearch-sql-plugin"
@@ -463,6 +587,11 @@ task integTestWithSecurity(type: RestIntegTestTask) {
463587
logger.quiet "${desc.className}.${desc.name}: ${result.resultType} ${(result.getEndTime() - result.getStartTime())/1000}s"
464588
}
465589

590+
// Exclude tests that require the analytics engine plugin stack (run separately via analyticsEngineSecurityIT)
591+
filter {
592+
excludeTestsMatching 'org.opensearch.sql.security.AnalyticsEngineSecurityIT'
593+
}
594+
466595
systemProperty 'tests.security.manager', 'false'
467596
systemProperty 'project.root', project.projectDir.absolutePath
468597

0 commit comments

Comments
 (0)