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-551b0baab5c7d462cb4047b383264bd8cca47a38
in repository https://gitbox.apache.org/repos/asf/texera.git

commit dc47197dbfb49680710152d4c0e1b0e383b68756
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(", ")}"
+    )
+  }
 
 }

Reply via email to