github.com/cs3org/reva/v2@v2.27.7/internal/http/services/sciencemesh/apps.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  	"context"
    23  	"encoding/json"
    24  	"errors"
    25  	"net/http"
    26  	"strings"
    27  
    28  	gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
    29  	rpcv1beta1 "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
    30  	ocmpb "github.com/cs3org/go-cs3apis/cs3/sharing/ocm/v1beta1"
    31  	"github.com/cs3org/reva/v2/internal/http/services/reqres"
    32  	"github.com/cs3org/reva/v2/pkg/errtypes"
    33  	"github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool"
    34  	"github.com/cs3org/reva/v2/pkg/rhttp/router"
    35  )
    36  
    37  type appsHandler struct {
    38  	gatewaySelector *pool.Selector[gateway.GatewayAPIClient]
    39  	ocmMountPoint   string
    40  }
    41  
    42  func (h *appsHandler) init(c *config) error {
    43  	var err error
    44  	h.gatewaySelector, err = pool.GatewaySelector(c.GatewaySvc)
    45  	if err != nil {
    46  		return err
    47  	}
    48  	h.ocmMountPoint = c.OCMMountPoint
    49  
    50  	return nil
    51  }
    52  
    53  func (h *appsHandler) shareInfo(p string) (*ocmpb.ShareId, string) {
    54  	p = strings.TrimPrefix(p, h.ocmMountPoint)
    55  	shareID, rel := router.ShiftPath(p)
    56  	if len(rel) > 0 {
    57  		rel = rel[1:]
    58  	}
    59  	return &ocmpb.ShareId{OpaqueId: shareID}, rel
    60  }
    61  
    62  func (h *appsHandler) OpenInApp(w http.ResponseWriter, r *http.Request) {
    63  	ctx := r.Context()
    64  
    65  	if err := r.ParseForm(); err != nil {
    66  		reqres.WriteError(w, r, reqres.APIErrorInvalidParameter, "parameters could not be parsed", nil)
    67  		return
    68  	}
    69  
    70  	path := r.Form.Get("file")
    71  	if path == "" {
    72  		reqres.WriteError(w, r, reqres.APIErrorInvalidParameter, "missing file", nil)
    73  		return
    74  	}
    75  
    76  	shareID, rel := h.shareInfo(path)
    77  
    78  	template, err := h.webappTemplate(ctx, shareID)
    79  	if err != nil {
    80  		var e errtypes.NotFound
    81  		if errors.As(err, &e) {
    82  			reqres.WriteError(w, r, reqres.APIErrorNotFound, e.Error(), nil)
    83  		}
    84  		reqres.WriteError(w, r, reqres.APIErrorServerError, err.Error(), err)
    85  		return
    86  	}
    87  
    88  	url := resolveTemplate(template, rel)
    89  
    90  	if err := json.NewEncoder(w).Encode(map[string]any{
    91  		"app_url": url,
    92  	}); err != nil {
    93  		reqres.WriteError(w, r, reqres.APIErrorServerError, "error marshalling JSON response", err)
    94  		return
    95  	}
    96  
    97  	w.Header().Set("Content-Type", "application/json")
    98  	w.WriteHeader(http.StatusOK)
    99  }
   100  
   101  func (h *appsHandler) webappTemplate(ctx context.Context, id *ocmpb.ShareId) (string, error) {
   102  	gc, err := h.gatewaySelector.Next()
   103  	if err != nil {
   104  		return "", err
   105  	}
   106  	res, err := gc.GetReceivedOCMShare(ctx, &ocmpb.GetReceivedOCMShareRequest{
   107  		Ref: &ocmpb.ShareReference{
   108  			Spec: &ocmpb.ShareReference_Id{
   109  				Id: id,
   110  			},
   111  		},
   112  	})
   113  	if err != nil {
   114  		return "", err
   115  	}
   116  	if res.Status.Code != rpcv1beta1.Code_CODE_OK {
   117  		if res.Status.Code == rpcv1beta1.Code_CODE_NOT_FOUND {
   118  			return "", errtypes.NotFound(res.Status.Message)
   119  		}
   120  		return "", errtypes.InternalError(res.Status.Message)
   121  	}
   122  
   123  	webapp, ok := getWebappProtocol(res.Share.Protocols)
   124  	if !ok {
   125  		return "", errtypes.BadRequest("share does not contain webapp protocol")
   126  	}
   127  
   128  	return webapp.UriTemplate, nil
   129  }
   130  
   131  func getWebappProtocol(protocols []*ocmpb.Protocol) (*ocmpb.WebappProtocol, bool) {
   132  	for _, p := range protocols {
   133  		if t, ok := p.Term.(*ocmpb.Protocol_WebappOptions); ok {
   134  			return t.WebappOptions, true
   135  		}
   136  	}
   137  	return nil, false
   138  }
   139  
   140  func resolveTemplate(template string, rel string) string {
   141  	// the template is of type "https://open-cloud-mesh.org/s/share-hash/{relative-path-to-shared-resource}"
   142  	return strings.Replace(template, "{relative-path-to-shared-resource}", rel, 1)
   143  }