Skip to content

Commit

Permalink
add id support
Browse files Browse the repository at this point in the history
  • Loading branch information
dvoet committed Jan 8, 2025
1 parent d2f4f7e commit 1e042e7
Show file tree
Hide file tree
Showing 3 changed files with 39 additions and 11 deletions.
12 changes: 11 additions & 1 deletion src/main/resources/swagger/api-docs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1983,7 +1983,7 @@ paths:
description: |
This endpoint is used to add and remove members of multiple policies across multiple resource in one api call.
Each policy is updated in order in its own transaction. If a policy does not exist on a resource,
processing stops with an error but all updates up to that point are committed. Missing member emails or policies fail fast. Additions are processed
processing stops with an error but all updates up to that point are committed. Missing member ids, emails or policies fail fast. Additions are processed
before removals so if a subject is in both the add and remove lists for a policy, the ultimate result is removal.
This call is idempotent, if members being added already exist or members being removed do not exist, nothing will happen.
operationId: bulkMembershipUpdateV2
Expand Down Expand Up @@ -5130,6 +5130,11 @@ components:
policyName:
type: string
description: The name of the policy
addUserIds:
type: array
description: Sam user ids to add to the policy
items:
type: string
addEmails:
type: array
description: Emails to add to the policy
Expand All @@ -5140,6 +5145,11 @@ components:
description: Other policies to add to the specified policy
items:
$ref: '#/components/schemas/PolicyIdentifiers'
removeUserIds:
type: array
description: Sam user ids to remove from the policy
items:
type: string
removeEmails:
type: array
description: Emails to remove from the policy
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,23 @@
package org.broadinstitute.dsde.workbench.sam.model.api

import org.broadinstitute.dsde.workbench.model.WorkbenchEmail
import org.broadinstitute.dsde.workbench.model.{WorkbenchEmail, WorkbenchUserId}
import org.broadinstitute.dsde.workbench.sam.model.{AccessPolicyName, PolicyIdentifiers, ResourceId, ResourceTypeName}
import spray.json.RootJsonFormat

case class PolicyMembershipUpdate(
policyName: AccessPolicyName,
addUserIds: Set[WorkbenchUserId],
addEmails: Set[WorkbenchEmail],
addPolicies: Set[PolicyIdentifiers],
removeUserIds: Set[WorkbenchUserId],
removeEmails: Set[WorkbenchEmail],
removePolicies: Set[PolicyIdentifiers]
)
object PolicyMembershipUpdate {
import spray.json.DefaultJsonProtocol._
import SamJsonSupport._
import org.broadinstitute.dsde.workbench.model.WorkbenchIdentityJsonSupport._
implicit val policyMembershipUpdateFormat: RootJsonFormat[PolicyMembershipUpdate] = jsonFormat5(PolicyMembershipUpdate.apply)
implicit val policyMembershipUpdateFormat: RootJsonFormat[PolicyMembershipUpdate] = jsonFormat7(PolicyMembershipUpdate.apply)
}

case class BulkMembershipUpdate(resourceTypeName: ResourceTypeName, resourceId: ResourceId, policyUpdates: Seq[PolicyMembershipUpdate])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,18 +152,22 @@ class ResourceService(
for {
emailsToSubjects <- mapEmailsToSubjects(emails, samRequestContext)
maybeEmailsError = validateMemberEmails(emailsToSubjects)
_ <- maybeRaiseBadRequest(maybeEmailsError)

maybeMemberPoliciesError <- validateMemberPolicies(memberPolicies, samRequestContext)
_ <- maybeRaiseBadRequest(maybeMemberPoliciesError)
maybeUserIdError <- validateUserIds(membershipUpdates.flatMap(_.policyUpdates.flatMap(up => up.addUserIds ++ up.removeUserIds)).toSet, samRequestContext)

_ <- maybeRaiseBadRequest(maybeUserIdError ++ maybeEmailsError ++ maybeMemberPoliciesError)

_ <- membershipUpdates.toList.traverse { bulkMembershipUpdate =>
val resource = FullyQualifiedResourceId(bulkMembershipUpdate.resourceTypeName, bulkMembershipUpdate.resourceId)
bulkMembershipUpdate.policyUpdates.toList.traverse { policyMembershipUpdate =>
val policyId = FullyQualifiedPolicyId(resource, policyMembershipUpdate.policyName)
val addSubjects = policyMembershipUpdate.addEmails.flatMap(emailsToSubjects) ++ policyMembershipUpdate.addPolicies.map(_.toFullyQualifiedPolicyId)
val addSubjects = policyMembershipUpdate.addEmails.flatMap(emailsToSubjects) ++ policyMembershipUpdate.addPolicies.map(
_.toFullyQualifiedPolicyId
) ++ policyMembershipUpdate.addUserIds
val removeSubjects =
policyMembershipUpdate.removeEmails.flatMap(emailsToSubjects) ++ policyMembershipUpdate.removePolicies.map(_.toFullyQualifiedPolicyId)
policyMembershipUpdate.removeEmails.flatMap(emailsToSubjects) ++ policyMembershipUpdate.removePolicies.map(
_.toFullyQualifiedPolicyId
) ++ policyMembershipUpdate.removeUserIds
for {
originalPolicies <- accessPolicyDAO.listAccessPolicies(policyId.resource, samRequestContext)
_ <- IO.raiseWhen(!originalPolicies.exists(_.id == policyId))(
Expand All @@ -177,9 +181,21 @@ class ResourceService(
} yield ()
}

private def maybeRaiseBadRequest(maybeError: Option[ErrorReport]) =
IO.raiseWhen(maybeError.isDefined)(
new WorkbenchExceptionWithErrorReport(maybeError.get.copy(statusCode = Option(StatusCodes.BadRequest)))
private def validateUserIds(userIds: Set[WorkbenchUserId], samRequestContext: SamRequestContext): IO[Option[ErrorReport]] =
userIds.toList.traverse { userId =>
directoryDAO.loadUser(userId, samRequestContext).map {
case None => Option(ErrorReport(s"User $userId not found"))
case _ => None
}
} map { errors =>
if (errors.nonEmpty) {
Option(ErrorReport("Invalid user ids specified", errors.flatten))
} else None
}

private def maybeRaiseBadRequest(maybeErrors: Iterable[ErrorReport]) =
IO.raiseWhen(maybeErrors.nonEmpty)(
new WorkbenchExceptionWithErrorReport(ErrorReport("Bad Request", Option(StatusCodes.BadRequest), maybeErrors.toSeq, Seq.empty, None, None))
)

/** Validates the resource first and if any validations fail, an exception is thrown with an error report that describes what failed. If validations pass,
Expand Down

0 comments on commit 1e042e7

Please sign in to comment.