Skip to content

Commit

Permalink
Support OCM v1.0 schema but bail out if the options aren't empty
Browse files Browse the repository at this point in the history
  • Loading branch information
glpatcern committed Mar 31, 2023
1 parent b0e3e75 commit 423c0a8
Show file tree
Hide file tree
Showing 11 changed files with 122 additions and 54 deletions.
14 changes: 14 additions & 0 deletions changelog/unreleased/ocm-reconcile.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
Enhancement: Support OCM v1.0 schema

Following cs3org/cs3apis#206, we add the fields to ensure
backwards compatibility with OCM v1.0. However, if the
protocol.options undocumented object is not empty, we bail
out for now. Supporting interoperability with OCM v1.0
implementations (notably Nextcloud 25) may come in the future
if the undocumented options are fully reverse engineered.

Also, added viewMode to webapp protocol options
and adapted all SQL code and unit tests.

https://github.com/cs3org/cs3apis/pull/207
https://github.com/cs3org/reva/pull/3757
2 changes: 1 addition & 1 deletion internal/http/services/ocmd/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ type configHandler struct {
func (h *configHandler) init(c *config) {
h.c = c.Config
if h.c.APIVersion == "" {
h.c.APIVersion = "1.0-proposal1"
h.c.APIVersion = "1.1.0"
}
if h.c.Host == "" {
h.c.Host = "localhost"
Expand Down
24 changes: 21 additions & 3 deletions internal/http/services/ocmd/protocols.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
ocm "github.com/cs3org/go-cs3apis/cs3/sharing/ocm/v1beta1"
providerv1beta1 "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
ocmshare "github.com/cs3org/reva/pkg/ocm/share"
utils "github.com/cs3org/reva/pkg/utils"
)

// Protocols is the list of protocols.
Expand Down Expand Up @@ -73,11 +74,12 @@ func (w *WebDAV) ToOCMProtocol() *ocm.Protocol {
// Webapp contains the parameters for the Webapp protocol.
type Webapp struct {
URITemplate string `json:"uriTemplate" validate:"required"`
ViewMode string `json:"viewMode" validate:"required,dive,required,oneof=view read write"`
}

// ToOCMProtocol convert the protocol to a ocm Protocol struct.
func (w *Webapp) ToOCMProtocol() *ocm.Protocol {
return ocmshare.NewWebappProtocol(w.URITemplate)
return ocmshare.NewWebappProtocol(w.URITemplate, utils.GetAPViewMode(w.ViewMode))
}

// Datatx contains the parameters for the Datatx protocol.
Expand Down Expand Up @@ -109,11 +111,22 @@ func (p *Protocols) UnmarshalJSON(data []byte) error {

for name, d := range prot {
var res Protocol

// we do not support the OCM v1.0 properties for now, therefore we just skip them
if name == "name" {
continue
}
if name == "options" {
var opt map[string]any
if err := json.Unmarshal(d, &opt); err != nil || len(opt) > 0 {
return fmt.Errorf("protocol options not supported: %s", string(d))
}
continue
}
ctype, ok := protocolImpl[name]
if !ok {
return fmt.Errorf("protocol %s not recognised", name)
}

res = reflect.New(ctype).Interface().(Protocol)
if err := json.Unmarshal(d, &res); err != nil {
return err
Expand All @@ -126,10 +139,15 @@ func (p *Protocols) UnmarshalJSON(data []byte) error {

// MarshalJSON implements the Marshaler interface.
func (p Protocols) MarshalJSON() ([]byte, error) {
d := make(map[string]Protocol)
d := make(map[string]any)
for _, prot := range p {
d[getProtocolName(prot)] = prot
}
// fill in the OCM v1.0 properties, but only if at least a protocol was defined
if len(p) > 0 {
d["name"] = "multi"
d["options"] = map[string]any{}
}
return json.Marshal(d)
}

Expand Down
50 changes: 33 additions & 17 deletions internal/http/services/ocmd/protocols_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,11 @@ func TestUnmarshalProtocol(t *testing.T) {
expected: []Protocol{},
},
{
raw: `{"webdav":{"sharedSecret":"secret","permissions":["read","write"],"url":"http://example.org"}}`,
raw: `{"name":"foo", "options":{"unsupported": "value"}}`,
err: `protocol options not supported: {"unsupported": "value"}`,
},
{
raw: `{"name":"multi","options":{},"webdav":{"sharedSecret":"secret","permissions":["read","write"],"url":"http://example.org"}}`,
expected: []Protocol{
&WebDAV{
SharedSecret: "secret",
Expand All @@ -47,15 +51,15 @@ func TestUnmarshalProtocol(t *testing.T) {
},
},
{
raw: `{"webapp":{"uriTemplate":"http://example.org/{test}"}}`,
raw: `{"name":"multi","options":{},"webapp":{"uriTemplate":"http://example.org/{test}"}}`,
expected: []Protocol{
&Webapp{
URITemplate: "http://example.org/{test}",
},
},
},
{
raw: `{"datatx":{"sharedSecret":"secret","srcUri":"http://example.org","size":10}}`,
raw: `{"name":"multi","options":{},"datatx":{"sharedSecret":"secret","srcUri":"http://example.org","size":10}}`,
expected: []Protocol{
&Datatx{
SharedSecret: "secret",
Expand All @@ -65,7 +69,7 @@ func TestUnmarshalProtocol(t *testing.T) {
},
},
{
raw: `{"webdav":{"sharedSecret":"secret","permissions":["read","write"],"url":"http://example.org"},"webapp":{"uriTemplate":"http://example.org/{test}"},"datatx":{"sharedSecret":"secret","srcUri":"http://example.org","size":10}}`,
raw: `{"name":"multi","options":{},"webdav":{"sharedSecret":"secret","permissions":["read","write"],"url":"http://example.org"},"webapp":{"uriTemplate":"http://example.org/{test}"},"datatx":{"sharedSecret":"secret","srcUri":"http://example.org","size":10}}`,
expected: []Protocol{
&WebDAV{
SharedSecret: "secret",
Expand Down Expand Up @@ -125,11 +129,11 @@ func protocolsEqual(p1, p2 Protocols) bool {
func TestMarshalProtocol(t *testing.T) {
tests := []struct {
in Protocols
expected map[string]map[string]any
expected map[string]any
}{
{
in: []Protocol{},
expected: map[string]map[string]any{},
expected: map[string]any{},
},
{
in: []Protocol{
Expand All @@ -139,8 +143,10 @@ func TestMarshalProtocol(t *testing.T) {
URL: "http://example.org",
},
},
expected: map[string]map[string]any{
"webdav": {
expected: map[string]any{
"name": "multi",
"options": map[string]any{},
"webdav": map[string]any{
"sharedSecret": "secret",
"permissions": []any{"read"},
"url": "http://example.org",
Expand All @@ -151,11 +157,15 @@ func TestMarshalProtocol(t *testing.T) {
in: []Protocol{
&Webapp{
URITemplate: "http://example.org",
ViewMode: "read",
},
},
expected: map[string]map[string]any{
"webapp": {
expected: map[string]any{
"name": "multi",
"options": map[string]any{},
"webapp": map[string]any{
"uriTemplate": "http://example.org",
"viewMode": "read",
},
},
},
Expand All @@ -167,8 +177,10 @@ func TestMarshalProtocol(t *testing.T) {
Size: 10,
},
},
expected: map[string]map[string]any{
"datatx": {
expected: map[string]any{
"name": "multi",
"options": map[string]any{},
"datatx": map[string]any{
"sharedSecret": "secret",
"srcUri": "http://example.org/source",
"size": float64(10),
Expand All @@ -184,23 +196,27 @@ func TestMarshalProtocol(t *testing.T) {
},
&Webapp{
URITemplate: "http://example.org",
ViewMode: "read",
},
&Datatx{
SharedSecret: "secret",
SourceURI: "http://example.org/source",
Size: 10,
},
},
expected: map[string]map[string]any{
"webdav": {
expected: map[string]any{
"name": "multi",
"options": map[string]any{},
"webdav": map[string]any{
"sharedSecret": "secret",
"permissions": []any{"read"},
"url": "http://example.org",
},
"webapp": {
"webapp": map[string]any{
"uriTemplate": "http://example.org",
"viewMode": "read",
},
"datatx": {
"datatx": map[string]any{
"sharedSecret": "secret",
"srcUri": "http://example.org/source",
"size": float64(10),
Expand All @@ -215,7 +231,7 @@ func TestMarshalProtocol(t *testing.T) {
t.Fatal("not expected error", err)
}

var got map[string]map[string]any
var got map[string]any
if err := json.Unmarshal(d, &got); err != nil {
t.Fatal("not expected error", err)
}
Expand Down
2 changes: 1 addition & 1 deletion internal/http/services/ocmd/shares.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ type createShareRequest struct {
ShareType string `json:"shareType" validate:"required,oneof=user group"` // recipient share type (user or group)
ResourceType string `json:"resourceType" validate:"required,oneof=file folder"`
Expiration uint64 `json:"expiration"`
Protocols Protocols `json:"protocols" validate:"required"`
Protocols Protocols `json:"protocol" validate:"required"`
}

// CreateShare sends all the informations to the consumer needed to start
Expand Down
4 changes: 2 additions & 2 deletions pkg/ocm/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ type NewShareRequest struct {
ShareType string `json:"shareType"`
Expiration uint64 `json:"expiration"`
ResourceType string `json:"resourceType"`
Protocols ocmd.Protocols `json:"protocols"`
Protocols ocmd.Protocols `json:"protocol"`
}

func (r *NewShareRequest) toJSON() (io.Reader, error) {
Expand All @@ -183,7 +183,7 @@ type NewShareResponse struct {
}

// NewShare creates a new share.
// https://github.com/cs3org/OCM-API/blob/223285aa4d828ed85c361c7382efd08c42b5e719/spec.yaml
// https://github.com/cs3org/OCM-API/blob/develop/spec.yaml
func (c *OCMClient) NewShare(ctx context.Context, endpoint string, r *NewShareRequest) (*NewShareResponse, error) {
url, err := url.JoinPath(endpoint, "shares")
if err != nil {
Expand Down
13 changes: 7 additions & 6 deletions pkg/ocm/share/repository/sql/conversions.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import (
"strconv"
"strings"

providerv1beta1 "github.com/cs3org/go-cs3apis/cs3/app/provider/v1beta1"
appprovider "github.com/cs3org/go-cs3apis/cs3/app/provider/v1beta1"
userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
ocm "github.com/cs3org/go-cs3apis/cs3/sharing/ocm/v1beta1"
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
Expand Down Expand Up @@ -78,8 +78,8 @@ const (
const (
// WebDAVProtocol is the WebDav protocol.
WebDAVProtocol Protocol = iota
// WebappProtcol is the Webapp protocol.
WebappProtcol
// WebappProtocol is the Webapp protocol.
WebappProtocol
// TransferProtocol is the Transfer protocol.
TransferProtocol
)
Expand Down Expand Up @@ -171,6 +171,7 @@ type dbProtocol struct {
WebDAVSharedSecret *string
WebDavPermissions *int
WebappURITemplate *string
WebappViewMode *int
TransferSourceURI *string
TransferSharedSecret *string
TransferSize *int
Expand Down Expand Up @@ -272,7 +273,7 @@ func convertToCS3AccessMethod(m *dbAccessMethod) *ocm.AccessMethod {
case WebDAVAccessMethod:
return share.NewWebDavAccessMethod(conversions.RoleFromOCSPermissions(conversions.Permissions(*m.WebDAVPermissions)).CS3ResourcePermissions())
case WebappAccessMethod:
return share.NewWebappAccessMethod(providerv1beta1.ViewMode(*m.WebAppViewMode))
return share.NewWebappAccessMethod(appprovider.ViewMode(*m.WebAppViewMode))
case TransferAccessMethod:
return share.NewTransferAccessMethod()
}
Expand All @@ -285,8 +286,8 @@ func convertToCS3Protocol(p *dbProtocol) *ocm.Protocol {
return share.NewWebDAVProtocol(*p.WebDAVURI, *p.WebDAVSharedSecret, &ocm.SharePermissions{
Permissions: conversions.RoleFromOCSPermissions(conversions.Permissions(*p.WebDavPermissions)).CS3ResourcePermissions(),
})
case WebappProtcol:
return share.NewWebappProtocol(*p.WebappURITemplate)
case WebappProtocol:
return share.NewWebappProtocol(*p.WebappURITemplate, appprovider.ViewMode(*p.WebappViewMode))
case TransferProtocol:
return share.NewTransferProtocol(*p.TransferSourceURI, *p.TransferSharedSecret, uint64(*p.TransferSize))
}
Expand Down
14 changes: 7 additions & 7 deletions pkg/ocm/share/repository/sql/sql.go
Original file line number Diff line number Diff line change
Expand Up @@ -474,13 +474,13 @@ func storeWebDAVProtocol(tx *sql.Tx, shareID int64, o *ocm.Protocol_WebdavOption
}

func storeWebappProtocol(tx *sql.Tx, shareID int64, o *ocm.Protocol_WebappOptions) error {
pID, err := storeProtocol(tx, shareID, WebappProtcol)
pID, err := storeProtocol(tx, shareID, WebappProtocol)
if err != nil {
return err
}

query := "INSERT INTO ocm_protocol_webapp SET ocm_protocol_id=?, uri_template=?"
params := []any{pID, o.WebappOptions.UriTemplate}
query := "INSERT INTO ocm_protocol_webapp SET ocm_protocol_id=?, uri_template=?, view_mode=?"
params := []any{pID, o.WebappOptions.UriTemplate, o.WebappOptions.ViewMode}

_, err = tx.Exec(query, params...)
return err
Expand Down Expand Up @@ -604,7 +604,7 @@ func (m *mgr) getProtocolsIds(ctx context.Context, ids []any) (map[string][]*ocm
if len(ids) == 0 {
return protocols, nil
}
query := "SELECT p.ocm_received_share_id, p.type, dav.uri, dav.shared_secret, dav.permissions, app.uri_template, tx.source_uri, tx.shared_secret, tx.size FROM ocm_received_share_protocols as p LEFT JOIN ocm_protocol_webdav as dav ON p.id=dav.ocm_protocol_id LEFT JOIN ocm_protocol_webapp as app ON p.id=app.ocm_protocol_id LEFT JOIN ocm_protocol_transfer as tx ON p.id=tx.ocm_protocol_id WHERE p.ocm_received_share_id IN "
query := "SELECT p.ocm_received_share_id, p.type, dav.uri, dav.shared_secret, dav.permissions, app.uri_template, app.view_mode, tx.source_uri, tx.shared_secret, tx.size FROM ocm_received_share_protocols as p LEFT JOIN ocm_protocol_webdav as dav ON p.id=dav.ocm_protocol_id LEFT JOIN ocm_protocol_webapp as app ON p.id=app.ocm_protocol_id LEFT JOIN ocm_protocol_transfer as tx ON p.id=tx.ocm_protocol_id WHERE p.ocm_received_share_id IN "
in := strings.Repeat("?,", len(ids))
query += "(" + in[:len(in)-1] + ")"

Expand All @@ -615,7 +615,7 @@ func (m *mgr) getProtocolsIds(ctx context.Context, ids []any) (map[string][]*ocm

var p dbProtocol
for rows.Next() {
if err := rows.Scan(&p.ShareID, &p.Type, &p.WebDAVURI, &p.WebDAVSharedSecret, &p.WebDavPermissions, &p.WebappURITemplate, &p.TransferSourceURI, &p.TransferSharedSecret, &p.TransferSize); err != nil {
if err := rows.Scan(&p.ShareID, &p.Type, &p.WebDAVURI, &p.WebDAVSharedSecret, &p.WebDavPermissions, &p.WebappURITemplate, &p.WebappViewMode, &p.TransferSourceURI, &p.TransferSharedSecret, &p.TransferSize); err != nil {
continue
}
protocols[p.ShareID] = append(protocols[p.ShareID], convertToCS3Protocol(&p))
Expand Down Expand Up @@ -683,7 +683,7 @@ func (m *mgr) getReceivedByKey(ctx context.Context, user *userpb.User, key *ocm.
}

func (m *mgr) getProtocols(ctx context.Context, id int) ([]*ocm.Protocol, error) {
query := "SELECT p.type, dav.uri, dav.shared_secret, dav.permissions, app.uri_template, tx.source_uri, tx.shared_secret, tx.size FROM ocm_received_share_protocols as p LEFT JOIN ocm_protocol_webdav as dav ON p.id=dav.ocm_protocol_id LEFT JOIN ocm_protocol_webapp as app ON p.id=app.ocm_protocol_id LEFT JOIN ocm_protocol_transfer as tx ON p.id=tx.ocm_protocol_id WHERE p.ocm_received_share_id=?"
query := "SELECT p.type, dav.uri, dav.shared_secret, dav.permissions, app.uri_template, app.view_mode, tx.source_uri, tx.shared_secret, tx.size FROM ocm_received_share_protocols as p LEFT JOIN ocm_protocol_webdav as dav ON p.id=dav.ocm_protocol_id LEFT JOIN ocm_protocol_webapp as app ON p.id=app.ocm_protocol_id LEFT JOIN ocm_protocol_transfer as tx ON p.id=tx.ocm_protocol_id WHERE p.ocm_received_share_id=?"

var protocols []*ocm.Protocol
rows, err := m.db.QueryContext(ctx, query, id)
Expand All @@ -693,7 +693,7 @@ func (m *mgr) getProtocols(ctx context.Context, id int) ([]*ocm.Protocol, error)

var p dbProtocol
for rows.Next() {
if err := rows.Scan(&p.Type, &p.WebDAVURI, &p.WebDAVSharedSecret, &p.WebDavPermissions, &p.WebappURITemplate, &p.TransferSourceURI, &p.TransferSharedSecret, &p.TransferSize); err != nil {
if err := rows.Scan(&p.Type, &p.WebDAVURI, &p.WebDAVSharedSecret, &p.WebDavPermissions, &p.WebappURITemplate, &p.WebappViewMode, &p.TransferSourceURI, &p.TransferSharedSecret, &p.TransferSize); err != nil {
continue
}
protocols = append(protocols, convertToCS3Protocol(&p))
Expand Down
Loading

0 comments on commit 423c0a8

Please sign in to comment.