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 }