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  }