Skip to content

Commit

Permalink
OCM: support bearer token access and other refactoring (#4670)
Browse files Browse the repository at this point in the history
* Removed dead code

* changelog

* ocm: support bearer token access

* ocm: adapted handling of shares

* Fixed propfind

* Cosmetic changes

* Use open provider authorizer for OCM in cernbox example, and make it truly open

* Refactored OCM client into OCMD package

* Updated changelog

* Introduced a .well-known endpoint for OCM

* Attempt to support both OCM 1.0 and 1.1 when accessing remote shares

* Revert "Attempt to support both OCM 1.0 and 1.1 when accessing remote shares"

This cannot work, by construction we can't use basic and bearer auth.
We could instead store the share type and decide accordingly,
but for now Reva only remains capable to connect to OCM 1.1 systems.

This reverts commit e6ab385.

* Removed unused config
  • Loading branch information
glpatcern authored Jul 30, 2024
1 parent d754032 commit dde65a4
Show file tree
Hide file tree
Showing 28 changed files with 387 additions and 419 deletions.
8 changes: 8 additions & 0 deletions changelog/unreleased/ocm-access.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
Enhancement: ocm: support bearer token access

This PR adds support for accessing remote OCM 1.1 shares via bearer token,
as opposed to having the shared secret in the URL only.
In addition, the OCM client package is now part of the OCMD server package,
and the Discover methods have been all consolidated in one place.

https://github.com/cs3org/reva/pull/4670
7 changes: 3 additions & 4 deletions examples/cernbox/cernbox.toml
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,8 @@ webapp_template = "{{ vars.external_reva_endpoint }}/external/sciencemesh/{{.Tok
file = "{{ vars.ocmshares_json_file }}"

[grpc.services.ocmproviderauthorizer]
driver = "json"
driver = "open" # pure OCM, all remote shares are accepted
#driver = "json" # to enable sciencemesh

[grpc.services.ocmproviderauthorizer.drivers.json]
# this is used by the docker-based test deployment, not in production
Expand Down Expand Up @@ -279,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 Expand Up @@ -392,8 +393,6 @@ insecure = true
[http.services.prometheus]
address = ":443"

[http.services.sysinfo]

#[http.services.ui]
#address = ":443"

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
4 changes: 1 addition & 3 deletions 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 Expand Up @@ -284,7 +284,5 @@ metrics_data_driver_type = "json"
metrics_data_location = "/etc/revad/metrics.json"
metrics_record_interval = 5000

[http.services.sysinfo]

[http.middlewares.cors]
[http.middlewares.log]
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
4 changes: 3 additions & 1 deletion internal/grpc/services/ocmcore/ocmcore.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@

package ocmcore

// This package implements the core OCM API for receiving external shares from remote EFSS systems.

import (
"context"
"fmt"
Expand Down Expand Up @@ -102,7 +104,7 @@ func (s *service) UnprotectedEndpoints() []string {
return []string{"/cs3.ocm.core.v1beta1.OcmCoreAPI/CreateOCMCoreShare"}
}

// CreateOCMCoreShare is called when an OCM request comes into this reva instance from.
// CreateOCMCoreShare is called when a remote OCM request comes into this reva instance.
func (s *service) CreateOCMCoreShare(ctx context.Context, req *ocmcore.CreateOCMCoreShareRequest) (*ocmcore.CreateOCMCoreShareResponse, error) {
if req.ShareType != ocm.ShareType_SHARE_TYPE_USER {
return nil, errtypes.NotSupported("share type not supported")
Expand Down
23 changes: 10 additions & 13 deletions internal/grpc/services/ocminvitemanager/ocminvitemanager.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ import (
invitepb "github.com/cs3org/go-cs3apis/cs3/ocm/invite/v1beta1"
ocmprovider "github.com/cs3org/go-cs3apis/cs3/ocm/provider/v1beta1"
rpcv1beta1 "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
"github.com/cs3org/reva/internal/http/services/opencloudmesh/ocmd"
"github.com/cs3org/reva/pkg/appctx"
"github.com/cs3org/reva/pkg/errtypes"
"github.com/cs3org/reva/pkg/ocm/client"
"github.com/cs3org/reva/pkg/ocm/invite"
"github.com/cs3org/reva/pkg/ocm/invite/repository/registry"
"github.com/cs3org/reva/pkg/plugin"
Expand Down Expand Up @@ -66,7 +66,7 @@ type config struct {
type service struct {
conf *config
repo invite.Repository
ocmClient *client.OCMClient
ocmClient *ocmd.OCMClient
}

func (c *config) ApplyDefaults() {
Expand Down Expand Up @@ -110,12 +110,9 @@ func New(ctx context.Context, m map[string]interface{}) (rgrpc.Service, error) {
}

service := &service{
conf: &c,
repo: repo,
ocmClient: client.New(&client.Config{
Timeout: time.Duration(c.OCMClientTimeout) * time.Second,
Insecure: c.OCMClientInsecure,
}),
conf: &c,
repo: repo,
ocmClient: ocmd.NewClient(time.Duration(c.OCMClientTimeout)*time.Second, c.OCMClientInsecure),
}
return service, nil
}
Expand Down Expand Up @@ -166,7 +163,7 @@ func (s *service) ForwardInvite(ctx context.Context, req *invitepb.ForwardInvite
return nil, err
}

remoteUser, err := s.ocmClient.InviteAccepted(ctx, ocmEndpoint, &client.InviteAcceptedRequest{
remoteUser, err := s.ocmClient.InviteAccepted(ctx, ocmEndpoint, &ocmd.InviteAcceptedRequest{
Token: req.InviteToken.GetToken(),
RecipientProvider: s.conf.ProviderDomain,
UserID: user.GetId().GetOpaqueId(),
Expand All @@ -175,19 +172,19 @@ func (s *service) ForwardInvite(ctx context.Context, req *invitepb.ForwardInvite
})
if err != nil {
switch {
case errors.Is(err, client.ErrTokenInvalid):
case errors.Is(err, ocmd.ErrTokenInvalid):
return &invitepb.ForwardInviteResponse{
Status: status.NewInvalid(ctx, "token not valid"),
}, nil
case errors.Is(err, client.ErrTokenNotFound):
case errors.Is(err, ocmd.ErrTokenNotFound):
return &invitepb.ForwardInviteResponse{
Status: status.NewNotFound(ctx, "token not found"),
}, nil
case errors.Is(err, client.ErrUserAlreadyAccepted):
case errors.Is(err, ocmd.ErrUserAlreadyAccepted):
return &invitepb.ForwardInviteResponse{
Status: status.NewAlreadyExists(ctx, err, err.Error()),
}, nil
case errors.Is(err, client.ErrServiceNotTrusted):
case errors.Is(err, ocmd.ErrServiceNotTrusted):
return &invitepb.ForwardInviteResponse{
Status: status.NewPermissionDenied(ctx, err, err.Error()),
}, nil
Expand Down
40 changes: 18 additions & 22 deletions internal/grpc/services/ocmshareprovider/ocmshareprovider.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@

package ocmshareprovider

// This package implements the OCM client API: it allows shares created on this Reva instance
// to be sent to a remote EFSS system via OCM.

import (
"context"
"fmt"
Expand All @@ -38,7 +41,6 @@ import (

"github.com/cs3org/reva/pkg/appctx"
"github.com/cs3org/reva/pkg/errtypes"
"github.com/cs3org/reva/pkg/ocm/client"
"github.com/cs3org/reva/pkg/ocm/share"
"github.com/cs3org/reva/pkg/ocm/share/repository/registry"
"github.com/cs3org/reva/pkg/plugin"
Expand Down Expand Up @@ -70,13 +72,13 @@ type config struct {
GatewaySVC string `mapstructure:"gatewaysvc" validate:"required"`
ProviderDomain string `docs:"The same domain registered in the provider authorizer" mapstructure:"provider_domain" validate:"required"`
WebDAVEndpoint string `mapstructure:"webdav_endpoint" validate:"required"`
WebappTemplate string `mapstructure:"webapp_template"`
WebappTemplate string `mapstructure:"webapp_template" validate:"required"`
}

type service struct {
conf *config
repo share.Repository
client *client.OCMClient
client *ocmd.OCMClient
gateway gateway.GatewayAPIClient
webappTmpl *template.Template
walker walker.Walker
Expand All @@ -89,9 +91,6 @@ func (c *config) ApplyDefaults() {
if c.ClientTimeout == 0 {
c.ClientTimeout = 10
}
if c.WebappTemplate == "" {
c.WebappTemplate = "https://cernbox.cern.ch/external/sciencemesh/{{.Token}}{relative-path-to-shared-resource}"
}

c.GatewaySVC = sharedconf.GetGatewaySVC(c.GatewaySVC)
}
Expand Down Expand Up @@ -119,11 +118,6 @@ func New(ctx context.Context, m map[string]interface{}) (rgrpc.Service, error) {
return nil, err
}

client := client.New(&client.Config{
Timeout: time.Duration(c.ClientTimeout) * time.Second,
Insecure: c.ClientInsecure,
})

gateway, err := pool.GetGatewayServiceClient(pool.Endpoint(c.GatewaySVC))
if err != nil {
return nil, err
Expand All @@ -135,10 +129,11 @@ func New(ctx context.Context, m map[string]interface{}) (rgrpc.Service, error) {
}
walker := walker.NewWalker(gateway)

ocmcl := ocmd.NewClient(time.Duration(c.ClientTimeout)*time.Second, c.ClientInsecure)
service := &service{
conf: &c,
repo: repo,
client: client,
client: ocmcl,
gateway: gateway,
webappTmpl: tpl,
walker: walker,
Expand Down Expand Up @@ -178,13 +173,14 @@ func getResourceType(info *providerpb.ResourceInfo) string {
return "unknown"
}

func (s *service) webdavURL(ctx context.Context, share *ocm.Share) string {
// the url is in the form of https://cernbox.cern.ch/remote.php/dav/ocm/token
p, _ := url.JoinPath(s.conf.WebDAVEndpoint, "/remote.php/dav/ocm", share.Token)
func (s *service) webdavURL(share *ocm.Share) string {
// the url is expected to be in the form https://ourserver/remote.php/dav/ocm/{ShareId}, see c.WebdavRoot in ocmprovider.go
// TODO(lopresti) take the root from http.services.wellknown.ocmprovider's config
p, _ := url.JoinPath(s.conf.WebDAVEndpoint, "/remote.php/dav/ocm", share.Id.OpaqueId)
return p
}

func (s *service) getWebdavProtocol(ctx context.Context, share *ocm.Share, m *ocm.AccessMethod_WebdavOptions) *ocmd.WebDAV {
func (s *service) getWebdavProtocol(share *ocm.Share, m *ocm.AccessMethod_WebdavOptions) *ocmd.WebDAV {
var perms []string
if m.WebdavOptions.Permissions.InitiateFileDownload {
perms = append(perms, "read")
Expand All @@ -195,7 +191,7 @@ func (s *service) getWebdavProtocol(ctx context.Context, share *ocm.Share, m *oc

return &ocmd.WebDAV{
Permissions: perms,
URL: s.webdavURL(ctx, share),
URL: s.webdavURL(share),
SharedSecret: share.Token,
}
}
Expand Down Expand Up @@ -233,7 +229,7 @@ func (s *service) getDataTransferProtocol(ctx context.Context, share *ocm.Share)
panic(err)
}
return &ocmd.Datatx{
SourceURI: s.webdavURL(ctx, share),
SourceURI: s.webdavURL(share),
Size: size,
}
}
Expand All @@ -248,7 +244,7 @@ func (s *service) getProtocols(ctx context.Context, share *ocm.Share) ocmd.Proto
for _, m := range share.AccessMethods {
switch t := m.Term.(type) {
case *ocm.AccessMethod_WebdavOptions:
p = append(p, s.getWebdavProtocol(ctx, share, t))
p = append(p, s.getWebdavProtocol(share, t))
case *ocm.AccessMethod_WebappOptions:
p = append(p, s.getWebappProtocol(share))
case *ocm.AccessMethod_TransferOptions:
Expand Down Expand Up @@ -323,7 +319,7 @@ func (s *service) CreateOCMShare(ctx context.Context, req *ocm.CreateOCMShareReq
}, nil
}

newShareReq := &client.NewShareRequest{
newShareReq := &ocmd.NewShareRequest{
ShareWith: formatOCMUser(req.Grantee.GetUserId()),
Name: ocmshare.Name,
ProviderID: ocmshare.Id.OpaqueId,
Expand All @@ -348,11 +344,11 @@ func (s *service) CreateOCMShare(ctx context.Context, req *ocm.CreateOCMShareReq
newShareRes, err := s.client.NewShare(ctx, ocmEndpoint, newShareReq)
if err != nil {
switch {
case errors.Is(err, client.ErrInvalidParameters):
case errors.Is(err, ocmd.ErrInvalidParameters):
return &ocm.CreateOCMShareResponse{
Status: status.NewInvalidArg(ctx, err.Error()),
}, nil
case errors.Is(err, client.ErrServiceNotTrusted):
case errors.Is(err, ocmd.ErrServiceNotTrusted):
return &ocm.CreateOCMShareResponse{
Status: status.NewInvalidArg(ctx, err.Error()),
}, nil
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.
)
Loading

0 comments on commit dde65a4

Please sign in to comment.