diff --git a/services/storage-users/pkg/config/config.go b/services/storage-users/pkg/config/config.go index 5423259a0f..88e326f42c 100644 --- a/services/storage-users/pkg/config/config.go +++ b/services/storage-users/pkg/config/config.go @@ -38,6 +38,7 @@ type Config struct { ReadOnly bool `yaml:"readonly" env:"STORAGE_USERS_READ_ONLY" desc:"Set this storage to be read-only."` UploadExpiration int64 `yaml:"upload_expiration" env:"STORAGE_USERS_UPLOAD_EXPIRATION" desc:"Duration in seconds after which uploads will expire. Note that when setting this to a low number, uploads could be cancelled before they are finished and return a 403 to the user."` Tasks Tasks `yaml:"tasks"` + ServiceAccount ServiceAccount `yaml:"service_account"` Supervised bool `yaml:"-"` Context context.Context `yaml:"-"` @@ -278,3 +279,9 @@ type PurgeTrashBin struct { PersonalDeleteBefore time.Duration `yaml:"personal_delete_before" env:"STORAGE_USERS_PURGE_TRASH_BIN_PERSONAL_DELETE_BEFORE" desc:"Specifies the period of time in which items that have been in the personal trash-bin for longer than this value should be deleted. A value of 0 means no automatic deletion. The value is human-readable, valid values are '24h', '60m', '60s' etc."` ProjectDeleteBefore time.Duration `yaml:"project_delete_before" env:"STORAGE_USERS_PURGE_TRASH_BIN_PROJECT_DELETE_BEFORE" desc:"Specifies the period of time in which items that have been in the project trash-bin for longer than this value should be deleted. A value of 0 means no automatic deletion. The value is human-readable, valid values are '24h', '60m', '60s' etc."` } + +// ServiceAccount is the configuration for the used service account +type ServiceAccount struct { + ServiceAccountID string `yaml:"service_account_id" env:"OCIS_SERVICE_ACCOUNT_ID;STORAGE_USERS_SERVICE_ACCOUNT_ID" desc:"The ID of the service account the service should use. See the 'auth-service' service description for more details."` + ServiceAccountSecret string `yaml:"service_account_secret" env:"OCIS_SERVICE_ACCOUNT_SECRET;STORAGE_USERS_SERVICE_ACCOUNT_SECRET" desc:"The service account secret."` +} diff --git a/services/storage-users/pkg/config/defaults/defaultconfig.go b/services/storage-users/pkg/config/defaults/defaultconfig.go index 9cb71e4f26..5f4009bbc2 100644 --- a/services/storage-users/pkg/config/defaults/defaultconfig.go +++ b/services/storage-users/pkg/config/defaults/defaultconfig.go @@ -108,6 +108,10 @@ func DefaultConfig() *config.Config { PersonalDeleteBefore: 30 * 24 * time.Hour, }, }, + ServiceAccount: config.ServiceAccount{ + ServiceAccountID: "service-user-id", + ServiceAccountSecret: "secret-string", + }, } } diff --git a/services/storage-users/pkg/event/service.go b/services/storage-users/pkg/event/service.go index 91384f75d5..cd2e9b9673 100644 --- a/services/storage-users/pkg/event/service.go +++ b/services/storage-users/pkg/event/service.go @@ -4,7 +4,6 @@ import ( "time" apiGateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1" - apiUser "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" "github.com/cs3org/reva/v2/pkg/events" "github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool" "github.com/owncloud/ocis/v2/ocis-pkg/log" @@ -53,11 +52,6 @@ func (s Service) Run() error { executionTime = time.Now() } - executantID := ev.ExecutantID - if executantID == nil { - executantID = &apiUser.UserId{OpaqueId: s.config.Tasks.PurgeTrashBin.UserID} - } - tasks := map[task.SpaceType]time.Time{ task.Project: executionTime.Add(-s.config.Tasks.PurgeTrashBin.ProjectDeleteBefore), task.Personal: executionTime.Add(-s.config.Tasks.PurgeTrashBin.PersonalDeleteBefore), @@ -70,7 +64,7 @@ func (s Service) Run() error { continue } - if err = task.PurgeTrashBin(executantID, deleteBefore, spaceType, s.gatewaySelector, s.config.Commons.MachineAuthAPIKey); err != nil { + if err = task.PurgeTrashBin(s.config.ServiceAccount.ServiceAccountID, deleteBefore, spaceType, s.gatewaySelector, s.config.ServiceAccount.ServiceAccountSecret); err != nil { errs = append(errs, err) } } diff --git a/services/storage-users/pkg/task/trash_bin.go b/services/storage-users/pkg/task/trash_bin.go index daf49fad1e..e7bb2cb63d 100644 --- a/services/storage-users/pkg/task/trash_bin.go +++ b/services/storage-users/pkg/task/trash_bin.go @@ -1,11 +1,9 @@ package task import ( - "fmt" "time" apiGateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1" - apiUser "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" apiRpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1" apiProvider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" "github.com/cs3org/reva/v2/pkg/errtypes" @@ -17,18 +15,18 @@ import ( // the provided executantID must have space access. // removeBefore specifies how long an item must be in the trash-bin to be deleted, // items that stay there for a shorter time are ignored and kept in place. -func PurgeTrashBin(executantID *apiUser.UserId, deleteBefore time.Time, spaceType SpaceType, gatewaySelector pool.Selectable[apiGateway.GatewayAPIClient], machineAuthAPIKey string) error { +func PurgeTrashBin(serviceAccountID string, deleteBefore time.Time, spaceType SpaceType, gatewaySelector pool.Selectable[apiGateway.GatewayAPIClient], serviceAccountSecret string) error { gatewayClient, err := gatewaySelector.Next() if err != nil { return err } - executantCtx, _, err := utils.Impersonate(executantID, gatewayClient, machineAuthAPIKey) + ctx, err := utils.GetServiceUserContext(serviceAccountID, gatewayClient, serviceAccountSecret) if err != nil { return err } - listStorageSpacesResponse, err := gatewayClient.ListStorageSpaces(executantCtx, &apiProvider.ListStorageSpacesRequest{ + listStorageSpacesResponse, err := gatewayClient.ListStorageSpaces(ctx, &apiProvider.ListStorageSpacesRequest{ Filters: []*apiProvider.ListStorageSpacesRequest_Filter{ { Type: apiProvider.ListStorageSpacesRequest_Filter_TYPE_SPACE_TYPE, @@ -43,52 +41,15 @@ func PurgeTrashBin(executantID *apiUser.UserId, deleteBefore time.Time, spaceTyp } for _, storageSpace := range listStorageSpacesResponse.StorageSpaces { - var ( - err error - impersonationID *apiUser.UserId - storageSpaceReference = &apiProvider.Reference{ - ResourceId: storageSpace.GetRoot(), - } - ) - - switch SpaceType(storageSpace.GetSpaceType()) { - case Personal: - impersonationID = storageSpace.GetOwner().GetId() - case Project: - var permissionsMap map[string]*apiProvider.ResourcePermissions - err := utils.ReadJSONFromOpaque(storageSpace.GetOpaque(), "grants", &permissionsMap) - if err != nil { - break - } - - for id, permissions := range permissionsMap { - if !permissions.Delete { - continue - } - - impersonationID = &apiUser.UserId{ - OpaqueId: id, - } - break - } - default: + if typ := storageSpace.GetSpaceType(); typ != "personal" && typ != "project" { + // ignore spaces that are neither personal nor project continue } - - if err != nil { - return err + storageSpaceReference := &apiProvider.Reference{ + ResourceId: storageSpace.GetRoot(), } - if impersonationID == nil { - return fmt.Errorf("can't impersonate space user for space: %s", storageSpace.GetId().GetOpaqueId()) - } - - impersonatedCtx, _, err := utils.Impersonate(impersonationID, gatewayClient, machineAuthAPIKey) - if err != nil { - return err - } - - listRecycleResponse, err := gatewayClient.ListRecycle(impersonatedCtx, &apiProvider.ListRecycleRequest{Ref: storageSpaceReference}) + listRecycleResponse, err := gatewayClient.ListRecycle(ctx, &apiProvider.ListRecycleRequest{Ref: storageSpaceReference}) if err != nil { return err } @@ -99,7 +60,7 @@ func PurgeTrashBin(executantID *apiUser.UserId, deleteBefore time.Time, spaceTyp continue } - purgeRecycleResponse, err := gatewayClient.PurgeRecycle(impersonatedCtx, &apiProvider.PurgeRecycleRequest{ + purgeRecycleResponse, err := gatewayClient.PurgeRecycle(ctx, &apiProvider.PurgeRecycleRequest{ Ref: storageSpaceReference, Key: recycleItem.Key, }) diff --git a/services/storage-users/pkg/task/trash_bin_test.go b/services/storage-users/pkg/task/trash_bin_test.go index 1066565c95..9800f954f6 100644 --- a/services/storage-users/pkg/task/trash_bin_test.go +++ b/services/storage-users/pkg/task/trash_bin_test.go @@ -115,7 +115,7 @@ var _ = Describe("trash", func() { gatewayClient.On("GetUser", mock.Anything, mock.Anything).Return(getUserResponse, nil) gatewayClient.On("Authenticate", mock.Anything, mock.Anything).Return(nil, genericError) - err := task.PurgeTrashBin(user.Id, now, task.Project, gatewaySelector, "") + err := task.PurgeTrashBin("service-user-id", now, task.Project, gatewaySelector, "") Expect(err).To(HaveOccurred()) }) It("throws an error if space listing fails", func() { @@ -123,45 +123,9 @@ var _ = Describe("trash", func() { gatewayClient.On("Authenticate", mock.Anything, mock.Anything).Return(authenticateResponse, nil) gatewayClient.On("ListStorageSpaces", mock.Anything, mock.Anything).Return(nil, genericError) - err := task.PurgeTrashBin(user.Id, now, task.Project, gatewaySelector, "") + err := task.PurgeTrashBin("service-user-id", now, task.Project, gatewaySelector, "") Expect(err).To(HaveOccurred()) }) - It("throws an error if a personal space user can't be impersonated", func() { - listStorageSpacesResponse.StorageSpaces = []*apiProvider.StorageSpace{personalSpace} - gatewayClient.On("GetUser", mock.Anything, mock.Anything).Return(getUserResponse, nil) - gatewayClient.On("Authenticate", mock.Anything, mock.Anything).Return(authenticateResponse, nil) - gatewayClient.On("ListStorageSpaces", mock.Anything, mock.Anything).Return(listStorageSpacesResponse, nil) - - err := task.PurgeTrashBin(user.Id, now, task.Project, gatewaySelector, "") - Expect(err).To(MatchError(errors.New("can't impersonate space user for space: personal"))) - }) - It("throws an error if a project space user can't be impersonated", func() { - listStorageSpacesResponse.StorageSpaces = []*apiProvider.StorageSpace{projectSpace} - gatewayClient.On("GetUser", mock.Anything, mock.Anything).Return(getUserResponse, nil) - gatewayClient.On("Authenticate", mock.Anything, mock.Anything).Return(authenticateResponse, nil) - gatewayClient.On("ListStorageSpaces", mock.Anything, mock.Anything).Return(listStorageSpacesResponse, nil) - - err := task.PurgeTrashBin(user.Id, now, task.Project, gatewaySelector, "") - Expect(err).To(MatchError(errors.New("can't impersonate space user for space: project"))) - }) - It("throws an error if a project space has no user with delete permissions", func() { - listStorageSpacesResponse.StorageSpaces = []*apiProvider.StorageSpace{projectSpace} - projectSpace.Opaque.Map = map[string]*apiTypes.OpaqueEntry{ - "grants": { - Value: MustMarshal(map[string]*apiProvider.ResourcePermissions{ - "admin": { - Delete: false, - }, - }), - }, - } - gatewayClient.On("GetUser", mock.Anything, mock.Anything).Return(getUserResponse, nil) - gatewayClient.On("Authenticate", mock.Anything, mock.Anything).Return(authenticateResponse, nil) - gatewayClient.On("ListStorageSpaces", mock.Anything, mock.Anything).Return(listStorageSpacesResponse, nil) - - err := task.PurgeTrashBin(user.Id, now, task.Project, gatewaySelector, "") - Expect(err).To(MatchError(errors.New("can't impersonate space user for space: project"))) - }) It("only deletes items older than the specified period", func() { var ( recycleItems = map[string][]*apiProvider.RecycleItem{ @@ -231,7 +195,7 @@ var _ = Describe("trash", func() { }, nil, ) - err := task.PurgeTrashBin(user.Id, now, task.Project, gatewaySelector, "") + err := task.PurgeTrashBin("service-user-id", now, task.Project, gatewaySelector, "") Expect(err).To(BeNil()) Expect(recycleItems["personal"]).To(HaveLen(2)) Expect(recycleItems["project"]).To(HaveLen(2))