github.com/cs3org/reva/v2@v2.27.7/internal/http/services/wellknown/ocm.go (about) 1 // Copyright 2018-2024 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 wellknown 20 21 import ( 22 "encoding/json" 23 "net/http" 24 "net/url" 25 "path/filepath" 26 27 "github.com/cs3org/reva/v2/pkg/appctx" 28 ) 29 30 const OCMAPIVersion = "1.1.0" 31 32 type OcmProviderConfig struct { 33 OCMPrefix string `docs:"ocm;The prefix URL where the OCM API is served." mapstructure:"ocm_prefix"` 34 Endpoint string `docs:"This host's full URL. If it's not configured, it is assumed OCM is not available." mapstructure:"endpoint"` 35 Provider string `docs:"reva;A friendly name that defines this service." mapstructure:"provider"` 36 WebdavRoot string `docs:"/remote.php/dav/ocm;The root URL of the WebDAV endpoint to serve OCM shares." mapstructure:"webdav_root"` 37 WebappRoot string `docs:"/external/sciencemesh;The root URL to serve Web apps via OCM." mapstructure:"webapp_root"` 38 EnableWebapp bool `docs:"false;Whether web apps are enabled in OCM shares." mapstructure:"enable_webapp"` 39 EnableDatatx bool `docs:"false;Whether data transfers are enabled in OCM shares." mapstructure:"enable_datatx"` 40 } 41 42 type OcmDiscoveryData struct { 43 Enabled bool `json:"enabled" xml:"enabled"` 44 APIVersion string `json:"apiVersion" xml:"apiVersion"` 45 Endpoint string `json:"endPoint" xml:"endPoint"` 46 Provider string `json:"provider" xml:"provider"` 47 ResourceTypes []resourceTypes `json:"resourceTypes" xml:"resourceTypes"` 48 Capabilities []string `json:"capabilities" xml:"capabilities"` 49 } 50 51 type resourceTypes struct { 52 Name string `json:"name"` 53 ShareTypes []string `json:"shareTypes"` 54 Protocols map[string]string `json:"protocols"` 55 } 56 57 type wkocmHandler struct { 58 data *OcmDiscoveryData 59 } 60 61 func (c *OcmProviderConfig) ApplyDefaults() { 62 if c.OCMPrefix == "" { 63 c.OCMPrefix = "ocm" 64 } 65 if c.Provider == "" { 66 c.Provider = "reva" 67 } 68 if c.WebdavRoot == "" { 69 c.WebdavRoot = "/remote.php/dav/ocm/" 70 } 71 if c.WebdavRoot[len(c.WebdavRoot)-1:] != "/" { 72 c.WebdavRoot += "/" 73 } 74 if c.WebappRoot == "" { 75 c.WebappRoot = "/external/sciencemesh/" 76 } 77 if c.WebappRoot[len(c.WebappRoot)-1:] != "/" { 78 c.WebappRoot += "/" 79 } 80 } 81 82 func (h *wkocmHandler) init(c *OcmProviderConfig) { 83 // generates the (static) data structure to be exposed by /.well-known/ocm: 84 // first prepare an empty and disabled payload 85 c.ApplyDefaults() 86 d := &OcmDiscoveryData{} 87 d.Enabled = false 88 d.Endpoint = "" 89 d.APIVersion = OCMAPIVersion 90 d.Provider = c.Provider 91 d.ResourceTypes = []resourceTypes{{ 92 Name: "file", 93 ShareTypes: []string{}, 94 Protocols: map[string]string{}, 95 }} 96 d.Capabilities = []string{} 97 98 if c.Endpoint == "" { 99 h.data = d 100 return 101 } 102 103 endpointURL, err := url.Parse(c.Endpoint) 104 if err != nil { 105 h.data = d 106 return 107 } 108 109 // now prepare the enabled one 110 d.Enabled = true 111 d.Endpoint, _ = url.JoinPath(c.Endpoint, c.OCMPrefix) 112 rtProtos := map[string]string{} 113 // webdav is always enabled 114 rtProtos["webdav"] = filepath.Join(endpointURL.Path, c.WebdavRoot) 115 if c.EnableWebapp { 116 rtProtos["webapp"] = filepath.Join(endpointURL.Path, c.WebappRoot) 117 } 118 if c.EnableDatatx { 119 rtProtos["datatx"] = filepath.Join(endpointURL.Path, c.WebdavRoot) 120 } 121 d.ResourceTypes = []resourceTypes{{ 122 Name: "file", // so far we only support `file` 123 ShareTypes: []string{"user"}, // so far we only support `user` 124 Protocols: rtProtos, // expose the protocols as per configuration 125 }} 126 // for now we hardcode the capabilities, as this is currently only advisory 127 d.Capabilities = []string{"/invite-accepted"} 128 h.data = d 129 } 130 131 // This handler implements the OCM discovery endpoint specified in 132 // https://cs3org.github.io/OCM-API/docs.html?repo=OCM-API&user=cs3org#/paths/~1ocm-provider/get 133 func (h *wkocmHandler) Ocm(w http.ResponseWriter, r *http.Request) { 134 log := appctx.GetLogger(r.Context()) 135 w.Header().Set("Content-Type", "application/json") 136 w.WriteHeader(http.StatusOK) 137 if r.UserAgent() == "Nextcloud Server Crawler" { 138 // Nextcloud decided to only support OCM 1.0 and 1.1, not any 1.x as per SemVer. See 139 // https://github.com/nextcloud/server/pull/39574#issuecomment-1679191188 140 h.data.APIVersion = "1.1" 141 } else { 142 h.data.APIVersion = OCMAPIVersion 143 } 144 indented, _ := json.MarshalIndent(h.data, "", " ") 145 if _, err := w.Write(indented); err != nil { 146 log.Err(err).Msg("Error writing to ResponseWriter") 147 } 148 }