Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -252,11 +252,7 @@ object ResourceAccess {
requesterUid: Integer
): Response = {
requireWriteAccess(ctx, resource, id, requesterUid)
val grantee = new UserDao(ctx.configuration()).fetchOneByEmail(email)
if (grantee == null || grantee.getIsPlaceholder) {
throw new BadRequestException(s"No registered user with email $email")
}
val granteeUid = grantee.getUid
val granteeUid = resolveUidByEmail(ctx, email)
val granted = PrivilegeEnum.valueOf(privilege)

ctx
Expand All @@ -276,6 +272,7 @@ object ResourceAccess {
* Removes the user's explicit grant; a no-op when they hold none.
*
* @throws jakarta.ws.rs.ForbiddenException if the caller cannot modify the resource.
* @throws jakarta.ws.rs.BadRequestException if the email does not match a registered user.
*/
def revoke[R <: Record, A <: Record](
ctx: DSLContext,
Expand All @@ -285,7 +282,7 @@ object ResourceAccess {
requesterUid: Integer
): Response = {
requireWriteAccess(ctx, resource, id, requesterUid)
val granteeUid = new UserDao(ctx.configuration()).fetchOneByEmail(email).getUid
val granteeUid = resolveUidByEmail(ctx, email)

ctx
.delete(resource.accessTable)
Expand Down Expand Up @@ -323,6 +320,19 @@ object ResourceAccess {
)
}

/**
* Resolves an email to its user id, throwing BadRequestException (400) when no registered
* account matches — the service registers no ExceptionMapper for NullPointerException, so a
* bare dereference surfaces as an opaque HTTP 500. Shared by grant/revoke.
*/
private def resolveUidByEmail(ctx: DSLContext, email: String): Integer = {
val user = new UserDao(ctx.configuration()).fetchOneByEmail(email)
if (user == null || user.getIsPlaceholder) {
throw new BadRequestException(s"No registered user with email $email")
}
user.getUid
}

/**
* Emails of the owners of every resource the caller has an explicit grant on, for the
* owner facet on list pages.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,12 @@ class DatasetAccessResourceSpec
accessList(privateDataset.getDid) shouldBe empty
}

it should "reject a revoke for an email with no account" in {
assertThrows[BadRequestException] {
accessResource.revokeAccess(privateDataset.getDid, "nobody@example.com", ownerSession)
}
}

it should "be forbidden for a user without write access" in {
grantDirectly(privateDataset.getDid, readGranteeUser.getUid, PrivilegeEnum.READ)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,12 @@ class ModelAccessResourceSpec
userHasReadAccess(getDSLContext, privateModel.getMid, readGranteeUser.getUid) shouldBe false
}

it should "reject a revoke for an email with no account" in {
assertThrows[BadRequestException] {
accessResource.revokeAccess(privateModel.getMid, "nobody@example.com", ownerSession)
}
}

it should "allow a WRITE grantee to revoke another user's access" in {
grantDirectly(privateModel.getMid, writeGranteeUser.getUid, PrivilegeEnum.WRITE)
grantDirectly(privateModel.getMid, readGranteeUser.getUid, PrivilegeEnum.READ)
Expand Down
Loading