From 2cf4784b2026fed468ee8d3e552db5595a825625 Mon Sep 17 00:00:00 2001 From: Jeremy Wharton Date: Mon, 16 Oct 2023 23:43:39 -0500 Subject: [PATCH] satellite/console: make project invites exclusive to paid tier This change makes the project member invitation feature exclusive to the paid tier. Change-Id: I13c967c8381d49b2d131e15799ad48487b0f6c74 --- .../console/consoleweb/consoleapi/projects.go | 16 +++++++++------ satellite/console/consoleweb/server_test.go | 4 ++++ satellite/console/service.go | 5 +++++ satellite/console/service_test.go | 20 ++++++++++++++++++- 4 files changed, 38 insertions(+), 7 deletions(-) diff --git a/satellite/console/consoleweb/consoleapi/projects.go b/satellite/console/consoleweb/consoleapi/projects.go index a3a58fd68..21933a6a5 100644 --- a/satellite/console/consoleweb/consoleapi/projects.go +++ b/satellite/console/consoleweb/consoleapi/projects.go @@ -473,11 +473,13 @@ func (p *Projects) InviteUser(w http.ResponseWriter, r *http.Request) { _, err = p.service.InviteNewProjectMember(ctx, id, email) if err != nil { + status := http.StatusInternalServerError if console.ErrUnauthorized.Has(err) || console.ErrNoMembership.Has(err) { - p.serveJSONError(ctx, w, http.StatusUnauthorized, err) - return + status = http.StatusUnauthorized + } else if console.ErrNotPaidTier.Has(err) { + status = http.StatusPaymentRequired } - p.serveJSONError(ctx, w, http.StatusInternalServerError, err) + p.serveJSONError(ctx, w, status, err) } } @@ -513,11 +515,13 @@ func (p *Projects) ReinviteUsers(w http.ResponseWriter, r *http.Request) { _, err = p.service.ReinviteProjectMembers(ctx, id, data.Emails) if err != nil { + status := http.StatusInternalServerError if console.ErrUnauthorized.Has(err) || console.ErrNoMembership.Has(err) { - p.serveJSONError(ctx, w, http.StatusUnauthorized, err) - return + status = http.StatusUnauthorized + } else if console.ErrNotPaidTier.Has(err) { + status = http.StatusPaymentRequired } - p.serveJSONError(ctx, w, http.StatusInternalServerError, err) + p.serveJSONError(ctx, w, status, err) } } diff --git a/satellite/console/consoleweb/server_test.go b/satellite/console/consoleweb/server_test.go index 7bbcaf111..3059b752e 100644 --- a/satellite/console/consoleweb/server_test.go +++ b/satellite/console/consoleweb/server_test.go @@ -94,6 +94,10 @@ func TestInvitedRouting(t *testing.T) { }, 1) require.NoError(t, err) + paid := true + err = sat.DB.Console().Users().Update(ctx, owner.ID, console.UpdateUserRequest{PaidTier: &paid}) + require.NoError(t, err) + project, err := sat.AddProject(ctx, owner.ID, "Test Project") require.NoError(t, err) diff --git a/satellite/console/service.go b/satellite/console/service.go index 5cac98cc5..bae0249b2 100644 --- a/satellite/console/service.go +++ b/satellite/console/service.go @@ -81,6 +81,7 @@ const ( projInviteExistsErrMsg = "An invitation for '%s' already exists" projInviteDoesntExistErrMsg = "An invitation for '%s' does not exist" newInviteLimitErrMsg = "Only one new invitation can be sent at a time" + paidTierInviteErrMsg = "Only paid tier users can invite project members" ) var ( @@ -3774,6 +3775,10 @@ func (s *Service) inviteProjectMembers(ctx context.Context, sender *User, projec } projectID = isMember.project.ID + if !sender.PaidTier { + return nil, ErrNotPaidTier.New(paidTierInviteErrMsg) + } + var users []*User var newUserEmails []string var unverifiedUsers []User diff --git a/satellite/console/service_test.go b/satellite/console/service_test.go index a2aea9055..21b811961 100644 --- a/satellite/console/service_test.go +++ b/satellite/console/service_test.go @@ -2166,6 +2166,15 @@ func TestProjectInvitations(t *testing.T) { return invite } + upgradeToPaidTier := func(t *testing.T, ctx context.Context, user *console.User) context.Context { + paid := true + err := sat.DB.Console().Users().Update(ctx, user.ID, console.UpdateUserRequest{PaidTier: &paid}) + require.NoError(t, err) + ctx, err = sat.UserContext(ctx, user.ID) + require.NoError(t, err) + return ctx + } + setInviteDate := func(t *testing.T, ctx context.Context, invite *console.ProjectInvitation, createdAt time.Time) { result, err := sat.DB.Testing().RawDB().ExecContext(ctx, "UPDATE project_invitations SET created_at = $1 WHERE project_id = $2 AND email = $3", @@ -2189,15 +2198,23 @@ func TestProjectInvitations(t *testing.T) { project, err := sat.AddProject(ctx, user.ID, "Test Project") require.NoError(t, err) + // inviting without being a paid tier user should fail. + invite, err := service.InviteNewProjectMember(ctx, project.ID, user2.Email) + require.True(t, console.ErrNotPaidTier.Has(err)) + require.Nil(t, invite) + + ctx = upgradeToPaidTier(t, ctx, user) + // expect reinvitation to fail due to lack of preexisting invitation record. invites, err := service.ReinviteProjectMembers(ctx, project.ID, []string{user2.Email}) require.True(t, console.ErrProjectInviteInvalid.Has(err)) require.Empty(t, invites) - invite, err := service.InviteNewProjectMember(ctx, project.ID, user2.Email) + invite, err = service.InviteNewProjectMember(ctx, project.ID, user2.Email) require.NoError(t, err) require.NotNil(t, invite) + // inviting while being a paid tier user should succeed. invites, err = service.GetUserProjectInvitations(ctx2) require.NoError(t, err) require.Len(t, invites, 1) @@ -2208,6 +2225,7 @@ func TestProjectInvitations(t *testing.T) { // prevent unauthorized users from inviting others (user2 is not a member of the project yet). const testEmail = "other@mail.com" + ctx2 = upgradeToPaidTier(t, ctx2, user2) _, err = service.InviteNewProjectMember(ctx2, project.ID, testEmail) require.Error(t, err) require.True(t, console.ErrNoMembership.Has(err))