Skip to content

Commit

Permalink
Introduced a .well-known endpoint for OCM
Browse files Browse the repository at this point in the history
  • Loading branch information
glpatcern committed May 16, 2024
1 parent 5a82311 commit d22f762
Show file tree
Hide file tree
Showing 14 changed files with 139 additions and 73 deletions.
2 changes: 1 addition & 1 deletion examples/cernbox/cernbox.toml
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@ sender_mail = "sciencemesh@{{ vars.provider_domain }}"
smtp_server = "smtp.{{ vars.provider_domain }}"
smtp_port = 25

[http.services.ocmprovider]
[http.services.wellknown.ocmprovider]
address = ":443"
ocm_prefix = "ocm"
provider = "Reva for CERNBox"
Expand Down
2 changes: 1 addition & 1 deletion examples/ocm/server-1.toml
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ driver = "ocmreceived"
address = "0.0.0.0:8080"
expose_recipient_display_name = true

[http.services.ocmprovider]
[http.services.wellknown.ocmprovider]
ocm_prefix = "ocm"
provider = "reva@cern"
endpoint = "http://localhost:{{ http.services.ocm.address.port }}"
Expand Down
2 changes: 1 addition & 1 deletion examples/ocm/server-2.toml
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ driver = "ocmreceived"
address = "0.0.0.0:80"
expose_recipient_display_name = true

[http.services.ocmprovider]
[http.services.wellknown.ocmprovider]
ocm_prefix = "ocm"
provider = "reva@cesnet"
endpoint = "http://localhost:{{ http.services.ocm.address.port }}"
Expand Down
2 changes: 1 addition & 1 deletion examples/sciencemesh/sciencemesh.toml
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ sender_mail = "sciencemesh@{{ vars.provider_domain }}"
smtp_server = "smtp.{{ vars.provider_domain }}"
smtp_port = 25

[http.services.ocmprovider]
[http.services.wellknown.ocmprovider]
address = ":443"
ocm_prefix = "ocm"
provider = "Reva for ownCloud/Nextcloud"
Expand Down
2 changes: 1 addition & 1 deletion examples/standalone/standalone.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,6 @@
[http.services.dataprovider]
[http.services.prometheus]
[http.services.ocm]
[http.services.ocmprovider]
[http.services.wellknown.ocmprovider]
[http.services.ocdav]
[http.services.ocs]
2 changes: 1 addition & 1 deletion examples/storage-references/gateway.toml
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ app_url = "https://your-collabora-server.org:9980"
[http.services.datagateway]
[http.services.prometheus]
[http.services.ocm]
[http.services.ocmprovider]
[http.services.wellknown.ocmprovider]
[http.services.ocdav]
[http.services.ocs]

Expand Down
2 changes: 1 addition & 1 deletion examples/two-server-setup/gateway-1.toml
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ address = "0.0.0.0:19001"
[http.services.datagateway]
[http.services.prometheus]
[http.services.ocm]
[http.services.ocmprovider]
[http.services.wellknown.ocmprovider]
provider = "Reva-Server-1"
endpoint = "http://localhost:19001"
enable_webapp = true
Expand Down
2 changes: 1 addition & 1 deletion examples/two-server-setup/gateway-2.toml
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ address = "0.0.0.0:29001"
[http.services.datagateway]
[http.services.prometheus]
[http.services.ocm]
[http.services.ocmprovider]
[http.services.wellknown.ocmprovider]
provider = "Reva-Server-2"
endpoint = "http://localhost:29001"
enable_webapp = true
Expand Down
2 changes: 1 addition & 1 deletion internal/http/services/loader/loader.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,13 @@ import (
_ "github.com/cs3org/reva/internal/http/services/helloworld"
_ "github.com/cs3org/reva/internal/http/services/metrics"
_ "github.com/cs3org/reva/internal/http/services/opencloudmesh/ocmd"
_ "github.com/cs3org/reva/internal/http/services/opencloudmesh/ocmprovider"
_ "github.com/cs3org/reva/internal/http/services/owncloud/ocdav"
_ "github.com/cs3org/reva/internal/http/services/owncloud/ocs"
_ "github.com/cs3org/reva/internal/http/services/pingpong"
_ "github.com/cs3org/reva/internal/http/services/plugins"
_ "github.com/cs3org/reva/internal/http/services/pprof"
_ "github.com/cs3org/reva/internal/http/services/preferences"
_ "github.com/cs3org/reva/internal/http/services/prometheus"
_ "github.com/cs3org/reva/internal/http/services/wellknown"
// Add your own service here.
)
6 changes: 3 additions & 3 deletions internal/http/services/opencloudmesh/ocmd/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import (
"net/url"
"time"

"github.com/cs3org/reva/internal/http/services/opencloudmesh/ocmprovider"
"github.com/cs3org/reva/internal/http/services/wellknown"
"github.com/cs3org/reva/pkg/appctx"
"github.com/cs3org/reva/pkg/errtypes"
"github.com/pkg/errors"
Expand Down Expand Up @@ -74,7 +74,7 @@ func NewClient(timeout time.Duration, insecure bool) *OCMClient {

// Discover returns a number of properties used to discover the capabilities offered by a remote cloud storage.
// https://cs3org.github.io/OCM-API/docs.html?branch=develop&repo=OCM-API&user=cs3org#/paths/~1ocm-provider/get
func (c *OCMClient) Discover(ctx context.Context, endpoint string) (*ocmprovider.DiscoveryData, error) {
func (c *OCMClient) Discover(ctx context.Context, endpoint string) (*wellknown.OcmDiscoveryData, error) {
url, err := url.JoinPath(endpoint, "/ocm-provider")
if err != nil {
return nil, err
Expand All @@ -97,7 +97,7 @@ func (c *OCMClient) Discover(ctx context.Context, endpoint string) (*ocmprovider
return nil, err
}

var disco ocmprovider.DiscoveryData
var disco wellknown.OcmDiscoveryData
err = json.Unmarshal(body, &disco)
if err != nil {
log := appctx.GetLogger(ctx)
Expand Down
6 changes: 5 additions & 1 deletion internal/http/services/owncloud/ocdav/ocdav.go
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ func (s *svc) Close() error {
}

func (s *svc) Unprotected() []string {
return []string{"/status.php", "/remote.php/dav/public-files/", "/apps/files/", "/index.php/f/", "/index.php/s/", "/s/", "/remote.php/dav/ocm/"}
return []string{"/status.php", "/remote.php/dav/public-files/", "/apps/files/", "/index.php/f/", "/index.php/s/", "/s/", "/remote.php/dav/ocm/", "/ocm-provider"}
}

func (s *svc) Handler() http.Handler {
Expand Down Expand Up @@ -255,6 +255,10 @@ func (s *svc) Handler() http.Handler {
http.Redirect(w, r, rURL, http.StatusMovedPermanently)
return
}
case "ocm-provider":
// this is to support the current/legacy discovery endpoint for OCM
http.Redirect(w, r, "/.well-known/ocm", http.StatusMovedPermanently)
return
}
switch head {
// the old `/webdav` endpoint uses remote.php/webdav/$path
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,27 +16,20 @@
// granted to it by virtue of its status as an Intergovernmental Organization
// or submit itself to any jurisdiction.

package ocmprovider
package wellknown

import (
"context"
"encoding/json"
"net/http"
"net/url"
"path/filepath"

"github.com/cs3org/reva/pkg/appctx"
"github.com/cs3org/reva/pkg/rhttp/global"
"github.com/cs3org/reva/pkg/utils/cfg"
)

const OCMAPIVersion = "1.1.0"

func init() {
global.Register("ocmprovider", New)
}

type config struct {
type OcmProviderConfig struct {
OCMPrefix string `docs:"ocm;The prefix URL where the OCM API is served." mapstructure:"ocm_prefix"`
Endpoint string `docs:"This host's full URL. If it's not configured, it is assumed OCM is not available." mapstructure:"endpoint"`
Provider string `docs:"reva;A friendly name that defines this service." mapstructure:"provider"`
Expand All @@ -46,7 +39,7 @@ type config struct {
EnableDatatx bool `docs:"false;Whether data transfers are enabled in OCM shares." mapstructure:"enable_datatx"`
}

type DiscoveryData struct {
type OcmDiscoveryData struct {
Enabled bool `json:"enabled" xml:"enabled"`
APIVersion string `json:"apiVersion" xml:"apiVersion"`
Endpoint string `json:"endPoint" xml:"endPoint"`
Expand All @@ -61,11 +54,11 @@ type resourceTypes struct {
Protocols map[string]string `json:"protocols"`
}

type svc struct {
data *DiscoveryData
type wkocmHandler struct {
data *OcmDiscoveryData
}

func (c *config) ApplyDefaults() {
func (c *OcmProviderConfig) ApplyDefaults() {
if c.OCMPrefix == "" {
c.OCMPrefix = "ocm"
}
Expand All @@ -86,10 +79,11 @@ func (c *config) ApplyDefaults() {
}
}

func (c *config) prepare() *DiscoveryData {
// generates the (static) data structure to be exposed by /ocm-provider:
func (h *wkocmHandler) init(c *OcmProviderConfig) {
// generates the (static) data structure to be exposed by /.well-known/ocm:
// first prepare an empty and disabled payload
d := &DiscoveryData{}
c.ApplyDefaults()
d := &OcmDiscoveryData{}
d.Enabled = false
d.Endpoint = ""
d.APIVersion = OCMAPIVersion
Expand All @@ -102,12 +96,14 @@ func (c *config) prepare() *DiscoveryData {
d.Capabilities = []string{}

if c.Endpoint == "" {
return d
h.data = d
return
}

endpointURL, err := url.Parse(c.Endpoint)
if err != nil {
return d
h.data = d
return
}

// now prepare the enabled one
Expand All @@ -129,49 +125,24 @@ func (c *config) prepare() *DiscoveryData {
}}
// for now we hardcode the capabilities, as this is currently only advisory
d.Capabilities = []string{"/invite-accepted"}
return d
h.data = d
}

// New returns a new ocmprovider object, that implements
// the OCM discovery endpoint specified in
// This handler implements the OCM discovery endpoint specified in
// https://cs3org.github.io/OCM-API/docs.html?repo=OCM-API&user=cs3org#/paths/~1ocm-provider/get
func New(ctx context.Context, m map[string]interface{}) (global.Service, error) {
var c config
if err := cfg.Decode(m, &c); err != nil {
return nil, err
func (h *wkocmHandler) Ocm(w http.ResponseWriter, r *http.Request) {
log := appctx.GetLogger(r.Context())
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
if r.UserAgent() == "Nextcloud Server Crawler" {
// Nextcloud decided to only support OCM 1.0 and 1.1, not any 1.x as per SemVer. See
// https://github.com/nextcloud/server/pull/39574#issuecomment-1679191188
h.data.APIVersion = "1.1"
} else {
h.data.APIVersion = OCMAPIVersion
}
indented, _ := json.MarshalIndent(h.data, "", " ")
if _, err := w.Write(indented); err != nil {
log.Err(err).Msg("Error writing to ResponseWriter")
}
return &svc{data: c.prepare()}, nil
}

// Close performs cleanup.
func (s *svc) Close() error {
return nil
}

func (s *svc) Prefix() string {
// this is hardcoded as per OCM specifications
return "/ocm-provider"
}

func (s *svc) Unprotected() []string {
return []string{"/"}
}

func (s *svc) Handler() http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
log := appctx.GetLogger(r.Context())
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
if r.UserAgent() == "Nextcloud Server Crawler" {
// Nextcloud decided to only support OCM 1.0 and 1.1, not any 1.x as per SemVer. See
// https://github.com/nextcloud/server/pull/39574#issuecomment-1679191188
s.data.APIVersion = "1.1"
} else {
s.data.APIVersion = OCMAPIVersion
}
indented, _ := json.MarshalIndent(s.data, "", " ")
if _, err := w.Write(indented); err != nil {
log.Err(err).Msg("Error writing to ResponseWriter")
}
})
}
91 changes: 91 additions & 0 deletions internal/http/services/wellknown/wellknown.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
// Copyright 2018-2024 CERN
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// In applying this license, CERN does not waive the privileges and immunities
// granted to it by virtue of its status as an Intergovernmental Organization
// or submit itself to any jurisdiction.

package wellknown

import (
"context"
"net/http"

"github.com/cs3org/reva/pkg/appctx"
"github.com/cs3org/reva/pkg/rhttp/global"
"github.com/cs3org/reva/pkg/utils/cfg"
"github.com/go-chi/chi/v5"
)

func init() {
global.Register("wellknown", New)
}

type svc struct {
router chi.Router
Conf *config
}

type config struct {
OCMProvider OcmProviderConfig `mapstructure:"ocmprovider"`
}

// New returns a new wellknown object.
func New(ctx context.Context, m map[string]interface{}) (global.Service, error) {
var c config
if err := cfg.Decode(m, &c); err != nil {
return nil, err
}

r := chi.NewRouter()
s := &svc{
router: r,
Conf: &c,
}
if err := s.routerInit(); err != nil {
return nil, err
}

return s, nil
}

func (s *svc) routerInit() error {
wkocmHandler := new(wkocmHandler)
wkocmHandler.init(&s.Conf.OCMProvider)
s.router.Get("/ocm", wkocmHandler.Ocm)
return nil
}

func (s *svc) Close() error {
return nil
}

func (s *svc) Prefix() string {
return "/.well-known"
}

func (s *svc) Unprotected() []string {
return []string{"/", "/ocm"}
}

func (s *svc) Handler() http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
log := appctx.GetLogger(r.Context())
log.Debug().Str("path", r.URL.Path).Msg(".well-known routing")

// unset raw path, otherwise chi uses it to route and then fails to match percent encoded path segments
r.URL.RawPath = ""
s.router.ServeHTTP(w, r)
})
}
2 changes: 1 addition & 1 deletion pkg/ocm/provider/authorizer/open/open.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ func (a *authorizer) GetInfoByDomain(ctx context.Context, domain string) (*ocmpr
}

// not yet known: try to discover the remote OCM endpoint
ocmClient := client.NewOCMClient(time.Duration(10)*time.Second, true)
ocmClient := client.NewClient(time.Duration(10)*time.Second, true)
ocmCaps, err := ocmClient.Discover(ctx, domain)
if err != nil {
return nil, errors.Wrap(err, "error probing OCM services at remote server")
Expand Down

0 comments on commit d22f762

Please sign in to comment.