-
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
PROD-972 ID-1324 Deduplicate group synchronization calls to google. #1472
Changes from 5 commits
4e82d9b
1d2a7f3
b3e38be
8c7293e
74060b9
8dd00cd
0c4c496
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
<?xml version="1.0" encoding="UTF-8" standalone="no"?> | ||
<databaseChangeLog logicalFilePath="dummy" | ||
xmlns="http://www.liquibase.org/xml/ns/dbchangelog" | ||
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext" | ||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.4.xsd"> | ||
|
||
<changeSet logicalFilePath="dummy" author="tgarwood" id="add_group_version_and_last_synchronized_version"> | ||
|
||
<addColumn tableName="sam_group"> | ||
<!-- Default value initially set to 1 for all existing records --> | ||
<column name="version" type="BIGINT" defaultValue="1"> | ||
<constraints nullable="false"/> | ||
</column> | ||
</addColumn> | ||
|
||
<!-- Default value initially set to null for all existing records --> | ||
<addColumn tableName="sam_group"> | ||
<column name="last_synchronized_version" type="BIGINT"> | ||
<constraints nullable="true"/> | ||
</column> | ||
</addColumn> | ||
|
||
</changeSet> | ||
</databaseChangeLog> |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -45,7 +45,9 @@ trait DirectoryDAO { | |
|
||
def isGroupMember(groupId: WorkbenchGroupIdentity, member: WorkbenchSubject, samRequestContext: SamRequestContext): IO[Boolean] | ||
|
||
def updateSynchronizedDate(groupId: WorkbenchGroupIdentity, samRequestContext: SamRequestContext): IO[Unit] | ||
def updateSynchronizedDateAndVersion(group: WorkbenchGroup, samRequestContext: SamRequestContext): IO[Unit] | ||
|
||
def updateGroupUpdatedDateAndVersionWithSession(groupId: WorkbenchGroupIdentity, samRequestContext: SamRequestContext): IO[Unit] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is needed to update the policies in resource service and it just creates a write db session and then calls updateGroupUpdatedDateAndVersion There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. updateGroupUpdatedDateAndVersion is also only on PostgresGroupDAO which ResourceService doesnt have access to |
||
|
||
def getSynchronizedDate(groupId: WorkbenchGroupIdentity, samRequestContext: SamRequestContext): IO[Option[Date]] | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,6 +15,9 @@ import org.broadinstitute.dsde.workbench.sam.util.OpenTelemetryIOUtils._ | |
import org.broadinstitute.dsde.workbench.sam.util.SamRequestContext | ||
import org.broadinstitute.dsde.workbench.util.FutureSupport | ||
|
||
class GroupAlreadySynchronized(errorReport: ErrorReport = ErrorReport(StatusCodes.Conflict, "Group has already been synchronized")) | ||
extends WorkbenchExceptionWithErrorReport(errorReport) | ||
|
||
/** This class makes sure that our google groups have the right members. | ||
* | ||
* For the simple case it merely compares group membership given by directoryDAO against group membership given by googleDirectoryDAO and does the appropriate | ||
|
@@ -51,7 +54,15 @@ class GoogleGroupSynchronizer( | |
IO.pure(Map.empty) | ||
} else { | ||
for { | ||
group <- loadSamGroup(groupId, samRequestContext) | ||
group: WorkbenchGroup <- loadSamGroup(groupId, samRequestContext) | ||
// If group.version > group.lastSynchronizedVersion, then the group needs to be synchronized | ||
// Else Noop | ||
_ <- | ||
if (group.version > group.lastSynchronizedVersion.getOrElse(0)) { | ||
IO.unit | ||
} else { | ||
IO.raiseError(new GroupAlreadySynchronized) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't really like handling this with an exception since this is not really an exceptional situation, we just want an early escape. |
||
} | ||
members <- calculateAuthDomainIntersectionIfRequired(group, samRequestContext) | ||
subGroupSyncs <- syncSubGroupsIfRequired(group, visitedGroups, samRequestContext) | ||
googleMemberEmails <- loadGoogleGroupMemberEmailsMaybeCreateGroup(group, samRequestContext) | ||
|
@@ -63,7 +74,7 @@ class GoogleGroupSynchronizer( | |
addedUserSyncReports <- toAdd.toList.traverse(addMemberToGoogleGroup(group, samRequestContext)) | ||
removedUserSyncReports <- toRemove.toList.traverse(removeMemberFromGoogleGroup(group, samRequestContext)) | ||
|
||
_ <- directoryDAO.updateSynchronizedDate(groupId, samRequestContext) | ||
_ <- directoryDAO.updateSynchronizedDateAndVersion(group, samRequestContext) | ||
} yield Map(group.email -> Seq(addedUserSyncReports, removedUserSyncReports).flatten) ++ subGroupSyncs.flatten | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -215,7 +215,9 @@ consistent "has a" relationship is tracked by this ticket: https://broadworkbenc | |
roles: Set[ResourceRoleName], | ||
actions: Set[ResourceAction], | ||
descendantPermissions: Set[AccessPolicyDescendantPermissions], | ||
public: Boolean | ||
public: Boolean, | ||
version: Int = 1, | ||
lastSynchronizedVersion: Option[Int] = None | ||
) extends WorkbenchGroup | ||
|
||
@Lenses final case class AccessPolicyDescendantPermissions(resourceType: ResourceTypeName, actions: Set[ResourceAction], roles: Set[ResourceRoleName]) | ||
|
@@ -231,10 +233,18 @@ consistent "has a" relationship is tracked by this ticket: https://broadworkbenc | |
email: WorkbenchEmail, | ||
roles: Set[ResourceRoleName], | ||
actions: Set[ResourceAction], | ||
public: Boolean | ||
public: Boolean, | ||
version: Int = 1, | ||
lastSynchronizedVersion: Option[Int] = None | ||
) | ||
|
||
@Lenses final case class BasicWorkbenchGroup(id: WorkbenchGroupName, members: Set[WorkbenchSubject], email: WorkbenchEmail) extends WorkbenchGroup | ||
@Lenses final case class BasicWorkbenchGroup( | ||
id: WorkbenchGroupName, | ||
members: Set[WorkbenchSubject], | ||
email: WorkbenchEmail, | ||
version: Int = 1, | ||
lastSynchronizedVersion: Option[Int] = None | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Defaulting these values (including in the other models) was to avoid having to change BasicWorkbenchGroup in a ton of places. With the proper tests in place I think this is ok. |
||
) extends WorkbenchGroup | ||
object BasicWorkbenchGroup { | ||
def apply(workbenchGroup: WorkbenchGroup): BasicWorkbenchGroup = | ||
workbenchGroup.id match { | ||
|
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.
We want to pass in the group here so that we can use the version of the group that is held in memory when the sync started.