Skip to content

Commit 8cf76ad

Browse files
committed
ocm: fixed discovery
1 parent 35b14f4 commit 8cf76ad

File tree

4 files changed

+88
-29
lines changed

4 files changed

+88
-29
lines changed

internal/http/services/opencloudmesh/ocmd/client.go

+34-15
Original file line numberDiff line numberDiff line change
@@ -70,37 +70,56 @@ func NewClient(timeout time.Duration, insecure bool) *OCMClient {
7070
// Discover returns a number of properties used to discover the capabilities offered by a remote cloud storage.
7171
// https://cs3org.github.io/OCM-API/docs.html?branch=develop&repo=OCM-API&user=cs3org#/paths/~1ocm-provider/get
7272
func (c *OCMClient) Discover(ctx context.Context, endpoint string) (*wellknown.OcmDiscoveryData, error) {
73-
url, err := url.JoinPath(endpoint, "/ocm-provider")
73+
log := appctx.GetLogger(ctx)
74+
75+
remoteurl, _ := url.JoinPath(endpoint, "/.well-known/ocm")
76+
body, err := c.discover(ctx, remoteurl)
77+
if err != nil || len(body) == 0 {
78+
log.Debug().Err(err).Str("sender", remoteurl).Str("response", string(body)).Msg("invalid or empty response, falling back to legacy discovery")
79+
remoteurl, _ := url.JoinPath(endpoint, "/ocm-provider") // legacy discovery endpoint
80+
81+
body, err = c.discover(ctx, remoteurl)
82+
if err != nil || len(body) == 0 {
83+
log.Warn().Err(err).Str("sender", remoteurl).Str("response", string(body)).Msg("invalid or empty response")
84+
return nil, errtypes.BadRequest("Invalid response on OCM discovery")
85+
}
86+
}
87+
88+
var disco wellknown.OcmDiscoveryData
89+
err = json.Unmarshal(body, &disco)
7490
if err != nil {
75-
return nil, err
91+
log.Warn().Err(err).Str("sender", remoteurl).Str("response", string(body)).Msg("malformed response")
92+
return nil, errtypes.BadRequest("Invalid payload on OCM discovery")
7693
}
7794

95+
log.Debug().Str("sender", remoteurl).Any("response", disco).Msg("discovery response")
96+
return &disco, nil
97+
}
98+
99+
func (c *OCMClient) discover(ctx context.Context, url string) ([]byte, error) {
100+
log := appctx.GetLogger(ctx)
101+
78102
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
79103
if err != nil {
80-
return nil, errors.Wrap(err, "error creating request")
104+
return nil, errors.Wrap(err, "error creating OCM discovery request")
81105
}
82106
req.Header.Set("Content-Type", "application/json")
83107

84108
resp, err := c.client.Do(req)
85109
if err != nil {
86-
return nil, errors.Wrap(err, "error doing request")
110+
return nil, errors.Wrap(err, "error doing OCM discovery request")
87111
}
88112
defer resp.Body.Close()
89-
90-
body, err := io.ReadAll(resp.Body)
91-
if err != nil {
92-
return nil, err
113+
if resp.StatusCode != http.StatusOK {
114+
log.Warn().Str("sender", url).Any("response", resp).Int("status", resp.StatusCode).Msg("discovery returned")
115+
return nil, errtypes.BadRequest("Remote does not offer a valid OCM discovery endpoint")
93116
}
94117

95-
var disco wellknown.OcmDiscoveryData
96-
err = json.Unmarshal(body, &disco)
118+
body, err := io.ReadAll(resp.Body)
97119
if err != nil {
98-
log := appctx.GetLogger(ctx)
99-
log.Warn().Str("sender", endpoint).Str("response", string(body)).Msg("malformed response")
100-
return nil, errtypes.InternalError("Invalid payload on OCM discovery")
120+
return nil, errors.Wrap(err, "malformed remote OCM discovery")
101121
}
102-
103-
return &disco, nil
122+
return body, nil
104123
}
105124

106125
// NewShare sends a new OCM share to the remote system.

internal/http/services/opencloudmesh/ocmd/invites.go

+1
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ func (h *invitesHandler) AcceptInvite(w http.ResponseWriter, r *http.Request) {
6060
reqres.WriteError(w, r, reqres.APIErrorInvalidParameter, "missing parameters in request", err)
6161
return
6262
}
63+
log.Info().Any("req", req).Msg("OCM /invite-accepted request received")
6364

6465
if req.Token == "" || req.UserID == "" || req.RecipientProvider == "" {
6566
reqres.WriteError(w, r, reqres.APIErrorInvalidParameter, "token, userID and recipiendProvider must not be null", nil)

internal/http/services/opencloudmesh/ocmd/shares.go

+52-14
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import (
2323
"fmt"
2424
"mime"
2525
"net/http"
26-
"path/filepath"
26+
"net/url"
2727
"strings"
2828
"time"
2929

@@ -63,8 +63,7 @@ func (h *sharesHandler) init(c *config) error {
6363
return nil
6464
}
6565

66-
// CreateShare sends all the informations to the consumer needed to start
67-
// synchronization between the two services.
66+
// CreateShare implements the OCM /shares call.
6867
func (h *sharesHandler) CreateShare(w http.ResponseWriter, r *http.Request) {
6968
ctx := r.Context()
7069
log := appctx.GetLogger(ctx)
@@ -73,6 +72,7 @@ func (h *sharesHandler) CreateShare(w http.ResponseWriter, r *http.Request) {
7372
reqres.WriteError(w, r, reqres.APIErrorInvalidParameter, err.Error(), nil)
7473
return
7574
}
75+
log.Info().Any("req", req).Msg("OCM /shares request received")
7676

7777
_, meshProvider, err := getIDAndMeshProvider(req.Sender)
7878
log.Debug().Msgf("Determined Mesh Provider '%s' from req.Sender '%s'", meshProvider, req.Sender)
@@ -161,6 +161,7 @@ func (h *sharesHandler) CreateShare(w http.ResponseWriter, r *http.Request) {
161161
}
162162
}
163163

164+
log.Info().Any("req", createShareReq).Msg("CreateOCMCoreShare payload")
164165
createShareResp, err := h.gatewayClient.CreateOCMCoreShare(ctx, createShareReq)
165166
if err != nil {
166167
reqres.WriteError(w, r, reqres.APIErrorServerError, "error creating ocm share", err)
@@ -210,10 +211,10 @@ func getCreateShareRequest(r *http.Request) (*NewShareRequest, error) {
210211
contentType, _, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
211212
if err == nil && contentType == "application/json" {
212213
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
213-
return nil, err
214+
return nil, errors.Wrap(err, "malformed OCM /shares request")
214215
}
215216
} else {
216-
return nil, errors.New("OCM /share request payload not recognised")
217+
return nil, errors.New("malformed OCM /shares request payload")
217218
}
218219
// validate the request
219220
if err := validate.Struct(req); err != nil {
@@ -249,40 +250,77 @@ func getAndResolveProtocols(p Protocols, r *http.Request) ([]*ocm.Protocol, erro
249250
protos := make([]*ocm.Protocol, 0, len(p))
250251
for _, data := range p {
251252
ocmProto := data.ToOCMProtocol()
253+
protocolName := GetProtocolName(data)
254+
var uri string
255+
var isLocalhost bool
256+
257+
switch protocolName {
258+
case "webdav":
259+
uri = ocmProto.GetWebdavOptions().Uri
260+
isLocalhost = strings.Contains(uri, "localhost")
261+
case "webapp":
262+
uri = ocmProto.GetWebappOptions().UriTemplate
263+
isLocalhost = strings.Contains(uri, "localhost")
264+
}
265+
252266
// Irrespective from the presence of a full `uri` in the payload (deprecated), resolve the remote root
253-
remoteRoot, err := discoverOcmRoot(r, GetProtocolName(data))
267+
// yet skip this if the remote is localhost (for integration tests)
268+
if isLocalhost {
269+
protos = append(protos, ocmProto)
270+
continue
271+
}
272+
remoteRoot, err := discoverOcmRoot(r, protocolName)
254273
if err != nil {
255274
return nil, err
256275
}
257-
if GetProtocolName(data) == "webdav" {
258-
ocmProto.GetWebdavOptions().Uri = filepath.Join(remoteRoot, ocmProto.GetWebdavOptions().SharedSecret)
259-
} else if GetProtocolName(data) == "webapp" {
260-
// ocmProto.GetWebappOptions().Uri = filepath.Join(remoteRoot, ocmProto.GetWebappOptions().SharedSecret) -> this is OCM 1.2
276+
uri, _ = url.JoinPath(remoteRoot, uri[strings.LastIndex(uri, "/")+1:])
277+
278+
switch protocolName {
279+
case "webdav":
280+
ocmProto.GetWebdavOptions().Uri = uri
281+
case "webapp":
282+
ocmProto.GetWebappOptions().UriTemplate = uri
261283
}
262284
protos = append(protos, ocmProto)
263285
}
286+
264287
return protos, nil
265288
}
266289

290+
267291
func discoverOcmRoot(r *http.Request, proto string) (string, error) {
268292
// implements the OCM discovery logic to fetch the root at the remote host that sent the share for the given proto, see
269293
// https://cs3org.github.io/OCM-API/docs.html?branch=v1.1.0&repo=OCM-API&user=cs3org#/paths/~1ocm-provider/get
270294
ctx := r.Context()
271295
log := appctx.GetLogger(ctx)
272-
log.Debug().Str("sender", r.Host).Msg("received OCM share, attempting to discover sender endpoint")
296+
297+
// assume the sender host is either given in the usual reverse proxy headers or as RemoteAddr, and that the
298+
// remote end listens on https regardless if the incoming connection got its TLS terminated upstream of us
299+
senderURL := r.Header.Get("X-Real-Ip")
300+
if senderURL == "" {
301+
senderURL = r.Header.Get("X-Forwarded-For")
302+
}
303+
if senderURL == "" {
304+
senderURL = r.RemoteAddr
305+
}
306+
senderURL = "https://" + senderURL[:strings.LastIndex(senderURL, ":")]
307+
log.Debug().Str("sender", senderURL).Msg("received OCM share, attempting to discover sender endpoint")
273308

274309
ocmClient := NewClient(time.Duration(10)*time.Second, true)
275-
ocmCaps, err := ocmClient.Discover(ctx, r.Host)
310+
ocmCaps, err := ocmClient.Discover(ctx, senderURL)
276311
if err != nil {
277-
log.Warn().Str("sender", r.Host).Err(err).Msg("failed to discover OCM sender")
312+
log.Warn().Str("sender", senderURL).Err(err).Msg("failed to discover OCM sender")
278313
return "", err
279314
}
280315
for _, t := range ocmCaps.ResourceTypes {
281316
protoRoot, ok := t.Protocols[proto]
282317
if ok {
283318
// assume the first resourceType that exposes a root is OK to use: as a matter of fact,
284319
// no implementation exists yet that exposes multiple resource types with different roots.
285-
return filepath.Join(ocmCaps.Endpoint, protoRoot), nil
320+
u, _ := url.Parse(ocmCaps.Endpoint)
321+
u.Path = protoRoot
322+
u.RawQuery = ""
323+
return u.String(), nil
286324
}
287325
}
288326

internal/http/services/owncloud/ocdav/dav.go

+1
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@ func (h *DavHandler) Handler(s *svc) http.Handler {
184184

185185
c, err := pool.GetGatewayServiceClient(pool.Endpoint(s.c.GatewaySvc))
186186
if err != nil {
187+
log.Error().Err(err).Msg("error getting gateway during OCM authentication")
187188
w.WriteHeader(http.StatusNotFound)
188189
return
189190
}

0 commit comments

Comments
 (0)