@@ -21,6 +21,7 @@ import Wire.API.UserGroup
2121import Wire.BrigAPIAccess (BrigAPIAccess )
2222import Wire.BrigAPIAccess qualified as BrigAPI
2323import Wire.ScimSubsystem
24+ import Wire.UserGroupStore qualified as UGStore
2425import Wire.UserSubsystem
2526
2627data ScimSubsystemConfig = ScimSubsystemConfig
@@ -35,6 +36,7 @@ interpretScimSubsystem ::
3536 InterpreterFor ScimSubsystem r
3637interpretScimSubsystem = interpret $ \ case
3738 ScimCreateUserGroup teamId scimGroup -> createScimGroupImpl teamId scimGroup
39+ ScimUpdateUserGroup teamId userGroupId scimGroup -> scimUpdateUserGroupImpl teamId userGroupId scimGroup
3840
3941data ScimSubsystemError
4042 = ScimSubsystemError ScimError
@@ -57,9 +59,7 @@ createScimGroupImpl ::
5759createScimGroupImpl teamId grp = do
5860 membersNotManagedByScim <- do
5961 let uidsAsText = (. value) <$> grp. members
60- uids :: [UserId ] <-
61- let thrw = throw . ScimSubsystemInvalidGroupMemberId
62- in forM uidsAsText $ either (thrw . Text. pack) pure . parseIdFromText
62+ uids :: [UserId ] <- uidsAsText `mapM` parseMember
6363 users <- BrigAPI. getAccountsBy def {getByUserId = uids}
6464 pure $
6565 users
@@ -83,6 +83,53 @@ createScimGroupImpl teamId grp = do
8383 ScimSubsystemConfig scimBaseUri <- input
8484 pure $ toStoredGroup scimBaseUri ug
8585
86+ scimUpdateUserGroupImpl ::
87+ forall r .
88+ ( Member UGStore. UserGroupStore r ,
89+ Member (Input ScimSubsystemConfig ) r ,
90+ Member (Error ScimSubsystemError ) r ,
91+ Member UserSubsystem r ,
92+ Member (Input (Local () )) r
93+ ) =>
94+ TeamId ->
95+ UserGroupId ->
96+ SCG. Group ->
97+ Sem r (SCG. StoredGroup SparTag )
98+ scimUpdateUserGroupImpl teamId gid grp = do
99+ let includeChannels = False
100+ mExisting <- UGStore. getUserGroup teamId gid includeChannels
101+ existing <- maybe (scimThrow $ notFound " Group" (UUID. toText $ gid. toUUID)) pure mExisting
102+ when (existing. managedBy /= ManagedByScim ) do
103+ scimThrow $ notFound " Group" (UUID. toText $ gid. toUUID)
104+
105+ ugName <- either (scimThrow . badRequest InvalidValue . Just ) pure $ userGroupNameFromText grp. displayName
106+
107+ reqMemberIds <- for grp. members parseMember
108+
109+ let currentSet = Set. fromList (toList (runIdentity existing. members))
110+ requestedSet = Set. fromList reqMemberIds
111+ toAdd = requestedSet `Set.difference` currentSet
112+
113+ unless (null toAdd) do
114+ accounts <- inputQualifyLocal def {getByUserId = Set. toList toAdd} >>= getAccountsBy
115+ let nonScim = [userId u | u <- accounts, u. userManagedBy /= ManagedByScim ]
116+ found = Set. fromList (userId <$> accounts)
117+ missing = Set. toList (toAdd `Set.difference` found)
118+ unless (null nonScim) do
119+ throw (ScimSubsystemScimGroupWithNonScimMembers nonScim)
120+ case missing of
121+ [] -> pure ()
122+ (u : _) -> scimThrow $ notFound " User" (idToText u)
123+
124+ when (existing. name /= ugName) do
125+ _ <- UGStore. updateUserGroup teamId gid UserGroupUpdate {name = ugName}
126+ pure ()
127+
128+ UGStore. updateUsers gid (V. fromList reqMemberIds)
129+
130+ ScimSubsystemConfig scimBaseUri <- input
131+ maybe (scimThrow $ notFound " Group" (UUID. toText $ gid. toUUID)) (pure . toStoredGroup scimBaseUri) =<< UGStore. getUserGroup teamId gid includeChannels
132+
86133toStoredGroup :: Common. URI -> UserGroup -> SCG. StoredGroup SparTag
87134toStoredGroup scimBaseUri ug = Meta. WithMeta meta (Common. WithId ug. id_ sg)
88135 where
@@ -113,3 +160,9 @@ toStoredGroup scimBaseUri ug = Meta.WithMeta meta (Common.WithId ug.id_ sg)
113160 | uid <- toList (runIdentity ug. members)
114161 ]
115162 }
163+
164+ parseMember ::
165+ (Member (Error ScimSubsystemError ) r ) =>
166+ SCG. Member ->
167+ Sem r UserId
168+ parseMember m = parseIdFromText m. value & either (throw . ScimSubsystemInvalidGroupMemberId ) pure
0 commit comments