This is an automated email from the ASF dual-hosted git repository.
github-merge-queue[bot] pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/texera.git
The following commit(s) were added to refs/heads/main by this push:
new 0bce181efd fix(project): correct access privilege lookup (#5156)
0bce181efd is described below
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))
+ }
+}