From e6ffc27e1a0943954c463c5d6656058300cd88b1 Mon Sep 17 00:00:00 2001 From: Matthew Ball Date: Tue, 12 May 2026 23:08:39 -0700 Subject: [PATCH 1/5] enforce @RolesAllowed on microservice resources --- build.sbt | 2 +- .../ComputingUnitManagingService.scala | 4 ++ .../apache/texera/service/ConfigService.scala | 4 ++ .../texera/service/ConfigServiceRunSpec.scala | 55 +++++++++++++++++++ .../service/WorkflowCompilingService.scala | 14 +++++ 5 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 config-service/src/test/scala/org/apache/texera/service/ConfigServiceRunSpec.scala diff --git a/build.sbt b/build.sbt index b7b6b3cfb20..8b38e2e009f 100644 --- a/build.sbt +++ b/build.sbt @@ -113,7 +113,7 @@ lazy val FileService = (project in file("file-service")) lazy val WorkflowOperator = (project in file("common/workflow-operator")).settings(asfLicensingSettingsWithVendored).dependsOn(WorkflowCore) lazy val WorkflowCompilingService = (project in file("workflow-compiling-service")) - .dependsOn(WorkflowOperator, Config) + .dependsOn(WorkflowOperator, Auth, Config) .settings(asfLicensingSettings) .settings( dependencyOverrides ++= Seq( diff --git a/computing-unit-managing-service/src/main/scala/org/apache/texera/service/ComputingUnitManagingService.scala b/computing-unit-managing-service/src/main/scala/org/apache/texera/service/ComputingUnitManagingService.scala index a15ced30a29..ce6a4cae2ba 100644 --- a/computing-unit-managing-service/src/main/scala/org/apache/texera/service/ComputingUnitManagingService.scala +++ b/computing-unit-managing-service/src/main/scala/org/apache/texera/service/ComputingUnitManagingService.scala @@ -32,6 +32,7 @@ import org.apache.texera.service.resource.{ ComputingUnitManagingResource, HealthCheckResource } +import org.glassfish.jersey.server.filter.RolesAllowedDynamicFeature import java.nio.file.Path class ComputingUnitManagingService extends Application[ComputingUnitManagingServiceConfiguration] { @@ -70,6 +71,9 @@ class ComputingUnitManagingService extends Application[ComputingUnitManagingServ new io.dropwizard.auth.AuthValueFactoryProvider.Binder(classOf[SessionUser]) ) + // Enforce @RolesAllowed annotations on resource methods + environment.jersey.register(classOf[RolesAllowedDynamicFeature]) + environment.jersey().register(new ComputingUnitManagingResource) environment.jersey().register(new ComputingUnitAccessResource) diff --git a/config-service/src/main/scala/org/apache/texera/service/ConfigService.scala b/config-service/src/main/scala/org/apache/texera/service/ConfigService.scala index c787016c270..545aac494b3 100644 --- a/config-service/src/main/scala/org/apache/texera/service/ConfigService.scala +++ b/config-service/src/main/scala/org/apache/texera/service/ConfigService.scala @@ -31,6 +31,7 @@ import org.apache.texera.config.DefaultsConfig import org.apache.texera.dao.SqlServer import org.apache.texera.service.resource.{ConfigResource, HealthCheckResource} import org.eclipse.jetty.server.session.SessionHandler +import org.glassfish.jersey.server.filter.RolesAllowedDynamicFeature import org.jooq.impl.DSL import java.nio.file.Path @@ -71,6 +72,9 @@ class ConfigService extends Application[ConfigServiceConfiguration] with LazyLog new io.dropwizard.auth.AuthValueFactoryProvider.Binder(classOf[SessionUser]) ) + // Enforce @RolesAllowed annotations on resource methods + environment.jersey.register(classOf[RolesAllowedDynamicFeature]) + environment.jersey.register(new ConfigResource) // Preload default.conf into site_setting tables diff --git a/config-service/src/test/scala/org/apache/texera/service/ConfigServiceRunSpec.scala b/config-service/src/test/scala/org/apache/texera/service/ConfigServiceRunSpec.scala new file mode 100644 index 00000000000..e3982e37750 --- /dev/null +++ b/config-service/src/test/scala/org/apache/texera/service/ConfigServiceRunSpec.scala @@ -0,0 +1,55 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.texera.service + +import io.dropwizard.core.setup.Environment +import io.dropwizard.jersey.setup.JerseyEnvironment +import io.dropwizard.jetty.MutableServletContextHandler +import io.dropwizard.jetty.setup.ServletEnvironment +import org.glassfish.jersey.server.filter.RolesAllowedDynamicFeature +import org.mockito.Mockito.{mock, verify, when} +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers + +class ConfigServiceRunSpec extends AnyFlatSpec with Matchers { + + // Verifies that the @RolesAllowed annotations on ConfigResource are actually + // enforced by Jersey, which requires RolesAllowedDynamicFeature to be + // registered on the Jersey environment. + "ConfigService.run" should "register RolesAllowedDynamicFeature on the Jersey environment" in { + val jersey = mock(classOf[JerseyEnvironment]) + val servlets = mock(classOf[ServletEnvironment]) + val context = mock(classOf[MutableServletContextHandler]) + val env = mock(classOf[Environment]) + when(env.jersey).thenReturn(jersey) + when(env.servlets).thenReturn(servlets) + when(env.getApplicationContext).thenReturn(context) + + val service = new ConfigService + // run() reaches into SqlServer near the end to preload defaults; that throws + // here because no real DB is wired up. By that point all Jersey registrations + // have already executed, so the verification below is still valid. + intercept[Exception] { + service.run(mock(classOf[ConfigServiceConfiguration]), env) + } + + verify(jersey).register(classOf[RolesAllowedDynamicFeature]) + } +} diff --git a/workflow-compiling-service/src/main/scala/org/apache/texera/service/WorkflowCompilingService.scala b/workflow-compiling-service/src/main/scala/org/apache/texera/service/WorkflowCompilingService.scala index 40fb3a2dd8f..dbfff379f89 100644 --- a/workflow-compiling-service/src/main/scala/org/apache/texera/service/WorkflowCompilingService.scala +++ b/workflow-compiling-service/src/main/scala/org/apache/texera/service/WorkflowCompilingService.scala @@ -20,14 +20,17 @@ package org.apache.texera.service import com.fasterxml.jackson.module.scala.DefaultScalaModule +import io.dropwizard.auth.AuthDynamicFeature import io.dropwizard.configuration.{EnvironmentVariableSubstitutor, SubstitutingSourceProvider} import io.dropwizard.core.Application import io.dropwizard.core.setup.{Bootstrap, Environment} import org.apache.texera.amber.config.StorageConfig import org.apache.texera.amber.util.ObjectMapperUtils +import org.apache.texera.auth.{JwtAuthFilter, SessionUser} import org.apache.texera.dao.SqlServer import org.apache.texera.service.resource.{HealthCheckResource, WorkflowCompilationResource} import org.eclipse.jetty.servlet.FilterHolder +import org.glassfish.jersey.server.filter.RolesAllowedDynamicFeature import java.nio.file.Path @@ -61,6 +64,17 @@ class WorkflowCompilingService extends Application[WorkflowCompilingServiceConfi environment.jersey.register(classOf[HealthCheckResource]) + // Register JWT authentication filter + environment.jersey.register(new AuthDynamicFeature(classOf[JwtAuthFilter])) + + // Enable @Auth annotation for injecting SessionUser + environment.jersey.register( + new io.dropwizard.auth.AuthValueFactoryProvider.Binder(classOf[SessionUser]) + ) + + // Enforce @RolesAllowed annotations on resource methods + environment.jersey.register(classOf[RolesAllowedDynamicFeature]) + // register the compilation endpoint environment.jersey.register(classOf[WorkflowCompilationResource]) From 9c499413aac4383a713b4f3f708963f31e94bb72 Mon Sep 17 00:00:00 2001 From: Matthew Ball Date: Tue, 12 May 2026 23:17:03 -0700 Subject: [PATCH 2/5] Add dropwizard-auth as a dependency for workflow-compiling-service --- workflow-compiling-service/build.sbt | 1 + 1 file changed, 1 insertion(+) diff --git a/workflow-compiling-service/build.sbt b/workflow-compiling-service/build.sbt index 9560751d00b..95a69269927 100644 --- a/workflow-compiling-service/build.sbt +++ b/workflow-compiling-service/build.sbt @@ -84,5 +84,6 @@ libraryDependencies ++= Seq( // Core Dependencies libraryDependencies ++= Seq( "io.dropwizard" % "dropwizard-core" % dropwizardVersion, + "io.dropwizard" % "dropwizard-auth" % dropwizardVersion, // Dropwizard Authentication module "com.fasterxml.jackson.module" %% "jackson-module-scala" % "2.18.6" ) From 6f6da209c09802d06cbfa12037ac6ff54363cc87 Mon Sep 17 00:00:00 2001 From: Matthew Ball Date: Tue, 12 May 2026 23:39:38 -0700 Subject: [PATCH 3/5] Added appropriate licenses to License-binary --- workflow-compiling-service/LICENSE-binary | 3 +++ 1 file changed, 3 insertions(+) diff --git a/workflow-compiling-service/LICENSE-binary b/workflow-compiling-service/LICENSE-binary index 5b7548a4edc..ed6a9e1d266 100644 --- a/workflow-compiling-service/LICENSE-binary +++ b/workflow-compiling-service/LICENSE-binary @@ -281,6 +281,7 @@ Scala/Java jars: - commons-pool.commons-pool-1.6.jar - dev.failsafe.failsafe-3.3.2.jar - io.airlift.aircompressor-0.27.jar + - io.dropwizard.dropwizard-auth-4.0.7.jar - io.dropwizard.dropwizard-configuration-4.0.7.jar - io.dropwizard.dropwizard-core-4.0.7.jar - io.dropwizard.dropwizard-health-4.0.7.jar @@ -296,6 +297,7 @@ Scala/Java jars: - io.dropwizard.dropwizard-validation-4.0.7.jar - io.dropwizard.logback.logback-throttling-appender-1.4.2.jar - io.dropwizard.metrics.metrics-annotation-4.2.25.jar + - io.dropwizard.metrics.metrics-caffeine-4.2.25.jar - io.dropwizard.metrics.metrics-core-4.2.25.jar - io.dropwizard.metrics.metrics-healthchecks-4.2.25.jar - io.dropwizard.metrics.metrics-jakarta-servlets-4.2.25.jar @@ -419,6 +421,7 @@ Scala/Java jars: - org.apache.yetus.audience-annotations-0.13.0.jar - org.apache.zookeeper.zookeeper-3.5.6.jar - org.apache.zookeeper.zookeeper-jute-3.5.6.jar + - org.bitbucket.b_c.jose4j-0.9.6.jar - org.eclipse.jetty.jetty-http-11.0.20.jar - org.eclipse.jetty.jetty-io-11.0.20.jar - org.eclipse.jetty.jetty-security-11.0.20.jar From 5fda9880bbfbe7a0332ad2e98ac5bb1e73b95e44 Mon Sep 17 00:00:00 2001 From: Matthew Ball Date: Fri, 15 May 2026 16:02:16 -0700 Subject: [PATCH 4/5] Added test --- computing-unit-managing-service/build.sbt | 7 +++ .../ComputingUnitManagingServiceRunSpec.scala | 59 +++++++++++++++++++ .../WorkflowCompilingServiceRunSpec.scala | 59 +++++++++++++++++++ 3 files changed, 125 insertions(+) create mode 100644 computing-unit-managing-service/src/test/scala/org/apache/texera/service/ComputingUnitManagingServiceRunSpec.scala create mode 100644 workflow-compiling-service/src/test/scala/org/apache/texera/service/WorkflowCompilingServiceRunSpec.scala diff --git a/computing-unit-managing-service/build.sbt b/computing-unit-managing-service/build.sbt index 3d385d33d30..1c39a6b03d9 100644 --- a/computing-unit-managing-service/build.sbt +++ b/computing-unit-managing-service/build.sbt @@ -34,6 +34,13 @@ Universal / mappings := AddMetaInfLicenseFiles.distMappings( // Dependency Versions val dropwizardVersion = "4.0.7" +val mockitoVersion = "5.4.0" + +// Test Dependencies +libraryDependencies ++= Seq( + "org.scalatest" %% "scalatest" % "3.2.17" % Test, + "org.mockito" % "mockito-core" % mockitoVersion % Test +) // Dependencies libraryDependencies ++= Seq( diff --git a/computing-unit-managing-service/src/test/scala/org/apache/texera/service/ComputingUnitManagingServiceRunSpec.scala b/computing-unit-managing-service/src/test/scala/org/apache/texera/service/ComputingUnitManagingServiceRunSpec.scala new file mode 100644 index 00000000000..38e28e9253e --- /dev/null +++ b/computing-unit-managing-service/src/test/scala/org/apache/texera/service/ComputingUnitManagingServiceRunSpec.scala @@ -0,0 +1,59 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.texera.service + +import io.dropwizard.auth.{AuthDynamicFeature, AuthValueFactoryProvider} +import io.dropwizard.core.setup.Environment +import io.dropwizard.jersey.setup.JerseyEnvironment +import io.dropwizard.jetty.MutableServletContextHandler +import org.glassfish.jersey.server.filter.RolesAllowedDynamicFeature +import org.mockito.Mockito.{mock, verify, when} +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers + +class ComputingUnitManagingServiceRunSpec extends AnyFlatSpec with Matchers { + + // Verifies that the @RolesAllowed annotations on resource methods are actually + // enforced by Jersey, which requires RolesAllowedDynamicFeature, AuthDynamicFeature, + // and AuthValueFactoryProvider.Binder to be registered on the Jersey environment. + "ComputingUnitManagingService.run" should "register auth + RolesAllowedDynamicFeature on the Jersey environment" in { + val jersey = mock(classOf[JerseyEnvironment]) + val context = mock(classOf[MutableServletContextHandler]) + val env = mock(classOf[Environment]) + when(env.jersey).thenReturn(jersey) + when(env.getApplicationContext).thenReturn(context) + + val service = new ComputingUnitManagingService + // run() may throw if SqlServer/StorageConfig or other side effects fail + // under mocks. Auth + RolesAllowedDynamicFeature registrations happen + // before any such failure, so the verifications below remain valid. + try { + service.run(mock(classOf[ComputingUnitManagingServiceConfiguration]), env) + } catch { + case _: Throwable => // expected under mocks + } + + verify(jersey).register(classOf[RolesAllowedDynamicFeature]) + verify(jersey).register(org.mockito.ArgumentMatchers.any(classOf[AuthDynamicFeature])) + verify(jersey).register( + org.mockito.ArgumentMatchers.any(classOf[AuthValueFactoryProvider.Binder[_]]) + ) + } +} diff --git a/workflow-compiling-service/src/test/scala/org/apache/texera/service/WorkflowCompilingServiceRunSpec.scala b/workflow-compiling-service/src/test/scala/org/apache/texera/service/WorkflowCompilingServiceRunSpec.scala new file mode 100644 index 00000000000..e3049e8bcd9 --- /dev/null +++ b/workflow-compiling-service/src/test/scala/org/apache/texera/service/WorkflowCompilingServiceRunSpec.scala @@ -0,0 +1,59 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.texera.service + +import io.dropwizard.auth.{AuthDynamicFeature, AuthValueFactoryProvider} +import io.dropwizard.core.setup.Environment +import io.dropwizard.jersey.setup.JerseyEnvironment +import io.dropwizard.jetty.MutableServletContextHandler +import org.glassfish.jersey.server.filter.RolesAllowedDynamicFeature +import org.mockito.Mockito.{mock, verify, when} +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers + +class WorkflowCompilingServiceRunSpec extends AnyFlatSpec with Matchers { + + // Verifies that the @RolesAllowed annotations on resource methods are actually + // enforced by Jersey, which requires RolesAllowedDynamicFeature, AuthDynamicFeature, + // and AuthValueFactoryProvider.Binder to be registered on the Jersey environment. + "WorkflowCompilingService.run" should "register auth + RolesAllowedDynamicFeature on the Jersey environment" in { + val jersey = mock(classOf[JerseyEnvironment]) + val context = mock(classOf[MutableServletContextHandler]) + val env = mock(classOf[Environment]) + when(env.jersey).thenReturn(jersey) + when(env.getApplicationContext).thenReturn(context) + + val service = new WorkflowCompilingService + // run() may throw if SqlServer/StorageConfig or other side effects fail + // under mocks. Auth + RolesAllowedDynamicFeature registrations happen + // before any such failure, so the verifications below remain valid. + try { + service.run(mock(classOf[WorkflowCompilingServiceConfiguration]), env) + } catch { + case _: Throwable => // expected under mocks + } + + verify(jersey).register(classOf[RolesAllowedDynamicFeature]) + verify(jersey).register(org.mockito.ArgumentMatchers.any(classOf[AuthDynamicFeature])) + verify(jersey).register( + org.mockito.ArgumentMatchers.any(classOf[AuthValueFactoryProvider.Binder[_]]) + ) + } +} From ec42428fd9c3f3e36f8ef69cc91b6f636671edef Mon Sep 17 00:00:00 2001 From: Matthew Ball Date: Fri, 22 May 2026 01:10:31 -0700 Subject: [PATCH 5/5] extract registerAuthFeatures to decouple Jersey setup from DB init --- .../ComputingUnitManagingService.scala | 29 ++++++++++------- .../ComputingUnitManagingServiceRunSpec.scala | 15 ++------- .../service/WorkflowCompilingService.scala | 31 +++++++++++-------- .../WorkflowCompilingServiceRunSpec.scala | 15 ++------- 4 files changed, 39 insertions(+), 51 deletions(-) diff --git a/computing-unit-managing-service/src/main/scala/org/apache/texera/service/ComputingUnitManagingService.scala b/computing-unit-managing-service/src/main/scala/org/apache/texera/service/ComputingUnitManagingService.scala index ce6a4cae2ba..6184cf545a2 100644 --- a/computing-unit-managing-service/src/main/scala/org/apache/texera/service/ComputingUnitManagingService.scala +++ b/computing-unit-managing-service/src/main/scala/org/apache/texera/service/ComputingUnitManagingService.scala @@ -54,15 +54,29 @@ class ComputingUnitManagingService extends Application[ComputingUnitManagingServ configuration: ComputingUnitManagingServiceConfiguration, environment: Environment ): Unit = { + // Register http resources + environment.jersey.setUrlPattern("/api/*") + environment.jersey.register(classOf[HealthCheckResource]) + + ComputingUnitManagingService.registerAuthFeatures(environment) + SqlServer.initConnection( StorageConfig.jdbcUrl, StorageConfig.jdbcUsername, StorageConfig.jdbcPassword ) - // Register http resources - environment.jersey.setUrlPattern("/api/*") - environment.jersey.register(classOf[HealthCheckResource]) + environment.jersey().register(new ComputingUnitManagingResource) + environment.jersey().register(new ComputingUnitAccessResource) + + // Route request logs through SLF4J, controlled by TEXERA_SERVICE_LOG_LEVEL + RequestLoggingFilter.register(environment.getApplicationContext) + } +} + +object ComputingUnitManagingService { + // Registers JWT auth, @Auth injection, and @RolesAllowed enforcement. + def registerAuthFeatures(environment: Environment): Unit = { // Register JWT authentication filter environment.jersey.register(new AuthDynamicFeature(classOf[JwtAuthFilter])) @@ -73,16 +87,7 @@ class ComputingUnitManagingService extends Application[ComputingUnitManagingServ // Enforce @RolesAllowed annotations on resource methods environment.jersey.register(classOf[RolesAllowedDynamicFeature]) - - environment.jersey().register(new ComputingUnitManagingResource) - environment.jersey().register(new ComputingUnitAccessResource) - - // Route request logs through SLF4J, controlled by TEXERA_SERVICE_LOG_LEVEL - RequestLoggingFilter.register(environment.getApplicationContext) } -} - -object ComputingUnitManagingService { def main(args: Array[String]): Unit = { val configFilePath = Path diff --git a/computing-unit-managing-service/src/test/scala/org/apache/texera/service/ComputingUnitManagingServiceRunSpec.scala b/computing-unit-managing-service/src/test/scala/org/apache/texera/service/ComputingUnitManagingServiceRunSpec.scala index 38e28e9253e..d27f5725ac9 100644 --- a/computing-unit-managing-service/src/test/scala/org/apache/texera/service/ComputingUnitManagingServiceRunSpec.scala +++ b/computing-unit-managing-service/src/test/scala/org/apache/texera/service/ComputingUnitManagingServiceRunSpec.scala @@ -22,7 +22,6 @@ package org.apache.texera.service import io.dropwizard.auth.{AuthDynamicFeature, AuthValueFactoryProvider} import io.dropwizard.core.setup.Environment import io.dropwizard.jersey.setup.JerseyEnvironment -import io.dropwizard.jetty.MutableServletContextHandler import org.glassfish.jersey.server.filter.RolesAllowedDynamicFeature import org.mockito.Mockito.{mock, verify, when} import org.scalatest.flatspec.AnyFlatSpec @@ -33,22 +32,12 @@ class ComputingUnitManagingServiceRunSpec extends AnyFlatSpec with Matchers { // Verifies that the @RolesAllowed annotations on resource methods are actually // enforced by Jersey, which requires RolesAllowedDynamicFeature, AuthDynamicFeature, // and AuthValueFactoryProvider.Binder to be registered on the Jersey environment. - "ComputingUnitManagingService.run" should "register auth + RolesAllowedDynamicFeature on the Jersey environment" in { + "ComputingUnitManagingService.registerAuthFeatures" should "register auth + RolesAllowedDynamicFeature on the Jersey environment" in { val jersey = mock(classOf[JerseyEnvironment]) - val context = mock(classOf[MutableServletContextHandler]) val env = mock(classOf[Environment]) when(env.jersey).thenReturn(jersey) - when(env.getApplicationContext).thenReturn(context) - val service = new ComputingUnitManagingService - // run() may throw if SqlServer/StorageConfig or other side effects fail - // under mocks. Auth + RolesAllowedDynamicFeature registrations happen - // before any such failure, so the verifications below remain valid. - try { - service.run(mock(classOf[ComputingUnitManagingServiceConfiguration]), env) - } catch { - case _: Throwable => // expected under mocks - } + ComputingUnitManagingService.registerAuthFeatures(env) verify(jersey).register(classOf[RolesAllowedDynamicFeature]) verify(jersey).register(org.mockito.ArgumentMatchers.any(classOf[AuthDynamicFeature])) diff --git a/workflow-compiling-service/src/main/scala/org/apache/texera/service/WorkflowCompilingService.scala b/workflow-compiling-service/src/main/scala/org/apache/texera/service/WorkflowCompilingService.scala index dbfff379f89..8dc573aaf8b 100644 --- a/workflow-compiling-service/src/main/scala/org/apache/texera/service/WorkflowCompilingService.scala +++ b/workflow-compiling-service/src/main/scala/org/apache/texera/service/WorkflowCompilingService.scala @@ -56,25 +56,16 @@ class WorkflowCompilingService extends Application[WorkflowCompilingServiceConfi // serve backend at /api environment.jersey.setUrlPattern("/api/*") + environment.jersey.register(classOf[HealthCheckResource]) + + WorkflowCompilingService.registerAuthFeatures(environment) + SqlServer.initConnection( StorageConfig.jdbcUrl, StorageConfig.jdbcUsername, StorageConfig.jdbcPassword ) - environment.jersey.register(classOf[HealthCheckResource]) - - // Register JWT authentication filter - environment.jersey.register(new AuthDynamicFeature(classOf[JwtAuthFilter])) - - // Enable @Auth annotation for injecting SessionUser - environment.jersey.register( - new io.dropwizard.auth.AuthValueFactoryProvider.Binder(classOf[SessionUser]) - ) - - // Enforce @RolesAllowed annotations on resource methods - environment.jersey.register(classOf[RolesAllowedDynamicFeature]) - // register the compilation endpoint environment.jersey.register(classOf[WorkflowCompilationResource]) @@ -104,6 +95,20 @@ class WorkflowCompilingService extends Application[WorkflowCompilingServiceConfi } object WorkflowCompilingService { + // Registers JWT auth, @Auth injection, and @RolesAllowed enforcement. + def registerAuthFeatures(environment: Environment): Unit = { + // Register JWT authentication filter + environment.jersey.register(new AuthDynamicFeature(classOf[JwtAuthFilter])) + + // Enable @Auth annotation for injecting SessionUser + environment.jersey.register( + new io.dropwizard.auth.AuthValueFactoryProvider.Binder(classOf[SessionUser]) + ) + + // Enforce @RolesAllowed annotations on resource methods + environment.jersey.register(classOf[RolesAllowedDynamicFeature]) + } + def main(args: Array[String]): Unit = { // set the configuration file's path val configFilePath = Path diff --git a/workflow-compiling-service/src/test/scala/org/apache/texera/service/WorkflowCompilingServiceRunSpec.scala b/workflow-compiling-service/src/test/scala/org/apache/texera/service/WorkflowCompilingServiceRunSpec.scala index e3049e8bcd9..ff5da1b5613 100644 --- a/workflow-compiling-service/src/test/scala/org/apache/texera/service/WorkflowCompilingServiceRunSpec.scala +++ b/workflow-compiling-service/src/test/scala/org/apache/texera/service/WorkflowCompilingServiceRunSpec.scala @@ -22,7 +22,6 @@ package org.apache.texera.service import io.dropwizard.auth.{AuthDynamicFeature, AuthValueFactoryProvider} import io.dropwizard.core.setup.Environment import io.dropwizard.jersey.setup.JerseyEnvironment -import io.dropwizard.jetty.MutableServletContextHandler import org.glassfish.jersey.server.filter.RolesAllowedDynamicFeature import org.mockito.Mockito.{mock, verify, when} import org.scalatest.flatspec.AnyFlatSpec @@ -33,22 +32,12 @@ class WorkflowCompilingServiceRunSpec extends AnyFlatSpec with Matchers { // Verifies that the @RolesAllowed annotations on resource methods are actually // enforced by Jersey, which requires RolesAllowedDynamicFeature, AuthDynamicFeature, // and AuthValueFactoryProvider.Binder to be registered on the Jersey environment. - "WorkflowCompilingService.run" should "register auth + RolesAllowedDynamicFeature on the Jersey environment" in { + "WorkflowCompilingService.registerAuthFeatures" should "register auth + RolesAllowedDynamicFeature on the Jersey environment" in { val jersey = mock(classOf[JerseyEnvironment]) - val context = mock(classOf[MutableServletContextHandler]) val env = mock(classOf[Environment]) when(env.jersey).thenReturn(jersey) - when(env.getApplicationContext).thenReturn(context) - val service = new WorkflowCompilingService - // run() may throw if SqlServer/StorageConfig or other side effects fail - // under mocks. Auth + RolesAllowedDynamicFeature registrations happen - // before any such failure, so the verifications below remain valid. - try { - service.run(mock(classOf[WorkflowCompilingServiceConfiguration]), env) - } catch { - case _: Throwable => // expected under mocks - } + WorkflowCompilingService.registerAuthFeatures(env) verify(jersey).register(classOf[RolesAllowedDynamicFeature]) verify(jersey).register(org.mockito.ArgumentMatchers.any(classOf[AuthDynamicFeature]))