github.com/cs3org/reva/v2@v2.27.7/pkg/ocm/storage/received/ocm.go (about)

     1  // Copyright 2018-2023 CERN
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  //
    15  // In applying this license, CERN does not waive the privileges and immunities
    16  // granted to it by virtue of its status as an Intergovernmental Organization
    17  // or submit itself to any jurisdiction.
    18  
    19  package ocm
    20  
    21  import (
    22  	"context"
    23  	"crypto/tls"
    24  	"encoding/base64"
    25  	"encoding/xml"
    26  	"io"
    27  	"io/fs"
    28  	"net/http"
    29  	"net/url"
    30  	"path/filepath"
    31  	"regexp"
    32  	"strings"
    33  
    34  	gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
    35  	userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
    36  	rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
    37  	ocmpb "github.com/cs3org/go-cs3apis/cs3/sharing/ocm/v1beta1"
    38  	provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
    39  	typepb "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
    40  	"github.com/rs/zerolog"
    41  	"github.com/studio-b12/gowebdav"
    42  
    43  	"github.com/cs3org/reva/v2/pkg/errtypes"
    44  	"github.com/cs3org/reva/v2/pkg/events"
    45  	"github.com/cs3org/reva/v2/pkg/mime"
    46  	"github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool"
    47  	"github.com/cs3org/reva/v2/pkg/rhttp/router"
    48  	"github.com/cs3org/reva/v2/pkg/sharedconf"
    49  	"github.com/cs3org/reva/v2/pkg/storage"
    50  	"github.com/cs3org/reva/v2/pkg/storage/fs/registry"
    51  	"github.com/cs3org/reva/v2/pkg/storagespace"
    52  	"github.com/cs3org/reva/v2/pkg/utils"
    53  	"github.com/cs3org/reva/v2/pkg/utils/cfg"
    54  )
    55  
    56  func init() {
    57  	registry.Register("ocmreceived", New)
    58  }
    59  
    60  type driver struct {
    61  	c       *config
    62  	gateway *pool.Selector[gateway.GatewayAPIClient]
    63  }
    64  
    65  type config struct {
    66  	GatewaySVC           string `mapstructure:"gatewaysvc"`
    67  	Insecure             bool   `mapstructure:"insecure"`
    68  	StorageRoot          string `mapstructure:"storage_root"`
    69  	ServiceAccountID     string `mapstructure:"service_account_id"`
    70  	ServiceAccountSecret string `mapstructure:"service_account_secret"`
    71  }
    72  
    73  func (c *config) ApplyDefaults() {
    74  	c.GatewaySVC = sharedconf.GetGatewaySVC(c.GatewaySVC)
    75  }
    76  
    77  // BearerAuthenticator represents an authenticator that adds a Bearer token to the Authorization header of HTTP requests.
    78  type BearerAuthenticator struct {
    79  	Token string
    80  }
    81  
    82  // Authorize adds the Bearer token to the Authorization header of the provided HTTP request.
    83  func (b BearerAuthenticator) Authorize(_ *http.Client, r *http.Request, _ string) error {
    84  	r.Header.Add("Authorization", "Bearer "+b.Token)
    85  	return nil
    86  }
    87  
    88  // Verify is not implemented for the BearerAuthenticator. It always returns false and nil error.
    89  func (BearerAuthenticator) Verify(*http.Client, *http.Response, string) (bool, error) {
    90  	return false, nil
    91  }
    92  
    93  // Clone creates a new instance of the BearerAuthenticator.
    94  func (b BearerAuthenticator) Clone() gowebdav.Authenticator {
    95  	return BearerAuthenticator{Token: b.Token}
    96  }
    97  
    98  // Close is not implemented for the BearerAuthenticator. It always returns nil.
    99  func (BearerAuthenticator) Close() error {
   100  	return nil
   101  }
   102  
   103  // New creates an OCM storage driver.
   104  func New(m map[string]interface{}, _ events.Stream, _ *zerolog.Logger) (storage.FS, error) {
   105  	var c config
   106  	if err := cfg.Decode(m, &c); err != nil {
   107  		return nil, err
   108  	}
   109  
   110  	gateway, err := pool.GatewaySelector(c.GatewaySVC)
   111  	if err != nil {
   112  		return nil, err
   113  	}
   114  
   115  	d := &driver{
   116  		c:       &c,
   117  		gateway: gateway,
   118  	}
   119  
   120  	return d, nil
   121  }
   122  
   123  func shareInfoFromPath(path string) (*ocmpb.ShareId, string) {
   124  	// the path is of the type /share_id[/rel_path]
   125  	shareID, rel := router.ShiftPath(path)
   126  	return &ocmpb.ShareId{OpaqueId: shareID}, rel
   127  }
   128  
   129  func shareInfoFromReference(ref *provider.Reference) (*ocmpb.ShareId, string) {
   130  	if ref.ResourceId == nil {
   131  		return shareInfoFromPath(ref.Path)
   132  	}
   133  
   134  	if ref.ResourceId.SpaceId == ref.ResourceId.OpaqueId {
   135  		return &ocmpb.ShareId{OpaqueId: ref.ResourceId.SpaceId}, ref.Path
   136  	}
   137  	decodedBytes, err := base64.StdEncoding.DecodeString(ref.ResourceId.OpaqueId)
   138  	if err != nil {
   139  		// this should never happen
   140  		return &ocmpb.ShareId{OpaqueId: ref.ResourceId.SpaceId}, ref.Path
   141  	}
   142  	return &ocmpb.ShareId{OpaqueId: ref.ResourceId.SpaceId}, filepath.Join(string(decodedBytes), ref.Path)
   143  
   144  }
   145  
   146  func (d *driver) getWebDAVFromShare(ctx context.Context, forUser *userpb.UserId, shareID *ocmpb.ShareId) (*ocmpb.ReceivedShare, string, string, error) {
   147  	// TODO: we may want to cache the share
   148  	req := &ocmpb.GetReceivedOCMShareRequest{
   149  		Ref: &ocmpb.ShareReference{
   150  			Spec: &ocmpb.ShareReference_Id{
   151  				Id: shareID,
   152  			},
   153  		},
   154  	}
   155  	if forUser != nil {
   156  		req.Opaque = utils.AppendJSONToOpaque(nil, "userid", forUser)
   157  	}
   158  	gwc, err := d.gateway.Next()
   159  	if err != nil {
   160  		return nil, "", "", err
   161  	}
   162  	res, err := gwc.GetReceivedOCMShare(ctx, req)
   163  	if err != nil {
   164  		return nil, "", "", err
   165  	}
   166  
   167  	if res.Status.Code != rpc.Code_CODE_OK {
   168  		if res.Status.Code == rpc.Code_CODE_NOT_FOUND {
   169  			return nil, "", "", errtypes.NotFound("share not found")
   170  		}
   171  		return nil, "", "", errtypes.InternalError(res.Status.Message)
   172  	}
   173  
   174  	dav, ok := getWebDAVProtocol(res.Share.Protocols)
   175  	if !ok {
   176  		return nil, "", "", errtypes.NotFound("share does not contain a WebDAV endpoint")
   177  	}
   178  
   179  	return res.Share, dav.Uri, dav.SharedSecret, nil
   180  }
   181  
   182  func getWebDAVProtocol(protocols []*ocmpb.Protocol) (*ocmpb.WebDAVProtocol, bool) {
   183  	for _, p := range protocols {
   184  		if dav, ok := p.Term.(*ocmpb.Protocol_WebdavOptions); ok {
   185  			return dav.WebdavOptions, true
   186  		}
   187  	}
   188  	return nil, false
   189  }
   190  
   191  func (d *driver) webdavClient(ctx context.Context, forUser *userpb.UserId, ref *provider.Reference) (*gowebdav.Client, *ocmpb.ReceivedShare, string, error) {
   192  	id, rel := shareInfoFromReference(ref)
   193  
   194  	share, endpoint, secret, err := d.getWebDAVFromShare(ctx, forUser, id)
   195  	if err != nil {
   196  		return nil, nil, "", err
   197  	}
   198  
   199  	endpoint, err = url.PathUnescape(endpoint)
   200  	if err != nil {
   201  		return nil, nil, "", err
   202  	}
   203  
   204  	// FIXME: it's still not clear from the OCM APIs how to use the shared secret
   205  	// will use as a token in the bearer authentication as this is the reva implementation
   206  	c := gowebdav.NewAuthClient(endpoint, gowebdav.NewPreemptiveAuth(BearerAuthenticator{Token: secret}))
   207  	if d.c.Insecure {
   208  		c.SetTransport(&http.Transport{
   209  			TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
   210  		})
   211  	}
   212  
   213  	return c, share, rel, nil
   214  }
   215  
   216  func (d *driver) CreateDir(ctx context.Context, ref *provider.Reference) error {
   217  	client, _, rel, err := d.webdavClient(ctx, nil, ref)
   218  	if err != nil {
   219  		return err
   220  	}
   221  	return client.MkdirAll(rel, 0)
   222  }
   223  
   224  func (d *driver) Delete(ctx context.Context, ref *provider.Reference) error {
   225  	client, _, rel, err := d.webdavClient(ctx, nil, ref)
   226  	if err != nil {
   227  		return err
   228  	}
   229  	return client.RemoveAll(rel)
   230  }
   231  
   232  func (d *driver) TouchFile(ctx context.Context, ref *provider.Reference, markprocessing bool, mtime string) error {
   233  	client, _, rel, err := d.webdavClient(ctx, nil, ref)
   234  	if err != nil {
   235  		return err
   236  	}
   237  	return client.Write(rel, []byte{}, 0)
   238  }
   239  
   240  func (d *driver) Move(ctx context.Context, oldRef, newRef *provider.Reference) error {
   241  	client, _, relOld, err := d.webdavClient(ctx, nil, oldRef)
   242  	if err != nil {
   243  		return err
   244  	}
   245  	_, relNew := shareInfoFromReference(newRef)
   246  
   247  	return client.Rename(relOld, relNew, false)
   248  }
   249  
   250  func getPathFromShareIDAndRelPath(shareID *ocmpb.ShareId, relPath string) string {
   251  	return filepath.Join("/", shareID.OpaqueId, relPath)
   252  }
   253  
   254  func convertStatToResourceInfo(ref *provider.Reference, f fs.FileInfo, share *ocmpb.ReceivedShare) (*provider.ResourceInfo, error) {
   255  	t := provider.ResourceType_RESOURCE_TYPE_FILE
   256  	if f.IsDir() {
   257  		t = provider.ResourceType_RESOURCE_TYPE_CONTAINER
   258  	}
   259  
   260  	webdavFile, ok := f.(gowebdav.File)
   261  	if !ok {
   262  		return nil, errtypes.InternalError("could not get webdav props")
   263  	}
   264  
   265  	var name string
   266  	switch {
   267  	case share.ResourceType == provider.ResourceType_RESOURCE_TYPE_FILE:
   268  		name = share.Name
   269  	case webdavFile.Path() == "/":
   270  		name = share.Name
   271  	default:
   272  		name = webdavFile.Name()
   273  	}
   274  
   275  	opaqueid := base64.StdEncoding.EncodeToString([]byte(webdavFile.Path()))
   276  
   277  	// ids are of the format <ocmstorageproviderid>$<shareid>!<opaqueid>
   278  	id := &provider.ResourceId{
   279  		StorageId: utils.OCMStorageProviderID,
   280  		SpaceId:   share.Id.OpaqueId,
   281  		OpaqueId:  opaqueid,
   282  	}
   283  	webdavProtocol, _ := getWebDAVProtocol(share.Protocols)
   284  
   285  	ri := provider.ResourceInfo{
   286  		Type:     t,
   287  		Id:       id,
   288  		MimeType: mime.Detect(f.IsDir(), f.Name()),
   289  		Path:     name,
   290  		Name:     name,
   291  		Size:     uint64(f.Size()),
   292  		Mtime: &typepb.Timestamp{
   293  			Seconds: uint64(f.ModTime().Unix()),
   294  		},
   295  		Etag:          webdavFile.ETag(),
   296  		Owner:         share.Creator,
   297  		PermissionSet: webdavProtocol.Permissions.Permissions,
   298  	}
   299  
   300  	if t == provider.ResourceType_RESOURCE_TYPE_FILE {
   301  		// get SHA1 checksum from owncloud specific properties if available
   302  		propstat := webdavFile.Sys().(gowebdav.Props)
   303  		ri.Checksum = extractChecksum(propstat)
   304  	}
   305  
   306  	if f.(gowebdav.File).StatusCode() == 425 {
   307  		ri.Opaque = utils.AppendPlainToOpaque(ri.Opaque, "status", "processing")
   308  	}
   309  
   310  	return &ri, nil
   311  }
   312  
   313  func extractChecksum(props gowebdav.Props) *provider.ResourceChecksum {
   314  	checksums := props.GetString(xml.Name{Space: "http://owncloud.org/ns", Local: "checksums"})
   315  	if checksums == "" {
   316  		return &provider.ResourceChecksum{
   317  			Type: provider.ResourceChecksumType_RESOURCE_CHECKSUM_TYPE_INVALID,
   318  		}
   319  	}
   320  	re := regexp.MustCompile("SHA1:(.*)")
   321  	matches := re.FindStringSubmatch(checksums)
   322  	if len(matches) == 2 {
   323  		return &provider.ResourceChecksum{
   324  			Type: provider.ResourceChecksumType_RESOURCE_CHECKSUM_TYPE_SHA1,
   325  			Sum:  matches[1],
   326  		}
   327  	}
   328  	return &provider.ResourceChecksum{
   329  		Type: provider.ResourceChecksumType_RESOURCE_CHECKSUM_TYPE_INVALID,
   330  	}
   331  }
   332  
   333  func (d *driver) GetMD(ctx context.Context, ref *provider.Reference, _ []string, _ []string) (*provider.ResourceInfo, error) {
   334  	client, share, rel, err := d.webdavClient(ctx, nil, ref)
   335  	if err != nil {
   336  		return nil, err
   337  	}
   338  
   339  	info, err := client.StatWithProps(rel, []string{}) // request all properties by giving an empty list
   340  	if err != nil {
   341  		if gowebdav.IsErrNotFound(err) {
   342  			return nil, errtypes.NotFound(ref.GetPath())
   343  		}
   344  		return nil, err
   345  	}
   346  
   347  	return convertStatToResourceInfo(ref, info, share)
   348  }
   349  
   350  func (d *driver) ListFolder(ctx context.Context, ref *provider.Reference, _ []string, _ []string) ([]*provider.ResourceInfo, error) {
   351  	client, share, rel, err := d.webdavClient(ctx, nil, ref)
   352  	if err != nil {
   353  		return nil, err
   354  	}
   355  
   356  	list, err := client.ReadDirWithProps(rel, []string{}) // request all properties by giving an empty list
   357  	if err != nil {
   358  		return nil, err
   359  	}
   360  
   361  	res := make([]*provider.ResourceInfo, 0, len(list))
   362  	for _, r := range list {
   363  		info, err := convertStatToResourceInfo(ref, r, share)
   364  		if err != nil {
   365  			return nil, err
   366  		}
   367  		res = append(res, info)
   368  	}
   369  	return res, nil
   370  }
   371  
   372  func (d *driver) Download(ctx context.Context, ref *provider.Reference, openReaderfunc func(*provider.ResourceInfo) bool) (*provider.ResourceInfo, io.ReadCloser, error) {
   373  	client, share, rel, err := d.webdavClient(ctx, nil, ref)
   374  	if err != nil {
   375  		return nil, nil, err
   376  	}
   377  
   378  	info, err := client.StatWithProps(rel, []string{}) // request all properties by giving an empty list
   379  	if err != nil {
   380  		if gowebdav.IsErrNotFound(err) {
   381  			return nil, nil, errtypes.NotFound(ref.GetPath())
   382  		}
   383  		return nil, nil, err
   384  	}
   385  	md, err := convertStatToResourceInfo(ref, info, share)
   386  	if err != nil {
   387  		return nil, nil, err
   388  	}
   389  
   390  	if !openReaderfunc(md) {
   391  		return md, nil, nil
   392  	}
   393  
   394  	reader, err := client.ReadStream(rel)
   395  	return md, reader, err
   396  }
   397  
   398  func (d *driver) GetPathByID(ctx context.Context, id *provider.ResourceId) (string, error) {
   399  	shareID, rel := shareInfoFromReference(&provider.Reference{
   400  		ResourceId: id,
   401  	})
   402  	return getPathFromShareIDAndRelPath(shareID, rel), nil
   403  }
   404  
   405  func (d *driver) Shutdown(ctx context.Context) error {
   406  	return nil
   407  }
   408  
   409  func (d *driver) CreateHome(ctx context.Context) error {
   410  	return errtypes.NotSupported("operation not supported")
   411  }
   412  
   413  func (d *driver) GetHome(ctx context.Context) (string, error) {
   414  	return "", errtypes.NotSupported("operation not supported")
   415  }
   416  
   417  func (d *driver) ListRevisions(ctx context.Context, ref *provider.Reference) ([]*provider.FileVersion, error) {
   418  	return nil, errtypes.NotSupported("operation not supported")
   419  }
   420  
   421  func (d *driver) DownloadRevision(ctx context.Context, ref *provider.Reference, key string, openReaderFunc func(md *provider.ResourceInfo) bool) (*provider.ResourceInfo, io.ReadCloser, error) {
   422  	return nil, nil, errtypes.NotSupported("operation not supported")
   423  }
   424  
   425  func (d *driver) RestoreRevision(ctx context.Context, ref *provider.Reference, key string) error {
   426  	return errtypes.NotSupported("operation not supported")
   427  }
   428  
   429  func (d *driver) ListRecycle(ctx context.Context, ref *provider.Reference, key, relativePath string) ([]*provider.RecycleItem, error) {
   430  	return nil, errtypes.NotSupported("operation not supported")
   431  }
   432  
   433  func (d *driver) RestoreRecycleItem(ctx context.Context, ref *provider.Reference, key, relativePath string, restoreRef *provider.Reference) error {
   434  	return errtypes.NotSupported("operation not supported")
   435  }
   436  
   437  func (d *driver) PurgeRecycleItem(ctx context.Context, ref *provider.Reference, key, relativePath string) error {
   438  	return errtypes.NotSupported("operation not supported")
   439  }
   440  
   441  func (d *driver) EmptyRecycle(ctx context.Context, ref *provider.Reference) error {
   442  	return errtypes.NotSupported("operation not supported")
   443  }
   444  
   445  func (d *driver) AddGrant(ctx context.Context, ref *provider.Reference, g *provider.Grant) error {
   446  	return errtypes.NotSupported("operation not supported")
   447  }
   448  
   449  func (d *driver) DenyGrant(ctx context.Context, ref *provider.Reference, g *provider.Grantee) error {
   450  	return errtypes.NotSupported("operation not supported")
   451  }
   452  
   453  func (d *driver) RemoveGrant(ctx context.Context, ref *provider.Reference, g *provider.Grant) error {
   454  	return errtypes.NotSupported("operation not supported")
   455  }
   456  
   457  func (d *driver) UpdateGrant(ctx context.Context, ref *provider.Reference, g *provider.Grant) error {
   458  	return errtypes.NotSupported("operation not supported")
   459  }
   460  
   461  func (d *driver) ListGrants(ctx context.Context, ref *provider.Reference) ([]*provider.Grant, error) {
   462  	return nil, errtypes.NotSupported("operation not supported")
   463  }
   464  
   465  func (d *driver) GetQuota(ctx context.Context, ref *provider.Reference) ( /*TotalBytes*/ uint64 /*UsedBytes*/, uint64, uint64, error) {
   466  	return 0, 0, 0, errtypes.NotSupported("operation not supported")
   467  }
   468  
   469  func (d *driver) CreateReference(ctx context.Context, path string, targetURI *url.URL) error {
   470  	return errtypes.NotSupported("operation not supported")
   471  }
   472  
   473  func (d *driver) SetArbitraryMetadata(ctx context.Context, ref *provider.Reference, md *provider.ArbitraryMetadata) error {
   474  	return errtypes.NotSupported("operation not supported")
   475  }
   476  
   477  func (d *driver) UnsetArbitraryMetadata(ctx context.Context, ref *provider.Reference, keys []string) error {
   478  	return errtypes.NotSupported("operation not supported")
   479  }
   480  
   481  // SetLock sets a lock on a file
   482  func (d *driver) SetLock(ctx context.Context, ref *provider.Reference, lock *provider.Lock) error {
   483  	client, _, rel, err := d.webdavClient(ctx, nil, ref)
   484  	if err != nil {
   485  		return err
   486  	}
   487  
   488  	return client.Lock(rel, lock.GetLockId())
   489  }
   490  
   491  func (d *driver) GetLock(ctx context.Context, ref *provider.Reference) (*provider.Lock, error) {
   492  	client, _, rel, err := d.webdavClient(ctx, nil, ref)
   493  	if err != nil {
   494  		return nil, err
   495  	}
   496  
   497  	token, err := client.GetLock(rel)
   498  	if err != nil {
   499  		return nil, err
   500  	}
   501  
   502  	return &provider.Lock{LockId: token, Type: provider.LockType_LOCK_TYPE_EXCL}, nil
   503  }
   504  
   505  func (d *driver) RefreshLock(ctx context.Context, ref *provider.Reference, lock *provider.Lock, existingLockID string) error {
   506  	client, _, rel, err := d.webdavClient(ctx, nil, ref)
   507  	if err != nil {
   508  		return err
   509  	}
   510  
   511  	return client.RefreshLock(rel, lock.GetLockId())
   512  }
   513  
   514  // Unlock removes a lock from a file
   515  func (d *driver) Unlock(ctx context.Context, ref *provider.Reference, lock *provider.Lock) error {
   516  	client, _, rel, err := d.webdavClient(ctx, nil, ref)
   517  	if err != nil {
   518  		return err
   519  	}
   520  
   521  	return client.Unlock(rel, lock.GetLockId())
   522  }
   523  
   524  func (d *driver) ListStorageSpaces(ctx context.Context, filters []*provider.ListStorageSpacesRequest_Filter, _ bool) ([]*provider.StorageSpace, error) {
   525  	spaceTypes := map[string]struct{}{}
   526  	var exists = struct{}{}
   527  	appendTypes := []string{}
   528  	for _, f := range filters {
   529  		if f.Type == provider.ListStorageSpacesRequest_Filter_TYPE_SPACE_TYPE {
   530  			spaceType := f.GetSpaceType()
   531  			if spaceType == "+mountpoint" {
   532  				appendTypes = append(appendTypes, strings.TrimPrefix(spaceType, "+"))
   533  				continue
   534  			}
   535  			spaceTypes[spaceType] = exists
   536  		}
   537  	}
   538  	gwc, err := d.gateway.Next()
   539  	if err != nil {
   540  		return nil, err
   541  	}
   542  	lrsRes, err := gwc.ListReceivedOCMShares(ctx, &ocmpb.ListReceivedOCMSharesRequest{})
   543  	if err != nil {
   544  		return nil, err
   545  	}
   546  	// FIXME This might have to be ListOCMShares
   547  	// these are grants
   548  
   549  	lsRes, err := gwc.ListOCMShares(ctx, &ocmpb.ListOCMSharesRequest{})
   550  	if err != nil {
   551  		return nil, err
   552  	}
   553  
   554  	if len(spaceTypes) == 0 {
   555  		spaceTypes["mountpoint"] = exists
   556  	}
   557  	for _, s := range appendTypes {
   558  		spaceTypes[s] = exists
   559  	}
   560  
   561  	spaces := []*provider.StorageSpace{}
   562  	for k := range spaceTypes {
   563  		if k == "mountpoint" {
   564  			for _, share := range lrsRes.Shares {
   565  				root := &provider.ResourceId{
   566  					StorageId: utils.OCMStorageProviderID,
   567  					SpaceId:   share.Id.OpaqueId,
   568  					OpaqueId:  share.Id.OpaqueId,
   569  				}
   570  				space := &provider.StorageSpace{
   571  					Id: &provider.StorageSpaceId{
   572  						OpaqueId: storagespace.FormatResourceID(root),
   573  					},
   574  					SpaceType: "mountpoint",
   575  					Owner: &userpb.User{
   576  						Id: share.Grantee.GetUserId(),
   577  					},
   578  					Root: root,
   579  				}
   580  
   581  				spaces = append(spaces, space)
   582  			}
   583  			for _, share := range lsRes.Shares {
   584  				root := &provider.ResourceId{
   585  					StorageId: utils.OCMStorageProviderID,
   586  					SpaceId:   share.Id.OpaqueId,
   587  					OpaqueId:  share.Id.OpaqueId,
   588  				}
   589  				space := &provider.StorageSpace{
   590  					Id: &provider.StorageSpaceId{
   591  						OpaqueId: storagespace.FormatResourceID(root),
   592  					},
   593  					SpaceType: "mountpoint",
   594  					Owner: &userpb.User{
   595  						Id: share.Grantee.GetUserId(),
   596  					},
   597  					Root: root,
   598  				}
   599  
   600  				spaces = append(spaces, space)
   601  			}
   602  		}
   603  	}
   604  
   605  	return spaces, nil
   606  }
   607  
   608  func (d *driver) CreateStorageSpace(ctx context.Context, req *provider.CreateStorageSpaceRequest) (*provider.CreateStorageSpaceResponse, error) {
   609  	return nil, errtypes.NotSupported("operation not supported")
   610  }
   611  
   612  func (d *driver) UpdateStorageSpace(ctx context.Context, req *provider.UpdateStorageSpaceRequest) (*provider.UpdateStorageSpaceResponse, error) {
   613  	return nil, errtypes.NotSupported("operation not supported")
   614  }
   615  
   616  func (d *driver) DeleteStorageSpace(ctx context.Context, req *provider.DeleteStorageSpaceRequest) error {
   617  	return errtypes.NotSupported("operation not supported")
   618  }