{satellite/console,web/satellite}: show error for project invite dupes

This change fixes an issue where a new project member invitation would
silently replace an older one that has the same project ID and email if
the email did not belong to a registered user. Additionally, the
satellite frontend has been updated to display more descriptive error
messages for project member invitations.

Change-Id: I32b582c40c0028b8eedf2aed4b5bfb43501594b4
This commit is contained in:
Jeremy Wharton 2023-07-10 01:24:29 -05:00 committed by Storj Robot
parent fbda13c752
commit c79d1b0d2f
3 changed files with 15 additions and 16 deletions

View File

@ -75,7 +75,7 @@ const (
projInviteInvalidErrMsg = "The invitation has expired or is invalid"
projInviteAlreadyMemberErrMsg = "You are already a member of the project"
projInviteResponseInvalidErrMsg = "Invalid project member invitation response"
projInviteActiveErrMsg = "The invitation for '%s' has not expired yet"
projInviteExistsErrMsg = "An active invitation for '%s' already exists"
)
var (
@ -143,8 +143,8 @@ var (
// or has expired.
ErrProjectInviteInvalid = errs.Class("invalid project invitation")
// ErrProjectInviteActive occurs when trying to reinvite a user whose invitation hasn't expired yet.
ErrProjectInviteActive = errs.Class("project invitation active")
// ErrAlreadyInvited occurs when trying to invite a user who already has an unexpired invitation.
ErrAlreadyInvited = errs.Class("user is already invited")
)
// Service is handling accounts related logic.
@ -3570,7 +3570,6 @@ func (s *Service) RespondToProjectInvitation(ctx context.Context, projectID uuid
// InviteProjectMembers invites users by email to given project.
// If an invitation already exists and has expired, it will be replaced and the user will be sent a new email.
// Email addresses not belonging to a user are ignored.
// projectID here may be project.PublicID or project.ID.
func (s *Service) InviteProjectMembers(ctx context.Context, projectID uuid.UUID, emails []string) (invites []ProjectInvitation, err error) {
defer mon.Task()(&ctx)(&err)
@ -3588,6 +3587,14 @@ func (s *Service) InviteProjectMembers(ctx context.Context, projectID uuid.UUID,
var users []*User
var newUserEmails []string
for _, email := range emails {
invite, err := s.store.ProjectInvitations().Get(ctx, projectID, email)
if err != nil && !errs.Is(err, sql.ErrNoRows) {
return nil, Error.Wrap(err)
}
if invite != nil && !s.IsProjectInvitationExpired(invite) {
return nil, ErrAlreadyInvited.New(projInviteExistsErrMsg, email)
}
invitedUser, err := s.store.Users().GetByEmail(ctx, email)
if err == nil {
_, err = s.isProjectMember(ctx, invitedUser.ID, projectID)
@ -3596,14 +3603,6 @@ func (s *Service) InviteProjectMembers(ctx context.Context, projectID uuid.UUID,
} else if err == nil {
return nil, ErrAlreadyMember.New("%s is already a member", email)
}
invite, err := s.store.ProjectInvitations().Get(ctx, projectID, email)
if err != nil && !errs.Is(err, sql.ErrNoRows) {
return nil, Error.Wrap(err)
}
if invite != nil && !s.IsProjectInvitationExpired(invite) {
return nil, ErrProjectInviteActive.New(projInviteActiveErrMsg, invitedUser.Email)
}
users = append(users, invitedUser)
} else if errs.Is(err, sql.ErrNoRows) {
newUserEmails = append(newUserEmails, email)

View File

@ -2064,7 +2064,7 @@ func TestProjectInvitations(t *testing.T) {
// resending an active invitation should fail.
invites, err = service.InviteProjectMembers(ctx2, project.ID, []string{user3.Email})
require.True(t, console.ErrProjectInviteActive.Has(err))
require.True(t, console.ErrAlreadyInvited.Has(err))
require.Empty(t, invites)
// expire the invitation.

View File

@ -188,7 +188,7 @@ async function onAddUsersClick(): Promise<void> {
}
if (emailArray.includes(usersStore.state.user.email)) {
await notify.error(`Error during adding project members. You can't add yourself to the project`, AnalyticsErrorEventSource.ADD_PROJECT_MEMBER_MODAL);
await notify.error(`Error adding project members. You can't add yourself to the project`, AnalyticsErrorEventSource.ADD_PROJECT_MEMBER_MODAL);
isLoading.value = false;
return;
@ -196,8 +196,8 @@ async function onAddUsersClick(): Promise<void> {
try {
await pmStore.inviteMembers(emailArray, projectsStore.state.selectedProject.id);
} catch (_) {
await notify.error(`Error during adding project members.`, AnalyticsErrorEventSource.ADD_PROJECT_MEMBER_MODAL);
} catch (error) {
await notify.error(`Error adding project members. ${error.message}`, AnalyticsErrorEventSource.ADD_PROJECT_MEMBER_MODAL);
isLoading.value = false;
return;