-
Notifications
You must be signed in to change notification settings - Fork 12
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[CORE-58] Cascade policy deletion #1564
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some comments inline about separate APIs/functions vs adding to the existing ones; otherwise, looks like a good approach to me. I'd love a knowledgable Sam person to review the SQL queries but it seems like a good high-level approach.
Can you write tests for this?
src/main/scala/org/broadinstitute/dsde/workbench/sam/dataAccess/PostgresAccessPolicyDAO.scala
Outdated
Show resolved
Hide resolved
src/main/scala/org/broadinstitute/dsde/workbench/sam/service/ResourceService.scala
Outdated
Show resolved
Hide resolved
I think this is fine and should just happen all the time. No new api, not even an option on the existing api. Just do it. One other change that needs to happen is that any groups that are modified need to be synchronized by calling So, I would update |
src/main/scala/org/broadinstitute/dsde/workbench/sam/dataAccess/PostgresAccessPolicyDAO.scala
Outdated
Show resolved
Hide resolved
@@ -441,21 +440,11 @@ class ResourceService( | |||
// leave a tomb stone if the resource type does not allow reuse | |||
leaveTombStone = !resourceTypes(resource.resourceTypeName).reuseIds | |||
_ <- accessPolicyDAO.deleteResource(resource, leaveTombStone, samRequestContext) | |||
_ <- cloudSyncPolicies(resource, samRequestContext) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The resource was deleted in the line above, the database calls in cloudSyncPolicies
won't find anything. I think you need to get the list of groups that will be changed by having the resource removed before deleting the resource then sync them afterwards.
And something else I think I missed: PostgresGroupDAO.updateGroupUpdatedDateAndVersion
needs to be called for each group updated.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just to make sure I understand what you're asking, I should do something like:
affectedPolicies <- accessPolicyDAO.findAffectedPolicies(resource, samRequestContext) //New method
_ <- accessPolicyDAO.deleteResource(resource, leaveTombStone, samRequestContext)
_ <- cloudSyncPolicies(affectedPolicies, samRequestContext) //Update this method to take in policies instead of finding them
_ <- updateGroupUpdatedDateAndVersion(affectedPolicies, samRequestContext)
Is that correct?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes but updateGroupUpdatedDateAndVersion
should come before cloudSyncPolicies
. The sync process keeps track of the last synchronized version and won't do anything if that version is the same as the current version. So the order of operations should be change the policy/group in the sam db, bump the version, cloud sync.
And here is yet another idea (sorry if I am jerking you around): instead of your new database queries to bulk delete entries, you can list the policies that need to be updated along with which policy is being removed. For each of those pairs (policy changing, policy removed), call removeSubjectFromPolicy
(in the same class, ResourceService
). That takes care of the database calls, syncing, everything. The code would look something like
affectedPolicies <- accessPolicyDAO.findAffectedPolicies(resource, samRequestContext) //New method
_ <- affectedPolicies.traverse { case (policyToUpdate, policyToRemove) => removeSubjectFromPolicy(policyToUpdate, policyToRemove, samRequestContext) }
where findAffectedPolicies
(or maybe there is a better name) returns a IO[List[(FullyQualifiedPolicyId, FullyQualifiedPolicyId)]]
src/main/scala/org/broadinstitute/dsde/workbench/sam/dataAccess/PostgresAccessPolicyDAO.scala
Outdated
Show resolved
Hide resolved
src/main/scala/org/broadinstitute/dsde/workbench/sam/dataAccess/PostgresAccessPolicyDAO.scala
Outdated
Show resolved
Hide resolved
.map { rs => | ||
val resourceTypeName1 = rs.get[ResourceTypeName](rt.resultName.name) | ||
val resourceId1 = rs.get[ResourceId](r.resultName.name) | ||
val accessPolicyName1 = rs.get[AccessPolicyName](pg.resultName.name) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pg is an alias to the group table so its name will not be an access policy name
val g = GroupTable.syntax("g") | ||
val pg = GroupTable.syntax("pg") | ||
val gm = GroupMemberTable.syntax("gm") | ||
val p = PolicyTable.syntax("p") | ||
val rt = ResourceTypeTable.syntax("rt") | ||
val r = ResourceTable.syntax("r") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suggest naming these something more readable and include whether they are representing rows from the container or member policy. Same with fullyQualifiedPolicyId1 vs fullyQualifiedPolicyId2. One is the container and one is the member. Naming them better will help keep things straight.
|
||
// remove from cloud first so a failure there does not leave sam in a bad state | ||
_ <- cloudDeletePolicies(resource, samRequestContext) | ||
_ <- deleteActionManagedIdentitiesForResource(resource, samRequestContext) | ||
|
||
// leave a tomb stone if the resource type does not allow reuse | ||
leaveTombStone = !resourceTypes(resource.resourceTypeName).reuseIds | ||
affectedPolicies <- accessPolicyDAO.findAffectedPolicyGroups(resource, samRequestContext) // New method | ||
_ <- affectedPolicies.traverse { case (policyToUpdate, policyToRemove) => removeSubjectFromPolicy(policyToUpdate, policyToRemove, samRequestContext) } | ||
_ <- cloudSyncPolicies(affectedPolicies, samRequestContext) // TODO: use this on affected policies |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if you are using removeSubjectFromPolicy
, do you still need the call to cloudSyncPolicies
? Does removeSubjectFromPolicy
handle the syncing internally?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure, actually. Maybe @dvoet knows?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you don't need to call cloudSyncPolicies
resourceId: FullyQualifiedResourceId, | ||
samRequestContext: SamRequestContext | ||
): IO[List[(FullyQualifiedPolicyId, FullyQualifiedPolicyId)]] | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
findPolicyGroupMemberhips? findPolicyGroupParticipation? maybe findPolicyGroupsInUse to reflect the method it is supplanting?
@@ -569,6 +569,7 @@ class PostgresAccessPolicyDAO( | |||
|
|||
override def deleteResource(resource: FullyQualifiedResourceId, leaveTombStone: Boolean, samRequestContext: SamRequestContext): IO[Unit] = | |||
serializableWriteTransaction("deleteResource", samRequestContext) { implicit session => | |||
deletePolicyMembersFromGroups(resource) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is this still necessary here, given that ResourceService
calls removeSubjectFromPolicy
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess not!
policyGroups should not be empty | ||
policyGroups.head._1 shouldEqual parentPolicy.id | ||
policyGroups.head._2 shouldEqual childPolicy.id |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you could also write this as:
policyGroups should contain theSameElementsAs List(parentPolicy.id, childPolicy.id)
optional; pick whatever approach is more consistent with Sam code. I do see theSameElementsAs
used elsewhere in Sam.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
need an extra set of parens in there
policyGroups should contain theSameElementsAs List((parentPolicy.id, childPolicy.id))
and that 1 line can replace your 3
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this looks right. Approved assuming the cleanup David pointed and my test comments.
policyGroups should not be empty | ||
policyGroups.head._1 shouldEqual parentPolicy.id | ||
policyGroups.head._2 shouldEqual childPolicy.id |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
need an extra set of parens in there
policyGroups should contain theSameElementsAs List((parentPolicy.id, childPolicy.id))
and that 1 line can replace your 3
childResourceFullyQualifiedId.resourceTypeName, | ||
childResourceFullyQualifiedId.resourceId, | ||
Set.empty, | ||
Set(childPolicy) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would add 2 more policies to childResource
, one that is not a member of a parentResource
policy and on that is a member of a different policy. Maybe even add another parent resource.
Quality Gate passedIssues Measures |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks for sticking with this one!
Ticket: https://broadworkbench.atlassian.net/browse/CORE-58
This PR now cascade deletes policies when deleting a resource. Instead of throwing an error that some policy members are in use like
deleteResource
previous did, it simply removes the problematic rows from the table.I've demonstrated on my BEE that with this code, when a workspace has policies attached to it due to an imported TDR snapshot, it can be successfully deleted without any errors or orphaned policy references.
Why:
Users were unable to delete any workspace that had had a TDR snapshot with a policy imported into it, as those policies had entries in Sam's database that were not being deleted.
PR checklist