diff --git a/go.mod b/go.mod index a1986ff90..403979f2e 100644 --- a/go.mod +++ b/go.mod @@ -257,4 +257,4 @@ require ( stash.kopano.io/kgol/oidc-go v0.3.2 // indirect ) -replace github.com/cs3org/reva => ../reva +replace github.com/cs3org/reva => github.com/micbar/reva v1.9.1-0.20220210130539-d274bcbe266c diff --git a/go.sum b/go.sum index c6bf7a86e..677763b7f 100644 --- a/go.sum +++ b/go.sum @@ -333,8 +333,6 @@ github.com/crewjam/saml v0.4.5/go.mod h1:qCJQpUtZte9R1ZjUBcW8qtCNlinbO363ooNl02S github.com/cs3org/cato v0.0.0-20200828125504-e418fc54dd5e/go.mod h1:XJEZ3/EQuI3BXTp/6DUzFr850vlxq11I6satRtz0YQ4= github.com/cs3org/go-cs3apis v0.0.0-20220126114148-64c025ccdd19 h1:1jqPH58jCxvbaJ9WLIJ7W2/m622bWS6ChptzljSG6IQ= github.com/cs3org/go-cs3apis v0.0.0-20220126114148-64c025ccdd19/go.mod h1:UXha4TguuB52H14EMoSsCqDj7k8a/t7g4gVP+bgY5LY= -github.com/cs3org/reva v1.16.1-0.20220208091630-7660ee26dce7 h1:6U5hiLJ1vl3J8aoscg6qZLrpFvp5/Y4RnxZ2xS4RQWA= -github.com/cs3org/reva v1.16.1-0.20220208091630-7660ee26dce7/go.mod h1:EAKoGXYmyIKmUSq/fgmAsTGHgrgwSb1PffGiA/AmgSo= github.com/cubewise-code/go-mime v0.0.0-20200519001935-8c5762b177d8 h1:Z9lwXumT5ACSmJ7WGnFl+OMLLjpz5uR2fyz7dC255FI= github.com/cubewise-code/go-mime v0.0.0-20200519001935-8c5762b177d8/go.mod h1:4abs/jPXcmJzYoYGF91JF9Uq9s/KL5n1jvFDix8KcqY= github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4= @@ -985,6 +983,8 @@ github.com/mendsley/gojwk v0.0.0-20141217222730-4d5ec6e58103 h1:Z/i1e+gTZrmcGeZy github.com/mendsley/gojwk v0.0.0-20141217222730-4d5ec6e58103/go.mod h1:o9YPB5aGP8ob35Vy6+vyq3P3bWe7NQWzf+JLiXCiMaE= github.com/mennanov/fieldmask-utils v0.5.0 h1:8em4akN0NM3hmmrg8VbvOPfdS4SSBdbFd53m9VtfOg0= github.com/mennanov/fieldmask-utils v0.5.0/go.mod h1:lah2lHczE2ff+7SqnNKpB+YzaO7M3h5iNO4LgPTJheM= +github.com/micbar/reva v1.9.1-0.20220210130539-d274bcbe266c h1:wHd+DF2ZWUmoNCeBVNjsx23rJUkW67+0sPa07QaOxMk= +github.com/micbar/reva v1.9.1-0.20220210130539-d274bcbe266c/go.mod h1:EAKoGXYmyIKmUSq/fgmAsTGHgrgwSb1PffGiA/AmgSo= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= github.com/miekg/dns v1.1.40/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= diff --git a/graph/pkg/service/v0/driveitems.go b/graph/pkg/service/v0/driveitems.go index de142d3ec..14ee759b3 100644 --- a/graph/pkg/service/v0/driveitems.go +++ b/graph/pkg/service/v0/driveitems.go @@ -12,7 +12,6 @@ import ( cs3rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1" storageprovider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" types "github.com/cs3org/go-cs3apis/cs3/types/v1beta1" - "github.com/cs3org/reva/pkg/utils" "github.com/go-chi/render" libregraph "github.com/owncloud/libre-graph-api-go" "github.com/owncloud/ocis/graph/pkg/service/v0/errorcode" @@ -77,14 +76,11 @@ func (g Graph) GetRootDriveChildren(w http.ResponseWriter, r *http.Request) { render.JSON(w, r, &listResponse{Value: files}) } -func (g Graph) getDriveItem(ctx context.Context, root *storageprovider.ResourceId, relativePath string) (*libregraph.DriveItem, error) { - +func (g Graph) getDriveItem(ctx context.Context, root *storageprovider.ResourceId) (*libregraph.DriveItem, error) { client := g.GetGatewayClient() ref := &storageprovider.Reference{ ResourceId: root, - // the path is always relative to the root of the drive, not the location of the .config/ocis/space.yaml file - Path: utils.MakeRelativePath(relativePath), } res, err := client.Stat(ctx, &storageprovider.StatRequest{Ref: ref}) if err != nil { @@ -93,10 +89,23 @@ func (g Graph) getDriveItem(ctx context.Context, root *storageprovider.ResourceI if res.Status.Code != cs3rpc.Code_CODE_OK { return nil, fmt.Errorf("could not stat %s: %s", ref, res.Status.Message) } - return cs3ResourceToDriveItem(res.Info) } +func (g Graph) getPathForDriveItem(ctx context.Context, ID *storageprovider.ResourceId) (*string, error) { + client := g.GetGatewayClient() + var path *string + res, err := client.GetPath(ctx, &storageprovider.GetPathRequest{ResourceId: ID}) + if err != nil { + return nil, err + } + if res.Status.Code != cs3rpc.Code_CODE_OK { + return nil, fmt.Errorf("could not stat %s: %s", ID, res.Status.Message) + } + path = &res.Path + return path, err +} + func formatDriveItems(mds []*storageprovider.ResourceInfo) ([]*libregraph.DriveItem, error) { responses := make([]*libregraph.DriveItem, 0, len(mds)) for i := range mds { @@ -145,85 +154,44 @@ func cs3ResourceToDriveItem(res *storageprovider.ResourceInfo) (*libregraph.Driv return driveItem, nil } -func (g Graph) getDriveMetadata(ctx context.Context, space *storageprovider.StorageSpace) (map[string]string, error) { - client := g.GetGatewayClient() - sResp, err := client.Stat( - ctx, - &storageprovider.StatRequest{ - Ref: &storageprovider.Reference{ - ResourceId: &storageprovider.ResourceId{ - StorageId: space.Root.StorageId, - OpaqueId: space.Root.OpaqueId, - }, - }, - }, - ) - if err != nil { - g.logger.Debug().Err(err).Interface("space", space).Msg("transport error") - return nil, err - } - if sResp.Status.Code != cs3rpc.Code_CODE_OK { - g.logger.Debug().Interface("space", space).Msg("space not found") - return nil, fmt.Errorf("space not found") - } - md := sResp.Info.ArbitraryMetadata.GetMetadata() - if md == nil { - g.logger.Error().Err(err).Interface("space", space).Msg("could not read metadata from space") - return nil, fmt.Errorf("could not read metadata from space") - } - return md, nil -} - -func (g Graph) setSpaceMetadata(ctx context.Context, metadata map[string]string, resourceID *storageprovider.ResourceId) error { - if len(metadata) == 0 { - return nil - } - client := g.GetGatewayClient() - resMd, err := client.SetArbitraryMetadata( - ctx, - &storageprovider.SetArbitraryMetadataRequest{ - ArbitraryMetadata: &storageprovider.ArbitraryMetadata{ - Metadata: metadata, - }, - Ref: &storageprovider.Reference{ - ResourceId: resourceID, - }, - }, - ) - if err != nil { - g.logger.Error().Msg("transport error, could not set metadata") - return err - } - if resMd.Status.Code != cs3rpc.Code_CODE_OK { - g.logger.Error().Msg("could not set metadata") - return fmt.Errorf("could not set metadata") - } - return nil -} - -func (g Graph) GetSpecialSpaceItems(ctx context.Context, baseURL *url.URL, spaceRootID *storageprovider.ResourceId, metadata map[string]string) []libregraph.DriveItem { +func (g Graph) GetSpecialSpaceItems(ctx context.Context, baseURL *url.URL, space *storageprovider.StorageSpace) []libregraph.DriveItem { var spaceItems []libregraph.DriveItem - if metadata == nil { + if space.Opaque == nil { return spaceItems } - if readmePath, ok := metadata[ReadmePathAttrName]; ok { - readmeItem, err := g.getDriveItem(ctx, spaceRootID, readmePath) - if err != nil { - g.logger.Error().Err(err).Str(ReadmePathAttrName, readmePath).Msg("Could not get readme Item") - } else { - readmeItem.SpecialFolder = &libregraph.SpecialFolder{Name: libregraph.PtrString(ReadmePathSpecialFolderName)} - readmeItem.WebDavUrl = libregraph.PtrString(baseURL.String() + filepath.Join(spaceRootID.OpaqueId, readmePath)) - spaceItems = append(spaceItems, *readmeItem) + metadata := space.Opaque.Map + if readmeAttr, ok := metadata[ReadmeSpecialFolderName]; ok { + readmeID := string(readmeAttr.Value) + if readmeID != "" { + readmeItem, err := g.getDriveItem(ctx, &storageprovider.ResourceId{StorageId: space.Root.StorageId, OpaqueId: readmeID}) + if err != nil { + g.logger.Error().Err(err).Str("ID", readmeID).Msg("Could not get readme Item") + } + readmePath, err := g.getPathForDriveItem(ctx, &storageprovider.ResourceId{StorageId: space.Root.StorageId, OpaqueId: readmeID}) + if err != nil { + g.logger.Error().Err(err).Str("ID", readmeID).Msg("Could not get readme path") + } else { + readmeItem.SpecialFolder = &libregraph.SpecialFolder{Name: libregraph.PtrString(ReadmeSpecialFolderName)} + readmeItem.WebDavUrl = libregraph.PtrString(baseURL.String() + filepath.Join(space.Root.OpaqueId, *readmePath)) + spaceItems = append(spaceItems, *readmeItem) + } } } - if imagePath, ok := metadata[SpaceImageAttrName]; ok { - imageItem, err := g.getDriveItem(ctx, spaceRootID, imagePath) - if err != nil { - g.logger.Error().Err(err).Str(SpaceImageAttrName, imagePath).Msg("Could not get image Item") - } else { - imageItem.SpecialFolder = &libregraph.SpecialFolder{Name: libregraph.PtrString(SpaceImageSpecialFolderName)} - imageItem.WebDavUrl = libregraph.PtrString(baseURL.String() + filepath.Join(spaceRootID.OpaqueId, imagePath)) - spaceItems = append(spaceItems, *imageItem) + if metadataAttr, ok := metadata[SpaceImageSpecialFolderName]; ok { + imageID := string(metadataAttr.Value) + if imageID != "" { + imageItem, err := g.getDriveItem(ctx, &storageprovider.ResourceId{StorageId: space.Root.StorageId, OpaqueId: imageID}) + if err != nil { + g.logger.Error().Err(err).Str("ID", imageID).Msg("Could not get image Item") + } + imagePath, err := g.getPathForDriveItem(ctx, &storageprovider.ResourceId{StorageId: space.Root.StorageId, OpaqueId: imageID}) + if err != nil { + g.logger.Error().Err(err).Str("ID", imageID).Msg("Could not get image path") + } else { + imageItem.SpecialFolder = &libregraph.SpecialFolder{Name: libregraph.PtrString(SpaceImageSpecialFolderName)} + imageItem.WebDavUrl = libregraph.PtrString(baseURL.String() + filepath.Join(space.Root.OpaqueId, *imagePath)) + spaceItems = append(spaceItems, *imageItem) + } } } return spaceItems diff --git a/graph/pkg/service/v0/drives.go b/graph/pkg/service/v0/drives.go index f6e4cceb1..699701f9f 100644 --- a/graph/pkg/service/v0/drives.go +++ b/graph/pkg/service/v0/drives.go @@ -7,7 +7,6 @@ import ( "math" "net/http" "net/url" - "path/filepath" "strconv" "strings" @@ -192,6 +191,18 @@ func (g Graph) CreateDrive(w http.ResponseWriter, r *http.Request) { Quota: getQuota(drive.Quota, g.config.Spaces.DefaultQuota), } + spaceDescription := *drive.Description + if spaceDescription != "" { + csr.Opaque = &types.Opaque{ + Map: map[string]*types.OpaqueEntry{ + "permissions": { + Decoder: "plain", + Value: []byte(spaceDescription), + }, + }, + } + } + resp, err := client.CreateStorageSpace(r.Context(), &csr) if err != nil { errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, err.Error()) @@ -265,6 +276,7 @@ func (g Graph) UpdateDrive(w http.ResponseWriter, r *http.Request) { }, } + // Use the Opaque prop of the request if restore, _ := strconv.ParseBool(r.Header.Get("restore")); restore { updateSpaceRequest.Opaque = &types.Opaque{ Map: map[string]*types.OpaqueEntry{ @@ -276,38 +288,29 @@ func (g Graph) UpdateDrive(w http.ResponseWriter, r *http.Request) { } } + // Use the Opaque prop of the space + opaque := make(map[string]*types.OpaqueEntry) + spaceDescription := *drive.Description + if spaceDescription != "" { + opaque["description"] = &types.OpaqueEntry{ + Decoder: "plain", + Value: []byte(spaceDescription), + } + } + + for _, special := range drive.Special { + if special.Id != nil { + opaque[*special.SpecialFolder.Name] = &types.OpaqueEntry{ + Decoder: "plain", + Value: []byte(*special.Id), + } + } + } + updateSpaceRequest.StorageSpace.Opaque = &types.Opaque{Map: opaque} + if drive.Name != nil { updateSpaceRequest.StorageSpace.Name = *drive.Name } - metadata := make(map[string]string) - for _, specialItem := range drive.Special { - - baseUrl, err := url.Parse(g.config.Spaces.WebDavBase + g.config.Spaces.WebDavPath + driveID) - if err != nil { - g.logger.Error().Err(err) - } - newURL, err := filepath.Rel(baseUrl.String(), *specialItem.WebDavUrl) - if err != nil { - g.logger.Error().Err(err) - } - switch *specialItem.SpecialFolder.Name { - case SpaceImageSpecialFolderName: - metadata[SpaceImageAttrName] = newURL - case ReadmePathSpecialFolderName: - metadata[ReadmePathAttrName] = newURL - } - } - - if drive.Description != nil { - metadata[SpaceDescriptionAttrName] = *drive.Description - } - - if metadata != nil { - err = g.setSpaceMetadata(r.Context(), metadata, root) - if err != nil { - g.logger.Error().Err(err) - } - } if drive.Quota.HasTotal() { user := ctxpkg.ContextMustGetUser(r.Context()) @@ -360,33 +363,15 @@ func (g Graph) UpdateDrive(w http.ResponseWriter, r *http.Request) { render.JSON(w, r, spaces[0]) } -func (g Graph) formatDrives(ctx context.Context, baseURL *url.URL, mds []*storageprovider.StorageSpace) ([]*libregraph.Drive, error) { - responses := make([]*libregraph.Drive, 0, len(mds)) - for i := range mds { - res, err := g.cs3StorageSpaceToDrive(baseURL, mds[i]) +func (g Graph) formatDrives(ctx context.Context, baseURL *url.URL, storageSpaces []*storageprovider.StorageSpace) ([]*libregraph.Drive, error) { + responses := make([]*libregraph.Drive, 0, len(storageSpaces)) + for _, storageSpace := range storageSpaces { + res, err := g.cs3StorageSpaceToDrive(baseURL, storageSpace) if err != nil { return nil, err } - md, err := g.getDriveMetadata(ctx, mds[i]) - if err != nil { - g.logger.Error().Err(err).Interface("space", mds[i]).Msg("error reading extendedSpaceProperties") - continue - } - - if sd, ok := md[SpaceDescriptionAttrName]; ok { - res.Description = libregraph.PtrString(sd) - } - res.Special = g.GetSpecialSpaceItems( - ctx, - baseURL, - &storageprovider.ResourceId{ - StorageId: mds[i].Root.StorageId, - OpaqueId: mds[i].Root.OpaqueId, - }, - md, - ) - - res.Quota, err = g.getDriveQuota(ctx, mds[i]) + res.Special = g.GetSpecialSpaceItems(ctx, baseURL, storageSpace) + res.Quota, err = g.getDriveQuota(ctx, storageSpace) if err != nil { return nil, err } @@ -502,6 +487,11 @@ func (g Graph) cs3StorageSpaceToDrive(baseURL *url.URL, space *storageprovider.S Permissions: permissions, }, } + if space.Opaque != nil { + if description, ok := space.Opaque.Map["description"]; ok { + drive.Description = libregraph.PtrString(string(description.Value)) + } + } if space.Opaque != nil && space.Opaque.Map != nil { v, ok := space.Opaque.Map["trashed"] diff --git a/graph/pkg/service/v0/graph.go b/graph/pkg/service/v0/graph.go index 69f8a8152..b634384e3 100644 --- a/graph/pkg/service/v0/graph.go +++ b/graph/pkg/service/v0/graph.go @@ -23,6 +23,8 @@ type GatewayClient interface { // Returns the home path for the given authenticated user. // When a user has access to multiple storage providers, one of them is the home. GetHome(ctx context.Context, in *provider.GetHomeRequest, opts ...grpc.CallOption) (*provider.GetHomeResponse, error) + // GetPath does a path lookup for a resource by ID + GetPath(ctx context.Context, in *provider.GetPathRequest, opts ...grpc.CallOption) (*provider.GetPathResponse, error) // Returns a list of resource information // for the provided reference. // MUST return CODE_NOT_FOUND if the reference does not exists. @@ -39,8 +41,6 @@ type GatewayClient interface { ListStorageSpaces(ctx context.Context, in *provider.ListStorageSpacesRequest, opts ...grpc.CallOption) (*provider.ListStorageSpacesResponse, error) // Updates a storage space. UpdateStorageSpace(ctx context.Context, in *provider.UpdateStorageSpaceRequest, opts ...grpc.CallOption) (*provider.UpdateStorageSpaceResponse, error) - // SetArbitraryMetadata stores key value metadata - SetArbitraryMetadata(ctx context.Context, in *provider.SetArbitraryMetadataRequest, opts ...grpc.CallOption) (*provider.SetArbitraryMetadataResponse, error) // Deletes a storage space. DeleteStorageSpace(ctx context.Context, in *provider.DeleteStorageSpaceRequest, opts ...grpc.CallOption) (*provider.DeleteStorageSpaceResponse, error) // Returns the quota available under the provided @@ -92,9 +92,6 @@ const ( NoSpaceFoundMessage = "space with id `%s` not found" ListStorageSpacesTransportErr = "transport error sending list storage spaces grpc request" ListStorageSpacesReturnsErr = "list storage spaces grpc request returns an errorcode in the response" - ReadmePathAttrName = "space.readme" - ReadmePathSpecialFolderName = "readme" - SpaceImageAttrName = "space.image" + ReadmeSpecialFolderName = "readme" SpaceImageSpecialFolderName = "image" - SpaceDescriptionAttrName = "space.description" )