github.com/cs3org/reva/v2@v2.27.7/internal/http/services/sciencemesh/share.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 sciencemesh
    20  
    21  import (
    22  	"encoding/json"
    23  	"errors"
    24  	"mime"
    25  	"net/http"
    26  
    27  	appprovider "github.com/cs3org/go-cs3apis/cs3/app/provider/v1beta1"
    28  	gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
    29  	userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
    30  	ocmprovider "github.com/cs3org/go-cs3apis/cs3/ocm/provider/v1beta1"
    31  	rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
    32  	ocm "github.com/cs3org/go-cs3apis/cs3/sharing/ocm/v1beta1"
    33  	providerpb "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
    34  	"github.com/cs3org/reva/v2/internal/http/services/reqres"
    35  	"github.com/cs3org/reva/v2/pkg/appctx"
    36  	"github.com/cs3org/reva/v2/pkg/conversions"
    37  	"github.com/cs3org/reva/v2/pkg/ocm/share"
    38  	"github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool"
    39  	"github.com/go-playground/validator/v10"
    40  )
    41  
    42  var validate = validator.New()
    43  
    44  type sharesHandler struct {
    45  	gatewayClient gateway.GatewayAPIClient
    46  }
    47  
    48  func (h *sharesHandler) init(c *config) error {
    49  	var err error
    50  	h.gatewayClient, err = pool.GetGatewayServiceClient(c.GatewaySvc)
    51  	return err
    52  }
    53  
    54  type createShareRequest struct {
    55  	SourcePath        string `json:"sourcePath" validate:"required"`
    56  	TargetPath        string `json:"targetPath" validate:"required"`
    57  	Type              string `json:"type"`
    58  	Role              string `json:"role" validate:"oneof=viewer editor"`
    59  	RecipientUsername string `json:"recipientUsername" validate:"required"`
    60  	RecipientHost     string `json:"recipientHost" validate:"required"`
    61  }
    62  
    63  // CreateShare creates an OCM share.
    64  func (h *sharesHandler) CreateShare(w http.ResponseWriter, r *http.Request) {
    65  	log := appctx.GetLogger(r.Context())
    66  
    67  	req, err := getCreateShareRequest(r)
    68  	if err != nil {
    69  		reqres.WriteError(w, r, reqres.APIErrorInvalidParameter, "invalid parameters", err)
    70  		return
    71  	}
    72  
    73  	ctx := r.Context()
    74  
    75  	statRes, err := h.gatewayClient.Stat(ctx, &providerpb.StatRequest{
    76  		Ref: &providerpb.Reference{
    77  			Path: req.SourcePath,
    78  		},
    79  	})
    80  	switch {
    81  	case err != nil:
    82  		reqres.WriteError(w, r, reqres.APIErrorServerError, "unexpected error", err)
    83  		return
    84  	case statRes.Status.Code == rpc.Code_CODE_NOT_FOUND:
    85  		reqres.WriteError(w, r, reqres.APIErrorNotFound, statRes.Status.Message, nil)
    86  		return
    87  	case statRes.Status.Code != rpc.Code_CODE_OK:
    88  		reqres.WriteError(w, r, reqres.APIErrorServerError, statRes.Status.Message, errors.New(statRes.Status.Message))
    89  		return
    90  	}
    91  
    92  	recipientProviderInfo, err := h.gatewayClient.GetInfoByDomain(ctx, &ocmprovider.GetInfoByDomainRequest{
    93  		Domain: req.RecipientHost,
    94  	})
    95  	if err != nil {
    96  		reqres.WriteError(w, r, reqres.APIErrorServerError, "error sending a grpc get invite by domain info request", err)
    97  		return
    98  	}
    99  	if recipientProviderInfo.Status.Code != rpc.Code_CODE_OK {
   100  		reqres.WriteError(w, r, reqres.APIErrorNotFound, recipientProviderInfo.Status.Message, errors.New(recipientProviderInfo.Status.Message))
   101  		return
   102  	}
   103  
   104  	perm, viewMode := getPermissionsByRole(req.Role)
   105  
   106  	log.Debug().Msg("calling gatewayClient.CreateOCMShare from sciencemesh/share.go")
   107  	shareRes, err := h.gatewayClient.CreateOCMShare(ctx, &ocm.CreateOCMShareRequest{
   108  		ResourceId: statRes.Info.Id,
   109  		Grantee: &providerpb.Grantee{
   110  			Type: providerpb.GranteeType_GRANTEE_TYPE_USER,
   111  			Id: &providerpb.Grantee_UserId{
   112  				UserId: &userpb.UserId{
   113  					Idp:      req.RecipientHost,
   114  					OpaqueId: req.RecipientUsername,
   115  				},
   116  			},
   117  		},
   118  		RecipientMeshProvider: recipientProviderInfo.ProviderInfo,
   119  		AccessMethods: []*ocm.AccessMethod{
   120  			share.NewWebDavAccessMethod(perm),
   121  			share.NewWebappAccessMethod(viewMode),
   122  		},
   123  	})
   124  	log.Debug().Msg("called gatewayClient.CreateOCMShare from sciencemesh/share.go")
   125  
   126  	switch {
   127  	case err != nil:
   128  		reqres.WriteError(w, r, reqres.APIErrorServerError, "error sending a grpc CreateOCMShare", err)
   129  		return
   130  	case shareRes.Status.Code == rpc.Code_CODE_NOT_FOUND:
   131  		reqres.WriteError(w, r, reqres.APIErrorNotFound, shareRes.Status.Message, nil)
   132  		return
   133  	case shareRes.Status.Code == rpc.Code_CODE_ALREADY_EXISTS:
   134  		reqres.WriteError(w, r, reqres.APIErrorAlreadyExist, shareRes.Status.Message, nil)
   135  		return
   136  	case shareRes.Status.Code != rpc.Code_CODE_OK:
   137  		reqres.WriteError(w, r, reqres.APIErrorAlreadyExist, shareRes.Status.Message, errors.New(shareRes.Status.Message))
   138  		return
   139  	}
   140  
   141  	if err := json.NewEncoder(w).Encode(shareRes); err != nil {
   142  		log.Error().Err(err).Msg("error encoding response")
   143  		w.WriteHeader(http.StatusInternalServerError)
   144  		return
   145  	}
   146  
   147  	w.WriteHeader(http.StatusOK)
   148  }
   149  
   150  func getPermissionsByRole(role string) (*providerpb.ResourcePermissions, appprovider.ViewMode) {
   151  	switch role {
   152  	case "viewer":
   153  		return conversions.NewViewerRole().CS3ResourcePermissions(), appprovider.ViewMode_VIEW_MODE_READ_ONLY
   154  	case "editor":
   155  		return conversions.NewEditorRole().CS3ResourcePermissions(), appprovider.ViewMode_VIEW_MODE_READ_WRITE
   156  	}
   157  	return nil, 0
   158  }
   159  
   160  func getCreateShareRequest(r *http.Request) (*createShareRequest, error) {
   161  	var req createShareRequest
   162  	contentType, _, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
   163  	if err == nil && contentType == "application/json" {
   164  		if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
   165  			return nil, err
   166  		}
   167  	} else {
   168  		return nil, errors.New("body request not recognised")
   169  	}
   170  	// set defaults
   171  	if req.Type == "" {
   172  		req.Type = "viewer"
   173  	}
   174  	// validate the request
   175  	if err := validate.Struct(req); err != nil {
   176  		return nil, err
   177  	}
   178  	return &req, nil
   179  }