Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
e6ffc27
enforce @RolesAllowed on microservice resources
Ma77Ball May 13, 2026
9c49941
Add dropwizard-auth as a dependency for workflow-compiling-service
Ma77Ball May 13, 2026
f029770
Merge branch 'main' into fix/RolesAllowedUnenforced
Ma77Ball May 13, 2026
6f6da20
Added appropriate licenses to License-binary
Ma77Ball May 13, 2026
e513a47
Merge remote-tracking branch 'origin/fix/RolesAllowedUnenforced' into…
Ma77Ball May 13, 2026
76b1eff
Merge branch 'main' into fix/RolesAllowedUnenforced
Ma77Ball May 13, 2026
950bf39
Merge branch 'main' into fix/RolesAllowedUnenforced
Ma77Ball May 13, 2026
3dcfef8
Merge branch 'main' into fix/RolesAllowedUnenforced
Ma77Ball May 13, 2026
a7963fd
Merge branch 'main' into fix/RolesAllowedUnenforced
Ma77Ball May 13, 2026
0a86a8f
Merge branch 'main' into fix/RolesAllowedUnenforced
Ma77Ball May 13, 2026
93f40f3
Merge branch 'main' into fix/RolesAllowedUnenforced
Ma77Ball May 15, 2026
5fda988
Added test
Ma77Ball May 15, 2026
993e28b
Merge remote-tracking branch 'origin/fix/RolesAllowedUnenforced' into…
Ma77Ball May 15, 2026
e1bdf9e
Merge branch 'main' into fix/RolesAllowedUnenforced
Ma77Ball May 19, 2026
46812ef
Merge branch 'main' into fix/RolesAllowedUnenforced
Ma77Ball May 20, 2026
a5ba0e9
Merge branch 'main' into fix/RolesAllowedUnenforced
Ma77Ball May 22, 2026
ec42428
extract registerAuthFeatures to decouple Jersey setup from DB init
Ma77Ball May 22, 2026
df10f86
Merge branch 'main' into fix/RolesAllowedUnenforced
Ma77Ball May 22, 2026
877960f
Merge branch 'main' into fix/RolesAllowedUnenforced
Ma77Ball May 22, 2026
47a21a3
Merge branch 'main' into fix/RolesAllowedUnenforced
Ma77Ball May 23, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
7 changes: 7 additions & 0 deletions computing-unit-managing-service/build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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] {
Expand All @@ -53,21 +54,16 @@ class ComputingUnitManagingService extends Application[ComputingUnitManagingServ
configuration: ComputingUnitManagingServiceConfiguration,
environment: Environment
): Unit = {
SqlServer.initConnection(
StorageConfig.jdbcUrl,
StorageConfig.jdbcUsername,
StorageConfig.jdbcPassword
)
// Register http resources
environment.jersey.setUrlPattern("/api/*")
environment.jersey.register(classOf[HealthCheckResource])

// Register JWT authentication filter
environment.jersey.register(new AuthDynamicFeature(classOf[JwtAuthFilter]))
ComputingUnitManagingService.registerAuthFeatures(environment)

// Enable @Auth annotation for injecting SessionUser
environment.jersey.register(
new io.dropwizard.auth.AuthValueFactoryProvider.Binder(classOf[SessionUser])
SqlServer.initConnection(
StorageConfig.jdbcUrl,
StorageConfig.jdbcUsername,
StorageConfig.jdbcPassword
)

environment.jersey().register(new ComputingUnitManagingResource)
Expand All @@ -79,6 +75,19 @@ class ComputingUnitManagingService extends Application[ComputingUnitManagingServ
}

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]))

// 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 = {
val configFilePath = Path
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* 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 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.registerAuthFeatures" should "register auth + RolesAllowedDynamicFeature on the Jersey environment" in {
val jersey = mock(classOf[JerseyEnvironment])
val env = mock(classOf[Environment])
when(env.jersey).thenReturn(jersey)

ComputingUnitManagingService.registerAuthFeatures(env)

verify(jersey).register(classOf[RolesAllowedDynamicFeature])
verify(jersey).register(org.mockito.ArgumentMatchers.any(classOf[AuthDynamicFeature]))
verify(jersey).register(
org.mockito.ArgumentMatchers.any(classOf[AuthValueFactoryProvider.Binder[_]])
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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])
}
}
3 changes: 3 additions & 0 deletions workflow-compiling-service/LICENSE-binary
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions workflow-compiling-service/build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -53,14 +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 the compilation endpoint
environment.jersey.register(classOf[WorkflowCompilationResource])

Expand Down Expand Up @@ -90,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
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* 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 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.registerAuthFeatures" should "register auth + RolesAllowedDynamicFeature on the Jersey environment" in {
val jersey = mock(classOf[JerseyEnvironment])
val env = mock(classOf[Environment])
when(env.jersey).thenReturn(jersey)

WorkflowCompilingService.registerAuthFeatures(env)

verify(jersey).register(classOf[RolesAllowedDynamicFeature])
verify(jersey).register(org.mockito.ArgumentMatchers.any(classOf[AuthDynamicFeature]))
verify(jersey).register(
org.mockito.ArgumentMatchers.any(classOf[AuthValueFactoryProvider.Binder[_]])
Comment thread
Ma77Ball marked this conversation as resolved.
)
}
}
Loading