Merge pull request #2169 from coreos-inc/multi-invite

Change team invitation acceptance to join all invited teams under the org
This commit is contained in:
josephschorr 2016-11-28 19:00:23 -05:00 committed by GitHub
commit b24b8629e5
3 changed files with 119 additions and 12 deletions

View file

@ -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)

View file

@ -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()
}

View file

@ -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):