This is an automated email from the ASF dual-hosted git repository.
github-merge-queue[bot] pushed a commit to branch
gh-readonly-queue/release/v1.2/pr-7054-7106df496367311d8e4b8325da4cd62a0561f132
in repository https://gitbox.apache.org/repos/asf/texera.git
commit e3b5b60c484219c083e55087f6ae77503123260e
Author: github-actions[bot]
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Thu Jul 30 09:13:59 2026 -0400
fix(amber, v1.2): require auth on retrieveWorkflowRuntimeStatistics (#7054)
### What changes were proposed in this PR?
Automated backport of #7014 to `release/v1.2`.
Source: 0635737cb1cab5c9c0ea9bfb1773becc9d91a69a · [automation
run](https://github.com/apache/texera/actions/runs/30489096963)
### Any related issues, documentation, discussions?
Backport of #7014. Originally linked #6977.
### How was this PR tested?
Release-branch CI runs on this branch once the build is fixed and this
PR is marked ready for review.
### Was this PR authored or co-authored using generative AI tooling?
No.
Signed-off-by: Xinyuan Lin <[email protected]>
Co-authored-by: Meng Wang <[email protected]>
Co-authored-by: Xinyuan Lin <[email protected]>
Co-authored-by: Copilot Autofix powered by AI
<[email protected]>
---
.../user/workflow/WorkflowExecutionsResource.scala | 6 +++-
.../workflow/WorkflowExecutionsResourceSpec.scala | 33 ++++++++++++++++++++++
2 files changed, 38 insertions(+), 1 deletion(-)
diff --git
a/amber/src/main/scala/org/apache/texera/web/resource/dashboard/user/workflow/WorkflowExecutionsResource.scala
b/amber/src/main/scala/org/apache/texera/web/resource/dashboard/user/workflow/WorkflowExecutionsResource.scala
index 8bd5eb504d..8787bf3798 100644
---
a/amber/src/main/scala/org/apache/texera/web/resource/dashboard/user/workflow/WorkflowExecutionsResource.scala
+++
b/amber/src/main/scala/org/apache/texera/web/resource/dashboard/user/workflow/WorkflowExecutionsResource.scala
@@ -704,10 +704,14 @@ class WorkflowExecutionsResource {
@GET
@Produces(Array(MediaType.APPLICATION_JSON))
@Path("/{wid}/stats/{eid}")
+ @RolesAllowed(Array("REGULAR", "ADMIN"))
def retrieveWorkflowRuntimeStatistics(
@PathParam("wid") wid: Integer,
- @PathParam("eid") eid: Integer
+ @PathParam("eid") eid: Integer,
+ @Auth sessionUser: SessionUser
): List[WorkflowRuntimeStatistics] = {
+ validateUserCanAccessWorkflow(sessionUser.getUser.getUid, wid)
+
// Create URI for runtime statistics
val uriString: String = context
.select(WORKFLOW_EXECUTIONS.RUNTIME_STATS_URI)
diff --git
a/amber/src/test/scala/org/apache/texera/web/resource/dashboard/user/workflow/WorkflowExecutionsResourceSpec.scala
b/amber/src/test/scala/org/apache/texera/web/resource/dashboard/user/workflow/WorkflowExecutionsResourceSpec.scala
index 75efa4da6a..ec35922f4e 100644
---
a/amber/src/test/scala/org/apache/texera/web/resource/dashboard/user/workflow/WorkflowExecutionsResourceSpec.scala
+++
b/amber/src/test/scala/org/apache/texera/web/resource/dashboard/user/workflow/WorkflowExecutionsResourceSpec.scala
@@ -872,5 +872,38 @@ class WorkflowExecutionsResourceSpec
val result = WorkflowExecutionsResource invokePrivate
privateMethod(testWorkflowWid, testUser)
assert(result.isEmpty)
}
+ // ─── new: endpoint auth-annotation audit (#6977) ──────────────────────────
+
+ "WorkflowExecutionsResource endpoints" should "all declare @RolesAllowed and
take an @Auth user" in {
+ val httpAnnotations: Seq[Class[_ <: java.lang.annotation.Annotation]] =
+ Seq(
+ classOf[javax.ws.rs.GET],
+ classOf[javax.ws.rs.PUT],
+ classOf[javax.ws.rs.POST],
+ classOf[javax.ws.rs.DELETE]
+ )
+ val handlers = classOf[WorkflowExecutionsResource].getDeclaredMethods.toSeq
+ .filter(m => httpAnnotations.exists(a => m.getAnnotation(a) != null))
+ assert(handlers.nonEmpty)
+
+ // exportResultToLocal authenticates manually: it serves a browser
form-submit
+ // download, which cannot carry an Authorization header, so the JWT
arrives as
+ // a form field and is verified in-method via JwtParser.parseToken
(including
+ // the role check). Any other handler must use the declarative annotations.
+ val manuallyAuthenticated = Set("exportResultToLocal")
+
+ val offenders = handlers.filterNot(m =>
manuallyAuthenticated.contains(m.getName)).filter { m =>
+ val hasRoles =
+ m.getAnnotation(classOf[javax.annotation.security.RolesAllowed]) !=
null
+ val hasAuthParam = m.getParameterAnnotations.exists(
+ _.exists(_.annotationType() == classOf[io.dropwizard.auth.Auth])
+ )
+ !(hasRoles && hasAuthParam)
+ }
+ assert(
+ offenders.isEmpty,
+ s"endpoints missing @RolesAllowed/@Auth:
${offenders.map(_.getName).sorted.mkString(", ")}"
+ )
+ }
}