This is an automated email from the ASF dual-hosted git repository.

github-merge-queue[bot] pushed a commit to branch 
gh-readonly-queue/main/pr-5156-770709b83d47fa7cd302ad5cb3ecf7a6546016de
in repository https://gitbox.apache.org/repos/asf/texera.git

commit 0bce181efd0604caa842aaeaa77eff63a6234c9f
Author: Minh Vu <[email protected]>
AuthorDate: Sun May 24 10:12:22 2026 +0200

    fix(project): correct access privilege lookup (#5156)
    
    ### What changes were proposed in this PR?
    Fixes the project access privilege lookup to query `PROJECT_USER_ACCESS`
    by `(pid, uid)` instead of mixing workflow/dataset access tables. Adds
    regression coverage for WRITE, READ, and NONE project access rows.
    
    ### Any related issues, documentation, discussions?
    Closes #5155
    
    ### How was this PR tested?
    - `git diff --check`
    - `git diff --cached --check`
    - Attempted:
    `JAVA_HOME=/opt/homebrew/opt/openjdk@17/libexec/openjdk.jdk/Contents/Home
    PATH=/opt/homebrew/opt/openjdk@17/bin:$PATH /tmp/texera-sbt
    "WorkflowExecutionService / Test / testOnly
    
org.apache.texera.web.resource.dashboard.user.project.ProjectAccessResourceSpec"`
    - Blocked before running the spec because local JOOQ generation could
    not authenticate to Postgres: `FATAL: password authentication failed for
    user "postgres"`, causing generated DAO classes to be unavailable.
    
    ### Was this PR authored or co-authored using generative AI tooling?
    No
    
    Co-authored-by: Meng Wang <[email protected]>
---
 .../user/project/ProjectAccessResource.scala       |  15 +--
 .../user/project/ProjectAccessResourceSpec.scala   | 132 +++++++++++++++++++++
 2 files changed, 138 insertions(+), 9 deletions(-)

diff --git 
a/amber/src/main/scala/org/apache/texera/web/resource/dashboard/user/project/ProjectAccessResource.scala
 
b/amber/src/main/scala/org/apache/texera/web/resource/dashboard/user/project/ProjectAccessResource.scala
index 1e3340973d..cf7e2cadc4 100644
--- 
a/amber/src/main/scala/org/apache/texera/web/resource/dashboard/user/project/ProjectAccessResource.scala
+++ 
b/amber/src/main/scala/org/apache/texera/web/resource/dashboard/user/project/ProjectAccessResource.scala
@@ -22,17 +22,14 @@ package 
org.apache.texera.web.resource.dashboard.user.project
 import io.dropwizard.auth.Auth
 import org.apache.texera.auth.SessionUser
 import org.apache.texera.dao.SqlServer
-import org.apache.texera.dao.jooq.generated.Tables.{
-  DATASET_USER_ACCESS,
-  PROJECT_USER_ACCESS,
-  USER,
-  WORKFLOW_USER_ACCESS
-}
+import org.apache.texera.dao.jooq.generated.Tables.{PROJECT_USER_ACCESS, USER}
 import org.apache.texera.dao.jooq.generated.enums.PrivilegeEnum
 import org.apache.texera.dao.jooq.generated.tables.daos.{ProjectDao, 
ProjectUserAccessDao, UserDao}
 import org.apache.texera.dao.jooq.generated.tables.pojos.ProjectUserAccess
 import org.apache.texera.web.model.common.AccessEntry
-import 
org.apache.texera.web.resource.dashboard.user.project.ProjectAccessResource.userHasWriteAccess
+import 
org.apache.texera.web.resource.dashboard.user.project.ProjectAccessResource.{
+  userHasWriteAccess
+}
 import org.jooq.DSLContext
 
 import java.util
@@ -54,11 +51,11 @@ object ProjectAccessResource {
     Option(
       context
         .select(PROJECT_USER_ACCESS.PRIVILEGE)
-        .from(WORKFLOW_USER_ACCESS)
+        .from(PROJECT_USER_ACCESS)
         .where(
           PROJECT_USER_ACCESS.PID
             .eq(pid)
-            .and(DATASET_USER_ACCESS.UID.eq(uid))
+            .and(PROJECT_USER_ACCESS.UID.eq(uid))
         )
         .fetchOneInto(classOf[PrivilegeEnum])
     ).getOrElse(PrivilegeEnum.NONE)
diff --git 
a/amber/src/test/scala/org/apache/texera/web/resource/dashboard/user/project/ProjectAccessResourceSpec.scala
 
b/amber/src/test/scala/org/apache/texera/web/resource/dashboard/user/project/ProjectAccessResourceSpec.scala
new file mode 100644
index 0000000000..185d5afcd1
--- /dev/null
+++ 
b/amber/src/test/scala/org/apache/texera/web/resource/dashboard/user/project/ProjectAccessResourceSpec.scala
@@ -0,0 +1,132 @@
+/*
+ * 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.web.resource.dashboard.user.project
+
+import org.apache.texera.auth.SessionUser
+import org.apache.texera.dao.MockTexeraDB
+import org.apache.texera.dao.jooq.generated.Tables.{PROJECT, 
PROJECT_USER_ACCESS, USER}
+import org.apache.texera.dao.jooq.generated.enums.{PrivilegeEnum, UserRoleEnum}
+import org.apache.texera.dao.jooq.generated.tables.daos.{ProjectUserAccessDao, 
UserDao}
+import org.apache.texera.dao.jooq.generated.tables.pojos.{ProjectUserAccess, 
User}
+import org.scalatest.flatspec.AnyFlatSpec
+import org.scalatest.{BeforeAndAfterAll, BeforeAndAfterEach}
+
+class ProjectAccessResourceSpec
+    extends AnyFlatSpec
+    with BeforeAndAfterAll
+    with BeforeAndAfterEach
+    with MockTexeraDB {
+
+  private val ownerUid = 7101
+  private val readerUid = 7102
+
+  private var owner: User = _
+  private var reader: User = _
+  private var userDao: UserDao = _
+  private var projectUserAccessDao: ProjectUserAccessDao = _
+  private var projectResource: ProjectResource = _
+
+  override protected def beforeAll(): Unit = {
+    initializeDBAndReplaceDSLContext()
+  }
+
+  override protected def beforeEach(): Unit = {
+    userDao = new UserDao(getDSLContext.configuration())
+    projectUserAccessDao = new 
ProjectUserAccessDao(getDSLContext.configuration())
+    projectResource = new ProjectResource()
+
+    owner = createUser(ownerUid, "project_owner", "[email protected]")
+    reader = createUser(readerUid, "project_reader", "[email protected]")
+
+    cleanupTestData()
+
+    userDao.insert(owner)
+    userDao.insert(reader)
+  }
+
+  override protected def afterEach(): Unit = {
+    cleanupTestData()
+  }
+
+  override protected def afterAll(): Unit = {
+    shutdownDB()
+  }
+
+  private def createUser(uid: Int, name: String, email: String): User = {
+    val user = new User
+    user.setUid(uid)
+    user.setName(name)
+    user.setEmail(email)
+    user.setPassword("password")
+    user.setRole(UserRoleEnum.REGULAR)
+    user
+  }
+
+  private def cleanupTestData(): Unit = {
+    getDSLContext
+      .deleteFrom(PROJECT_USER_ACCESS)
+      .where(PROJECT_USER_ACCESS.UID.in(ownerUid, readerUid))
+      .execute()
+
+    getDSLContext
+      .deleteFrom(PROJECT)
+      .where(PROJECT.OWNER_ID.eq(ownerUid))
+      .execute()
+
+    getDSLContext
+      .deleteFrom(USER)
+      .where(USER.UID.in(ownerUid, readerUid))
+      .execute()
+  }
+
+  "ProjectAccessResource.getProjectAccessPrivilege" should "return WRITE if 
granted" in {
+    val project = projectResource.createProject(new SessionUser(owner), 
"write-project")
+    val privilege = 
ProjectAccessResource.getProjectAccessPrivilege(project.getPid, ownerUid)
+
+    assert(privilege == PrivilegeEnum.WRITE)
+    assert(ProjectAccessResource.userHasWriteAccess(project.getPid, ownerUid))
+  }
+
+  it should "return READ if a project access row grants READ" in {
+    val project = projectResource.createProject(new SessionUser(owner), 
"read-project")
+    projectUserAccessDao.merge(
+      new ProjectUserAccess(readerUid, project.getPid, PrivilegeEnum.READ)
+    )
+
+    val privilege = 
ProjectAccessResource.getProjectAccessPrivilege(project.getPid, readerUid)
+
+    assert(privilege == PrivilegeEnum.READ)
+    assert(!ProjectAccessResource.userHasWriteAccess(project.getPid, 
readerUid))
+  }
+
+  it should "return NONE if the user only has access to another project" in {
+    val sharedProject = projectResource.createProject(new SessionUser(owner), 
"shared-project")
+    val privateProject = projectResource.createProject(new SessionUser(owner), 
"private-project")
+    projectUserAccessDao.merge(
+      new ProjectUserAccess(readerUid, sharedProject.getPid, 
PrivilegeEnum.READ)
+    )
+
+    val privilege =
+      ProjectAccessResource.getProjectAccessPrivilege(privateProject.getPid, 
readerUid)
+
+    assert(privilege == PrivilegeEnum.NONE)
+    assert(!ProjectAccessResource.userHasWriteAccess(privateProject.getPid, 
readerUid))
+  }
+}

Reply via email to