Skip to content

Commit abdb596

Browse files
authored
Merge pull request #150 from unisoncomputing/cp/patch-org-membership
Add/remove users from org memberships when roles change
2 parents f661a2c + 7f807e5 commit abdb596

File tree

5 files changed

+137
-1
lines changed

5 files changed

+137
-1
lines changed

src/Share/Web/Share/Orgs/Impl.hs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ module Share.Web.Share.Orgs.Impl (server) where
44

55
import Control.Lens
66
import Data.Either (isRight)
7+
import Data.Map qualified as Map
78
import Data.Set qualified as Set
89
import Servant
910
import Servant.Server.Generic
@@ -104,17 +105,39 @@ addRolesEndpoint orgHandle caller (AddRolesRequest {roleAssignments}) = do
104105
assertNoOrgSubjects roleAssignments
105106
PG.runTransaction do
106107
orgRoles <- OrgQ.addOrgRoles orgId roleAssignments
108+
let newMembers =
109+
(computeOrgMembershipChanges roleAssignments)
110+
& Map.filter id
111+
& Map.keysSet
112+
OrgQ.addOrgMembers orgId newMembers
107113
ListRolesResponse True . canonicalRoleAssignmentOrdering <$> displaySubjectsOf (traversed . traversed) orgRoles
108114

109115
removeRolesEndpoint :: UserHandle -> UserId -> RemoveRolesRequest -> WebApp ListRolesResponse
110116
removeRolesEndpoint orgHandle caller (RemoveRolesRequest {roleAssignments}) = do
111117
orgId <- orgIdByHandle orgHandle
112118
_authZReceipt <- AuthZ.permissionGuard $ AuthZ.checkEditOrgRoles caller orgId
113119
PG.runTransactionOrRespondError do
120+
let updatedUsersMap =
121+
roleAssignments
122+
& foldMap
123+
( \RoleAssignment {subject} ->
124+
case subject of
125+
UserSubject userId -> Map.singleton userId Set.empty
126+
_ -> Map.empty
127+
)
114128
orgRoles <- OrgQ.removeOrgRoles orgId roleAssignments
115129
OrgQ.doesOrgHaveOwner orgId >>= \case
116130
False -> throwError OrgMustHaveOwnerError
117131
True -> pure ()
132+
let remainingRolesMap = computeOrgMembershipChanges orgRoles
133+
let usersWithNoRemainingRoles = Map.keysSet updatedUsersMap `Set.difference` Map.keysSet remainingRolesMap
134+
let evictedMembers =
135+
remainingRolesMap
136+
-- Only keep users who should no longer be members
137+
& Map.filter not
138+
& Map.keysSet
139+
& Set.union usersWithNoRemainingRoles
140+
OrgQ.removeOrgMembers orgId evictedMembers
118141

119142
ListRolesResponse True . canonicalRoleAssignmentOrdering <$> displaySubjectsOf (traversed . traversed) orgRoles
120143

@@ -158,3 +181,35 @@ assertNoOrgSubjects roleAssignments = do
158181
)
159182
when hasOrgSubject do
160183
respondError OrgMemberOfOrgError
184+
185+
-- | This is part of a hack to temporarily fix org membership issues
186+
-- until we have time for a more robust solution.
187+
shouldRoleBeOrgMember :: RoleRef -> Bool
188+
shouldRoleBeOrgMember = \case
189+
RoleOrgViewer -> False
190+
RoleOrgContributor -> True
191+
RoleOrgMaintainer -> True
192+
RoleOrgAdmin -> True
193+
RoleOrgOwner -> True
194+
RoleOrgDefault -> True
195+
RoleTeamAdmin -> True
196+
RoleProjectViewer -> False
197+
RoleProjectContributor -> True
198+
RoleProjectMaintainer -> True
199+
RoleProjectAdmin -> True
200+
RoleProjectOwner -> True
201+
RoleProjectPublicAccess -> False
202+
203+
-- | Returns a list of users and whether they should end up as members of the org or not
204+
computeOrgMembershipChanges :: [RoleAssignment ResolvedAuthSubject] -> Map UserId Bool
205+
computeOrgMembershipChanges roleAssignments =
206+
roleAssignments
207+
& foldMap
208+
( \RoleAssignment {subject, roles} ->
209+
case subject of
210+
UserSubject userId ->
211+
let shouldBeMember = any shouldRoleBeOrgMember roles
212+
in [(userId, shouldBeMember)]
213+
_ -> mempty
214+
)
215+
& Map.fromListWith (||)
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
{
2+
"body": {
3+
"members": [
4+
{
5+
"avatarUrl": "https://www.gravatar.com/avatar/205e460b479e2e5b48aec07710c08d50?f=y&d=retro",
6+
"handle": "admin",
7+
"name": "Admin User",
8+
"userId": "U-<UUID>"
9+
},
10+
{
11+
"avatarUrl": null,
12+
"handle": "some-user",
13+
"name": null,
14+
"userId": "U-<UUID>"
15+
},
16+
{
17+
"avatarUrl": null,
18+
"handle": "test",
19+
"name": null,
20+
"userId": "U-<UUID>"
21+
},
22+
{
23+
"avatarUrl": "https://www.gravatar.com/avatar/205e460b479e2e5b48aec07710c08d50?f=y&d=retro",
24+
"handle": "transcripts",
25+
"name": "The Transcript User",
26+
"userId": "U-<UUID>"
27+
}
28+
]
29+
},
30+
"status": [
31+
{
32+
"status_code": 200
33+
}
34+
]
35+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
{
2+
"body": {
3+
"members": [
4+
{
5+
"avatarUrl": "https://www.gravatar.com/avatar/205e460b479e2e5b48aec07710c08d50?f=y&d=retro",
6+
"handle": "admin",
7+
"name": "Admin User",
8+
"userId": "U-<UUID>"
9+
},
10+
{
11+
"avatarUrl": null,
12+
"handle": "test",
13+
"name": null,
14+
"userId": "U-<UUID>"
15+
},
16+
{
17+
"avatarUrl": "https://www.gravatar.com/avatar/205e460b479e2e5b48aec07710c08d50?f=y&d=retro",
18+
"handle": "transcripts",
19+
"name": "The Transcript User",
20+
"userId": "U-<UUID>"
21+
}
22+
]
23+
},
24+
"status": [
25+
{
26+
"status_code": 200
27+
}
28+
]
29+
}

transcripts/share-apis/roles/maintainer-project-view.json

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,18 @@
2020
},
2121
"permissions": [
2222
"project:view",
23-
"project:contribute"
23+
"project:contribute",
24+
"project:maintain",
25+
"project:create",
26+
"org:view",
27+
"org:edit",
28+
"team:view",
29+
"notification_hub_entry:view",
30+
"notification_hub_entry:update",
31+
"notification_subscription:view",
32+
"notification_subscription:manage",
33+
"notification_delivery_method:view",
34+
"notification_delivery_method:manage"
2435
],
2536
"releaseDownloads": [],
2637
"slug": "privateorgproject",

transcripts/share-apis/roles/run.zsh

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ fetch "$test_user" POST grant-project-contributor '/orgs/unison/roles' "
3636
]
3737
}"
3838

39+
# Now the user should be a member of the org
40+
fetch "$test_user" GET check-contributor-membership-addition '/orgs/unison/members'
41+
3942
fetch "$some_user" GET maintainer-project-view '/users/unison/projects/privateorgproject'
4043

4144
# Remove the user from the org again
@@ -49,3 +52,6 @@ fetch "$test_user" DELETE revoke-project-contributor '/orgs/unison/roles' "
4952
}"
5053

5154
fetch "$some_user" GET non-maintainer-project-view '/users/unison/projects/privateorgproject'
55+
56+
# Now the user should be no longer be a member of the org
57+
fetch "$test_user" GET check-contributor-membership-removal '/orgs/unison/members'

0 commit comments

Comments
 (0)