satellite/console: allow user to update project when limits are above paid defaults

When a user's bandwidth/storage limits are manually set to exceed the
paid tier defaults, attempting to update their project via the satellite
UI (e.g. to change the name/description) would result in an error.
This change modifies the limit checks for updating a project to remove
this issue.

https://github.com/storj/storj/issues/4892

Change-Id: I48853a3289b0ac51587f268a18c1b25743123fcf
This commit is contained in:
Lizzy Thomson 2022-07-07 20:01:08 -06:00
parent d72f9525d4
commit c237468ac9
2 changed files with 88 additions and 13 deletions

View File

@ -1550,7 +1550,7 @@ func (s *Service) GenDeleteProject(ctx context.Context, projectID uuid.UUID) (ht
} }
// UpdateProject is a method for updating project name and description by id. // UpdateProject is a method for updating project name and description by id.
func (s *Service) UpdateProject(ctx context.Context, projectID uuid.UUID, projectInfo ProjectInfo) (p *Project, err error) { func (s *Service) UpdateProject(ctx context.Context, projectID uuid.UUID, updatedProject ProjectInfo) (p *Project, err error) {
defer mon.Task()(&ctx)(&err) defer mon.Task()(&ctx)(&err)
user, err := s.getUserAndAuditLog(ctx, "update project name and description", zap.String("projectID", projectID.String())) user, err := s.getUserAndAuditLog(ctx, "update project name and description", zap.String("projectID", projectID.String()))
@ -1558,7 +1558,7 @@ func (s *Service) UpdateProject(ctx context.Context, projectID uuid.UUID, projec
return nil, Error.Wrap(err) return nil, Error.Wrap(err)
} }
err = ValidateNameAndDescription(projectInfo.Name, projectInfo.Description) err = ValidateNameAndDescription(updatedProject.Name, updatedProject.Description)
if err != nil { if err != nil {
return nil, Error.Wrap(err) return nil, Error.Wrap(err)
} }
@ -1568,8 +1568,8 @@ func (s *Service) UpdateProject(ctx context.Context, projectID uuid.UUID, projec
return nil, Error.Wrap(err) return nil, Error.Wrap(err)
} }
project := isMember.project project := isMember.project
project.Name = projectInfo.Name project.Name = updatedProject.Name
project.Description = projectInfo.Description project.Description = updatedProject.Description
if user.PaidTier { if user.PaidTier {
if project.BandwidthLimit != nil && *project.BandwidthLimit == 0 { if project.BandwidthLimit != nil && *project.BandwidthLimit == 0 {
@ -1578,15 +1578,15 @@ func (s *Service) UpdateProject(ctx context.Context, projectID uuid.UUID, projec
if project.StorageLimit != nil && *project.StorageLimit == 0 { if project.StorageLimit != nil && *project.StorageLimit == 0 {
return nil, Error.New("current storage limit for project is set to 0 (updating disabled)") return nil, Error.New("current storage limit for project is set to 0 (updating disabled)")
} }
if projectInfo.StorageLimit <= 0 || projectInfo.BandwidthLimit <= 0 { if updatedProject.StorageLimit <= 0 || updatedProject.BandwidthLimit <= 0 {
return nil, Error.New("project limits must be greater than 0") return nil, Error.New("project limits must be greater than 0")
} }
if projectInfo.StorageLimit > s.config.UsageLimits.Storage.Paid { if updatedProject.StorageLimit > s.config.UsageLimits.Storage.Paid && updatedProject.StorageLimit > *project.StorageLimit {
return nil, Error.New("specified storage limit exceeds allowed maximum for current tier") return nil, Error.New("specified storage limit exceeds allowed maximum for current tier")
} }
if projectInfo.BandwidthLimit > s.config.UsageLimits.Bandwidth.Paid { if updatedProject.BandwidthLimit > s.config.UsageLimits.Bandwidth.Paid && updatedProject.BandwidthLimit > *project.BandwidthLimit {
return nil, Error.New("specified bandwidth limit exceeds allowed maximum for current tier") return nil, Error.New("specified bandwidth limit exceeds allowed maximum for current tier")
} }
@ -1594,7 +1594,7 @@ func (s *Service) UpdateProject(ctx context.Context, projectID uuid.UUID, projec
if err != nil { if err != nil {
return nil, Error.Wrap(err) return nil, Error.Wrap(err)
} }
if projectInfo.StorageLimit.Int64() < storageUsed { if updatedProject.StorageLimit.Int64() < storageUsed {
return nil, Error.New("cannot set storage limit below current usage") return nil, Error.New("cannot set storage limit below current usage")
} }
@ -1602,14 +1602,14 @@ func (s *Service) UpdateProject(ctx context.Context, projectID uuid.UUID, projec
if err != nil { if err != nil {
return nil, Error.Wrap(err) return nil, Error.Wrap(err)
} }
if projectInfo.BandwidthLimit.Int64() < bandwidthUsed { if updatedProject.BandwidthLimit.Int64() < bandwidthUsed {
return nil, Error.New("cannot set bandwidth limit below current usage") return nil, Error.New("cannot set bandwidth limit below current usage")
} }
project.StorageLimit = new(memory.Size) project.StorageLimit = new(memory.Size)
*project.StorageLimit = projectInfo.StorageLimit *project.StorageLimit = updatedProject.StorageLimit
project.BandwidthLimit = new(memory.Size) project.BandwidthLimit = new(memory.Size)
*project.BandwidthLimit = projectInfo.BandwidthLimit *project.BandwidthLimit = updatedProject.BandwidthLimit
} }
err = s.store.Projects().Update(ctx, project) err = s.store.Projects().Update(ctx, project)
@ -1672,14 +1672,14 @@ func (s *Service) GenUpdateProject(ctx context.Context, projectID uuid.UUID, pro
} }
} }
if projectInfo.StorageLimit > s.config.UsageLimits.Storage.Paid { if projectInfo.StorageLimit > s.config.UsageLimits.Storage.Paid && projectInfo.StorageLimit > *project.StorageLimit {
return nil, api.HTTPError{ return nil, api.HTTPError{
Status: http.StatusBadRequest, Status: http.StatusBadRequest,
Err: Error.New("specified storage limit exceeds allowed maximum for current tier"), Err: Error.New("specified storage limit exceeds allowed maximum for current tier"),
} }
} }
if projectInfo.BandwidthLimit > s.config.UsageLimits.Bandwidth.Paid { if projectInfo.BandwidthLimit > s.config.UsageLimits.Bandwidth.Paid && projectInfo.BandwidthLimit > *project.BandwidthLimit {
return nil, api.HTTPError{ return nil, api.HTTPError{
Status: http.StatusBadRequest, Status: http.StatusBadRequest,
Err: Error.New("specified bandwidth limit exceeds allowed maximum for current tier"), Err: Error.New("specified bandwidth limit exceeds allowed maximum for current tier"),

View File

@ -382,6 +382,81 @@ func TestPaidTier(t *testing.T) {
}) })
} }
// TestUpdateProjectExceedsLimits ensures that a project with limits manually set above the defaults can be updated.
func TestUpdateProjectExceedsLimits(t *testing.T) {
usageConfig := console.UsageLimitsConfig{
Storage: console.StorageLimitConfig{
Free: memory.GB,
Paid: memory.TB,
},
Bandwidth: console.BandwidthLimitConfig{
Free: 2 * memory.GB,
Paid: 2 * memory.TB,
},
Segment: console.SegmentLimitConfig{
Free: 10,
Paid: 50,
},
Project: console.ProjectLimitConfig{
Free: 1,
Paid: 3,
},
}
testplanet.Run(t, testplanet.Config{
SatelliteCount: 1, StorageNodeCount: 0, UplinkCount: 1,
Reconfigure: testplanet.Reconfigure{
Satellite: func(log *zap.Logger, index int, config *satellite.Config) {
config.Console.UsageLimits = usageConfig
},
},
}, func(t *testing.T, ctx *testcontext.Context, planet *testplanet.Planet) {
sat := planet.Satellites[0]
service := sat.API.Console.Service
projectID := planet.Uplinks[0].Projects[0].ID
updatedName := "newName"
updatedDescription := "newDescription"
updatedStorageLimit := memory.Size(100) + memory.TB
updatedBandwidthLimit := memory.Size(100) + memory.TB
proj, err := sat.API.DB.Console().Projects().Get(ctx, projectID)
require.NoError(t, err)
userCtx1, err := sat.UserContext(ctx, proj.OwnerID)
require.NoError(t, err)
// project should have free tier usage limits
require.Equal(t, usageConfig.Storage.Free, *proj.StorageLimit)
require.Equal(t, usageConfig.Bandwidth.Free, *proj.BandwidthLimit)
require.Equal(t, usageConfig.Segment.Free, *proj.SegmentLimit)
// update project name should succeed
_, err = service.UpdateProject(userCtx1, projectID, console.ProjectInfo{
Name: updatedName,
Description: updatedDescription,
})
require.NoError(t, err)
// manually set project limits above defaults
proj1, err := sat.API.DB.Console().Projects().Get(ctx, projectID)
require.NoError(t, err)
proj1.StorageLimit = new(memory.Size)
*proj1.StorageLimit = updatedStorageLimit
proj1.BandwidthLimit = new(memory.Size)
*proj1.BandwidthLimit = updatedBandwidthLimit
err = sat.DB.Console().Projects().Update(ctx, proj1)
require.NoError(t, err)
// try to update project name should succeed
_, err = service.UpdateProject(userCtx1, projectID, console.ProjectInfo{
Name: "updatedName",
Description: "updatedDescription",
})
require.NoError(t, err)
})
}
func TestMFA(t *testing.T) { func TestMFA(t *testing.T) {
testplanet.Run(t, testplanet.Config{ testplanet.Run(t, testplanet.Config{
SatelliteCount: 1, StorageNodeCount: 0, UplinkCount: 0, SatelliteCount: 1, StorageNodeCount: 0, UplinkCount: 0,