Fix attempts to confirm team invite for mismatched email address
Currently, if a user tries to confirm an invite sent to them on an account with a mismatching email address, we simply redirect to the org (where they get a 403). This change ensures they get the proper error response message, and restyles the error page to be nicer. Fixes #2227 Fixes https://www.pivotaltracker.com/story/show/136088507
This commit is contained in:
parent
2730c26b2e
commit
785c74de52
3 changed files with 118 additions and 10 deletions
|
@ -270,6 +270,10 @@ def delete_team_user_invite(team, user_obj):
|
|||
return True
|
||||
|
||||
|
||||
def lookup_team_invites_by_email(email):
|
||||
return TeamMemberInvite.select().where(TeamMemberInvite.email == email)
|
||||
|
||||
|
||||
def lookup_team_invites(user_obj):
|
||||
return TeamMemberInvite.select().where(TeamMemberInvite.user == user_obj)
|
||||
|
||||
|
@ -332,16 +336,12 @@ def confirm_team_invite(code, user_obj):
|
|||
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.
|
||||
if found.user is not None and found.user != user_obj:
|
||||
message = """This invite is intended for user "%s".
|
||||
Please login to that account and try again.""" % found.user.username
|
||||
raise DataModelException(message)
|
||||
|
||||
# Find all matching invitations for the user under the organization.
|
||||
code_found = False
|
||||
for invite in find_organization_invites(found.team.organization, user_obj):
|
||||
# Add the user to the team.
|
||||
try:
|
||||
code_found = True
|
||||
add_user_to_team(user_obj, invite.team)
|
||||
except UserAlreadyInTeam:
|
||||
# Ignore.
|
||||
|
@ -350,6 +350,16 @@ def confirm_team_invite(code, user_obj):
|
|||
# Delete the invite and return the team.
|
||||
invite.delete_instance()
|
||||
|
||||
if not code_found:
|
||||
if found.user:
|
||||
message = """This invite is intended for user "%s".
|
||||
Please login to that account and try again.""" % found.user.username
|
||||
raise DataModelException(message)
|
||||
else:
|
||||
message = """This invite is intended for email "%s".
|
||||
Please login to that account and try again.""" % found.email
|
||||
raise DataModelException(message)
|
||||
|
||||
team = found.team
|
||||
inviter = found.inviter
|
||||
return (team, inviter)
|
||||
|
|
|
@ -5,9 +5,12 @@
|
|||
<div class="user-setup" ng-show="user.anonymous" redirect-url="redirectUrl"
|
||||
invite-code="inviteCode">
|
||||
</div>
|
||||
<div class="quay-spinner" ng-show="!user.anonymous && loading"></div>
|
||||
<div class="alert alert-danger" ng-show="!user.anonymous && invalid">
|
||||
{{ invalid }}
|
||||
<div class="cor-loader-inline" ng-show="!user.anonymous && loading"></div>
|
||||
|
||||
<div class="error-view-element" ng-show="!user.anonymous && invalid">
|
||||
<h2><i class="fa fa-exclamation-triangle"></i> Confirmation Error</h2>
|
||||
<h3>Confirmation code does not match this account</h3>
|
||||
<div>{{ invalid }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -30,7 +30,7 @@ from data.database import RepositoryActionCount, Repository as RepositoryTable
|
|||
from test.helpers import assert_action_logged
|
||||
|
||||
from endpoints.api.team import (TeamMember, TeamMemberList, TeamMemberInvite, OrganizationTeam,
|
||||
TeamPermissions)
|
||||
TeamPermissions, InviteTeamMember)
|
||||
from endpoints.api.tag import RepositoryTagImages, RepositoryTag, RevertTag, ListRepositoryTags
|
||||
from endpoints.api.search import EntitySearch, ConductSearch
|
||||
from endpoints.api.image import RepositoryImage, RepositoryImageList
|
||||
|
@ -1556,7 +1556,102 @@ class TestAcceptTeamMemberInvite(ApiTestCase):
|
|||
params=dict(code=invites[0].invite_token),
|
||||
expected_code=400)
|
||||
|
||||
def test_accept_via_email(self):
|
||||
self.login(ADMIN_ACCESS_USER)
|
||||
|
||||
# Create the invite.
|
||||
member = model.user.get_user(NO_ACCESS_USER)
|
||||
response = self.putJsonResponse(InviteTeamMember,
|
||||
params=dict(orgname=ORGANIZATION, teamname='owners',
|
||||
email=member.email))
|
||||
|
||||
self.assertEquals(True, response['invited'])
|
||||
|
||||
# Login as the user.
|
||||
self.login(member.username)
|
||||
|
||||
# Accept the invite.
|
||||
invites = list(model.team.lookup_team_invites_by_email(member.email))
|
||||
self.assertEquals(1, len(invites))
|
||||
|
||||
self.putJsonResponse(TeamMemberInvite, params=dict(code=invites[0].invite_token))
|
||||
|
||||
# Verify the user is now on the team.
|
||||
json = self.getJsonResponse(TeamMemberList,
|
||||
params=dict(orgname=ORGANIZATION,
|
||||
teamname='owners'))
|
||||
|
||||
self.assertInTeam(json, member.username)
|
||||
|
||||
# Verify the accept now fails.
|
||||
self.putResponse(TeamMemberInvite,
|
||||
params=dict(code=invites[0].invite_token),
|
||||
expected_code=400)
|
||||
|
||||
|
||||
def test_accept_invite_different_user(self):
|
||||
self.login(ADMIN_ACCESS_USER)
|
||||
|
||||
# Create the invite.
|
||||
response = self.putJsonResponse(TeamMember,
|
||||
params=dict(orgname=ORGANIZATION, teamname='owners',
|
||||
membername=NO_ACCESS_USER))
|
||||
|
||||
self.assertEquals(True, response['invited'])
|
||||
|
||||
# Login as a different user.
|
||||
self.login(PUBLIC_USER)
|
||||
|
||||
# Try to accept the invite.
|
||||
user = model.user.get_user(NO_ACCESS_USER)
|
||||
invites = list(model.team.lookup_team_invites(user))
|
||||
self.assertEquals(1, len(invites))
|
||||
|
||||
self.putResponse(TeamMemberInvite, params=dict(code=invites[0].invite_token),
|
||||
expected_code=400)
|
||||
|
||||
# Ensure the invite is still valid.
|
||||
user = model.user.get_user(NO_ACCESS_USER)
|
||||
invites = list(model.team.lookup_team_invites(user))
|
||||
self.assertEquals(1, len(invites))
|
||||
|
||||
# Ensure the user is *not* a member of the team.
|
||||
self.login(ADMIN_ACCESS_USER)
|
||||
json = self.getJsonResponse(TeamMemberList,
|
||||
params=dict(orgname=ORGANIZATION,
|
||||
teamname='owners'))
|
||||
self.assertNotInTeam(json, PUBLIC_USER)
|
||||
|
||||
def test_accept_invite_different_email(self):
|
||||
self.login(ADMIN_ACCESS_USER)
|
||||
|
||||
# Create the invite.
|
||||
response = self.putJsonResponse(InviteTeamMember,
|
||||
params=dict(orgname=ORGANIZATION, teamname='owners',
|
||||
email='someemail@example.com'))
|
||||
|
||||
self.assertEquals(True, response['invited'])
|
||||
|
||||
# Login as a different user.
|
||||
self.login(PUBLIC_USER)
|
||||
|
||||
# Try to accept the invite.
|
||||
invites = list(model.team.lookup_team_invites_by_email('someemail@example.com'))
|
||||
self.assertEquals(1, len(invites))
|
||||
|
||||
self.putResponse(TeamMemberInvite, params=dict(code=invites[0].invite_token),
|
||||
expected_code=400)
|
||||
|
||||
# Ensure the invite is still valid.
|
||||
invites = list(model.team.lookup_team_invites_by_email('someemail@example.com'))
|
||||
self.assertEquals(1, len(invites))
|
||||
|
||||
# Ensure the user is *not* a member of the team.
|
||||
self.login(ADMIN_ACCESS_USER)
|
||||
json = self.getJsonResponse(TeamMemberList,
|
||||
params=dict(orgname=ORGANIZATION,
|
||||
teamname='owners'))
|
||||
self.assertNotInTeam(json, PUBLIC_USER)
|
||||
|
||||
class TestDeclineTeamMemberInvite(ApiTestCase):
|
||||
def test_decline_wronguser(self):
|
||||
|
|
Reference in a new issue