Change team invitation acceptance to join all invited teams under the org
Fixes #1989
This commit is contained in:
parent
1c3012a538
commit
402ad25690
3 changed files with 119 additions and 12 deletions
|
@ -312,10 +312,24 @@ def find_matching_team_invite(code, user_obj):
|
|||
return found
|
||||
|
||||
|
||||
def find_organization_invites(organization, user_obj):
|
||||
""" Finds all organization team invites for the given user under the given organization. """
|
||||
invite_check = (TeamMemberInvite.user == user_obj)
|
||||
if user_obj.verified:
|
||||
invite_check = invite_check | (TeamMemberInvite.email == user_obj.email)
|
||||
|
||||
query = (TeamMemberInvite
|
||||
.select()
|
||||
.join(Team)
|
||||
.where(invite_check, Team.organization == organization))
|
||||
return query
|
||||
|
||||
|
||||
def confirm_team_invite(code, user_obj):
|
||||
""" Confirms the given team invite code for the given user by adding the user to the team
|
||||
and deleting the code. Raises a DataModelException if the code was not found or does
|
||||
not apply to the given user. """
|
||||
not apply to the given user. If the user is invited to two or more teams under the
|
||||
same organization, they are automatically confirmed for all of them. """
|
||||
found = find_matching_team_invite(code, user_obj)
|
||||
|
||||
# If the invite is for a specific user, we have to confirm that here.
|
||||
|
@ -324,15 +338,18 @@ def confirm_team_invite(code, user_obj):
|
|||
Please login to that account and try again.""" % found.user.username
|
||||
raise DataModelException(message)
|
||||
|
||||
# Add the user to the team.
|
||||
try:
|
||||
add_user_to_team(user_obj, found.team)
|
||||
except UserAlreadyInTeam:
|
||||
# Ignore.
|
||||
pass
|
||||
# Find all matching invitations for the user under the organization.
|
||||
for invite in find_organization_invites(found.team.organization, user_obj):
|
||||
# Add the user to the team.
|
||||
try:
|
||||
add_user_to_team(user_obj, invite.team)
|
||||
except UserAlreadyInTeam:
|
||||
# Ignore.
|
||||
pass
|
||||
|
||||
# Delete the invite and return the team.
|
||||
invite.delete_instance()
|
||||
|
||||
# Delete the invite and return the team.
|
||||
team = found.team
|
||||
inviter = found.inviter
|
||||
found.delete_instance()
|
||||
return (team, inviter)
|
||||
|
|
|
@ -28,7 +28,8 @@ def permission_view(permission):
|
|||
def try_accept_invite(code, user):
|
||||
(team, inviter) = model.team.confirm_team_invite(code, user)
|
||||
|
||||
model.notification.delete_matching_notifications(user, 'org_team_invite', code=code)
|
||||
model.notification.delete_matching_notifications(user, 'org_team_invite',
|
||||
org=team.organization.username)
|
||||
|
||||
orgname = team.organization.username
|
||||
log_action('org_team_member_invite_accepted', orgname, {
|
||||
|
@ -208,6 +209,7 @@ class TeamMemberList(ApiResource):
|
|||
invites = model.team.get_organization_team_member_invites(team.id)
|
||||
|
||||
data = {
|
||||
'name': teamname,
|
||||
'members': [member_view(m) for m in members] + [invite_view(i) for i in invites],
|
||||
'can_edit': edit_permission.can()
|
||||
}
|
||||
|
|
|
@ -227,14 +227,14 @@ class ApiTestCase(unittest.TestCase):
|
|||
def assertNotInTeam(self, data, membername):
|
||||
for memberData in data['members']:
|
||||
if memberData['name'] == membername:
|
||||
self.fail(membername + ' found in team: ' + json.dumps(data))
|
||||
self.fail(membername + ' found in team: ' + data['name'])
|
||||
|
||||
def assertInTeam(self, data, membername):
|
||||
for member_data in data['members']:
|
||||
if member_data['name'] == membername:
|
||||
return
|
||||
|
||||
self.fail(membername + ' not found in team: ' + py_json.dumps(data))
|
||||
self.fail(membername + ' not found in team: ' + data['name'])
|
||||
|
||||
def login(self, username, password='password'):
|
||||
return self.postJsonResponse(Signin, data=dict(username=username, password=password))
|
||||
|
@ -682,6 +682,94 @@ class TestCreateNewUser(ApiTestCase):
|
|||
teamname='owners'))
|
||||
self.assertNotInTeam(json, NEW_USER_DETAILS['username'])
|
||||
|
||||
def test_createuser_withmultipleteaminvites(self):
|
||||
inviter = model.user.get_user(ADMIN_ACCESS_USER)
|
||||
owners_team = model.team.get_organization_team(ORGANIZATION, 'owners')
|
||||
readers_team = model.team.get_organization_team(ORGANIZATION, 'readers')
|
||||
other_owners_team = model.team.get_organization_team('library', 'owners')
|
||||
|
||||
owners_invite = model.team.add_or_invite_to_team(inviter, owners_team, None,
|
||||
NEW_USER_DETAILS['email'])
|
||||
|
||||
readers_invite = model.team.add_or_invite_to_team(inviter, readers_team, None,
|
||||
NEW_USER_DETAILS['email'])
|
||||
|
||||
other_owners_invite = model.team.add_or_invite_to_team(inviter, other_owners_team, None,
|
||||
NEW_USER_DETAILS['email'])
|
||||
|
||||
# Create the user and ensure they have a verified email address.
|
||||
details = {
|
||||
'invite_code': owners_invite.invite_token
|
||||
}
|
||||
details.update(NEW_USER_DETAILS)
|
||||
|
||||
data = self.postJsonResponse(User, data=details, expected_code=200)
|
||||
|
||||
# Make sure the user is verified since the email address of the user matches
|
||||
# that of the team invite.
|
||||
self.assertFalse('awaiting_verification' in data)
|
||||
|
||||
# Make sure the user was not (yet) added to the teams.
|
||||
self.login(ADMIN_ACCESS_USER)
|
||||
json = self.getJsonResponse(TeamMemberList,
|
||||
params=dict(orgname=ORGANIZATION,
|
||||
teamname='owners'))
|
||||
self.assertNotInTeam(json, NEW_USER_DETAILS['username'])
|
||||
|
||||
json = self.getJsonResponse(TeamMemberList,
|
||||
params=dict(orgname=ORGANIZATION,
|
||||
teamname='readers'))
|
||||
self.assertNotInTeam(json, NEW_USER_DETAILS['username'])
|
||||
|
||||
json = self.getJsonResponse(TeamMemberList,
|
||||
params=dict(orgname='library',
|
||||
teamname='owners'))
|
||||
self.assertNotInTeam(json, NEW_USER_DETAILS['username'])
|
||||
|
||||
# Accept the first invitation.
|
||||
self.login(NEW_USER_DETAILS['username'])
|
||||
self.putJsonResponse(TeamMemberInvite, params=dict(code=owners_invite.invite_token))
|
||||
|
||||
# Make sure both codes are now invalid.
|
||||
self.putResponse(TeamMemberInvite, params=dict(code=owners_invite.invite_token),
|
||||
expected_code=400)
|
||||
|
||||
self.putResponse(TeamMemberInvite, params=dict(code=readers_invite.invite_token),
|
||||
expected_code=400)
|
||||
|
||||
# Make sure the user is now in the two invited teams under the organization, but not
|
||||
# in the other org's team.
|
||||
self.login(ADMIN_ACCESS_USER)
|
||||
json = self.getJsonResponse(TeamMemberList,
|
||||
params=dict(orgname=ORGANIZATION,
|
||||
teamname='owners'))
|
||||
self.assertInTeam(json, NEW_USER_DETAILS['username'])
|
||||
|
||||
json = self.getJsonResponse(TeamMemberList,
|
||||
params=dict(orgname=ORGANIZATION,
|
||||
teamname='readers'))
|
||||
self.assertInTeam(json, NEW_USER_DETAILS['username'])
|
||||
|
||||
json = self.getJsonResponse(TeamMemberList,
|
||||
params=dict(orgname='library',
|
||||
teamname='owners'))
|
||||
self.assertNotInTeam(json, NEW_USER_DETAILS['username'])
|
||||
|
||||
# Accept the second invitation.
|
||||
self.login(NEW_USER_DETAILS['username'])
|
||||
self.putJsonResponse(TeamMemberInvite, params=dict(code=other_owners_invite.invite_token))
|
||||
|
||||
# Make sure the user was added to the other organization.
|
||||
self.login(ADMIN_ACCESS_USER)
|
||||
json = self.getJsonResponse(TeamMemberList,
|
||||
params=dict(orgname='library',
|
||||
teamname='owners'))
|
||||
self.assertInTeam(json, NEW_USER_DETAILS['username'])
|
||||
|
||||
# Make sure the invitation codes are now invalid.
|
||||
self.putResponse(TeamMemberInvite, params=dict(code=other_owners_invite.invite_token),
|
||||
expected_code=400)
|
||||
|
||||
|
||||
class TestDeleteNamespace(ApiTestCase):
|
||||
def test_deletenamespaces(self):
|
||||
|
|
Reference in a new issue