Skip to content

Commit e2678c2

Browse files
authored
Support OCM v1.0 schema (#3757)
* Support OCM v1.0 schema but bail out if the options aren't empty * Fixes following PR comments
1 parent 6a302f7 commit e2678c2

File tree

13 files changed

+146
-69
lines changed

13 files changed

+146
-69
lines changed

changelog/unreleased/ocm-reconcile.md

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
Enhancement: Support OCM v1.0 schema
2+
3+
Following cs3org/cs3apis#206, we add the fields to ensure
4+
backwards compatibility with OCM v1.0. However, if the
5+
`protocol.options` undocumented object is not empty, we bail
6+
out for now. Supporting interoperability with OCM v1.0
7+
implementations (notably Nextcloud 25) may come in the future
8+
if the undocumented options are fully reverse engineered. This
9+
is reflected in the unit tests as well.
10+
11+
Also, added viewMode to webapp protocol options (cs3org/cs3apis#207)
12+
and adapted all SQL code and unit tests.
13+
14+
https://github.com/cs3org/reva/pull/3757

go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ require (
1616
github.com/cheggaaa/pb v1.0.29
1717
github.com/coreos/go-oidc v2.2.1+incompatible
1818
github.com/cs3org/cato v0.0.0-20200828125504-e418fc54dd5e
19-
github.com/cs3org/go-cs3apis v0.0.0-20230228180528-ee4e51c97a49
19+
github.com/cs3org/go-cs3apis v0.0.0-20230331073620-011a5b8a3115
2020
github.com/dgraph-io/ristretto v0.1.1
2121
github.com/dolthub/go-mysql-server v0.14.0
2222
github.com/eventials/go-tus v0.0.0-20200718001131-45c7ec8f5d59

go.sum

+2-2
Original file line numberDiff line numberDiff line change
@@ -305,8 +305,8 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3
305305
github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
306306
github.com/cs3org/cato v0.0.0-20200828125504-e418fc54dd5e h1:tqSPWQeueWTKnJVMJffz4pz0o1WuQxJ28+5x5JgaHD8=
307307
github.com/cs3org/cato v0.0.0-20200828125504-e418fc54dd5e/go.mod h1:XJEZ3/EQuI3BXTp/6DUzFr850vlxq11I6satRtz0YQ4=
308-
github.com/cs3org/go-cs3apis v0.0.0-20230228180528-ee4e51c97a49 h1:CG65qpsSttrPAqdK19kaZaAiRadZ5xNFVrKoIjICBBM=
309-
github.com/cs3org/go-cs3apis v0.0.0-20230228180528-ee4e51c97a49/go.mod h1:UXha4TguuB52H14EMoSsCqDj7k8a/t7g4gVP+bgY5LY=
308+
github.com/cs3org/go-cs3apis v0.0.0-20230331073620-011a5b8a3115 h1:WR5sDbfsHZZViXKB0036V2hobzZSxew1MomrSk1kWyI=
309+
github.com/cs3org/go-cs3apis v0.0.0-20230331073620-011a5b8a3115/go.mod h1:UXha4TguuB52H14EMoSsCqDj7k8a/t7g4gVP+bgY5LY=
310310
github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4=
311311
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
312312
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=

internal/http/services/ocmd/protocols.go

+23-3
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,15 @@ package ocmd
2020

2121
import (
2222
"encoding/json"
23+
"errors"
2324
"fmt"
2425
"reflect"
2526
"strings"
2627

2728
ocm "github.com/cs3org/go-cs3apis/cs3/sharing/ocm/v1beta1"
2829
providerv1beta1 "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
2930
ocmshare "github.com/cs3org/reva/pkg/ocm/share"
31+
utils "github.com/cs3org/reva/pkg/utils"
3032
)
3133

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

7881
// ToOCMProtocol convert the protocol to a ocm Protocol struct.
7982
func (w *Webapp) ToOCMProtocol() *ocm.Protocol {
80-
return ocmshare.NewWebappProtocol(w.URITemplate)
83+
return ocmshare.NewWebappProtocol(w.URITemplate, utils.GetAppViewMode(w.ViewMode))
8184
}
8285

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

110113
for name, d := range prot {
111114
var res Protocol
115+
116+
// we do not support the OCM v1.0 properties for now, therefore just skip or bail out
117+
if name == "name" {
118+
continue
119+
}
120+
if name == "options" {
121+
var opt map[string]any
122+
if err := json.Unmarshal(d, &opt); err != nil || len(opt) > 0 {
123+
return fmt.Errorf("protocol options not supported: %s", string(d))
124+
}
125+
continue
126+
}
112127
ctype, ok := protocolImpl[name]
113128
if !ok {
114129
return fmt.Errorf("protocol %s not recognised", name)
115130
}
116-
117131
res = reflect.New(ctype).Interface().(Protocol)
118132
if err := json.Unmarshal(d, &res); err != nil {
119133
return err
@@ -126,10 +140,16 @@ func (p *Protocols) UnmarshalJSON(data []byte) error {
126140

127141
// MarshalJSON implements the Marshaler interface.
128142
func (p Protocols) MarshalJSON() ([]byte, error) {
129-
d := make(map[string]Protocol)
143+
if len(p) == 0 {
144+
return nil, errors.New("no protocol defined")
145+
}
146+
d := make(map[string]any)
130147
for _, prot := range p {
131148
d[getProtocolName(prot)] = prot
132149
}
150+
// fill in the OCM v1.0 properties
151+
d["name"] = "multi"
152+
d["options"] = map[string]any{}
133153
return json.Marshal(d)
134154
}
135155

internal/http/services/ocmd/protocols_test.go

+52-30
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,19 @@ func TestUnmarshalProtocol(t *testing.T) {
3737
expected: []Protocol{},
3838
},
3939
{
40-
raw: `{"webdav":{"sharedSecret":"secret","permissions":["read","write"],"url":"http://example.org"}}`,
40+
raw: `{"name":"foo","options":{ }}`,
41+
expected: []Protocol{},
42+
},
43+
{
44+
raw: `{"name":"foo","options":{"unsupported":"value"}}`,
45+
err: `protocol options not supported: {"unsupported":"value"}`,
46+
},
47+
{
48+
raw: `{"unsupported":{}}`,
49+
err: "protocol unsupported not recognised",
50+
},
51+
{
52+
raw: `{"name":"multi","options":{},"webdav":{"sharedSecret":"secret","permissions":["read","write"],"url":"http://example.org"}}`,
4153
expected: []Protocol{
4254
&WebDAV{
4355
SharedSecret: "secret",
@@ -47,15 +59,15 @@ func TestUnmarshalProtocol(t *testing.T) {
4759
},
4860
},
4961
{
50-
raw: `{"webapp":{"uriTemplate":"http://example.org/{test}"}}`,
62+
raw: `{"name":"multi","options":{},"webapp":{"uriTemplate":"http://example.org/{test}"}}`,
5163
expected: []Protocol{
5264
&Webapp{
5365
URITemplate: "http://example.org/{test}",
5466
},
5567
},
5668
},
5769
{
58-
raw: `{"datatx":{"sharedSecret":"secret","srcUri":"http://example.org","size":10}}`,
70+
raw: `{"name":"multi","options":{},"datatx":{"sharedSecret":"secret","srcUri":"http://example.org","size":10}}`,
5971
expected: []Protocol{
6072
&Datatx{
6173
SharedSecret: "secret",
@@ -65,7 +77,7 @@ func TestUnmarshalProtocol(t *testing.T) {
6577
},
6678
},
6779
{
68-
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}}`,
80+
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}}`,
6981
expected: []Protocol{
7082
&WebDAV{
7183
SharedSecret: "secret",
@@ -82,10 +94,6 @@ func TestUnmarshalProtocol(t *testing.T) {
8294
},
8395
},
8496
},
85-
{
86-
raw: `{"not_existing":{}}`,
87-
err: "protocol not_existing not recognised",
88-
},
8997
}
9098

9199
for _, tt := range tests {
@@ -125,11 +133,12 @@ func protocolsEqual(p1, p2 Protocols) bool {
125133
func TestMarshalProtocol(t *testing.T) {
126134
tests := []struct {
127135
in Protocols
128-
expected map[string]map[string]any
136+
expected map[string]any
137+
err string
129138
}{
130139
{
131-
in: []Protocol{},
132-
expected: map[string]map[string]any{},
140+
in: []Protocol{},
141+
err: "json: error calling MarshalJSON for type ocmd.Protocols: no protocol defined",
133142
},
134143
{
135144
in: []Protocol{
@@ -139,8 +148,10 @@ func TestMarshalProtocol(t *testing.T) {
139148
URL: "http://example.org",
140149
},
141150
},
142-
expected: map[string]map[string]any{
143-
"webdav": {
151+
expected: map[string]any{
152+
"name": "multi",
153+
"options": map[string]any{},
154+
"webdav": map[string]any{
144155
"sharedSecret": "secret",
145156
"permissions": []any{"read"},
146157
"url": "http://example.org",
@@ -151,11 +162,15 @@ func TestMarshalProtocol(t *testing.T) {
151162
in: []Protocol{
152163
&Webapp{
153164
URITemplate: "http://example.org",
165+
ViewMode: "read",
154166
},
155167
},
156-
expected: map[string]map[string]any{
157-
"webapp": {
168+
expected: map[string]any{
169+
"name": "multi",
170+
"options": map[string]any{},
171+
"webapp": map[string]any{
158172
"uriTemplate": "http://example.org",
173+
"viewMode": "read",
159174
},
160175
},
161176
},
@@ -167,8 +182,10 @@ func TestMarshalProtocol(t *testing.T) {
167182
Size: 10,
168183
},
169184
},
170-
expected: map[string]map[string]any{
171-
"datatx": {
185+
expected: map[string]any{
186+
"name": "multi",
187+
"options": map[string]any{},
188+
"datatx": map[string]any{
172189
"sharedSecret": "secret",
173190
"srcUri": "http://example.org/source",
174191
"size": float64(10),
@@ -184,23 +201,27 @@ func TestMarshalProtocol(t *testing.T) {
184201
},
185202
&Webapp{
186203
URITemplate: "http://example.org",
204+
ViewMode: "read",
187205
},
188206
&Datatx{
189207
SharedSecret: "secret",
190208
SourceURI: "http://example.org/source",
191209
Size: 10,
192210
},
193211
},
194-
expected: map[string]map[string]any{
195-
"webdav": {
212+
expected: map[string]any{
213+
"name": "multi",
214+
"options": map[string]any{},
215+
"webdav": map[string]any{
196216
"sharedSecret": "secret",
197217
"permissions": []any{"read"},
198218
"url": "http://example.org",
199219
},
200-
"webapp": {
220+
"webapp": map[string]any{
201221
"uriTemplate": "http://example.org",
222+
"viewMode": "read",
202223
},
203-
"datatx": {
224+
"datatx": map[string]any{
204225
"sharedSecret": "secret",
205226
"srcUri": "http://example.org/source",
206227
"size": float64(10),
@@ -211,17 +232,18 @@ func TestMarshalProtocol(t *testing.T) {
211232

212233
for _, tt := range tests {
213234
d, err := json.Marshal(tt.in)
214-
if err != nil {
215-
t.Fatal("not expected error", err)
216-
}
217-
218-
var got map[string]map[string]any
219-
if err := json.Unmarshal(d, &got); err != nil {
220-
t.Fatal("not expected error", err)
235+
if err != nil && err.Error() != tt.err {
236+
t.Fatalf("not expected error. got=%+v expected=%+v", err, tt.err)
221237
}
238+
if err == nil {
239+
var got map[string]any
240+
if err := json.Unmarshal(d, &got); err != nil {
241+
t.Fatalf("not expected error %+v with input %+v", err, tt.in)
242+
}
222243

223-
if !reflect.DeepEqual(tt.expected, got) {
224-
t.Fatalf("result does not match with expected. got=%+v expected=%+v", render.AsCode(got), render.AsCode(tt.expected))
244+
if !reflect.DeepEqual(tt.expected, got) {
245+
t.Fatalf("result does not match with expected. got=%+v expected=%+v", render.AsCode(got), render.AsCode(tt.expected))
246+
}
225247
}
226248
}
227249
}

internal/http/services/ocmd/shares.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ type createShareRequest struct {
7171
ShareType string `json:"shareType" validate:"required,oneof=user group"` // recipient share type (user or group)
7272
ResourceType string `json:"resourceType" validate:"required,oneof=file folder"`
7373
Expiration uint64 `json:"expiration"`
74-
Protocols Protocols `json:"protocols" validate:"required"`
74+
Protocols Protocols `json:"protocol" validate:"required"`
7575
}
7676

7777
// CreateShare sends all the informations to the consumer needed to start

pkg/ocm/client/client.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ type NewShareRequest struct {
166166
ShareType string `json:"shareType"`
167167
Expiration uint64 `json:"expiration"`
168168
ResourceType string `json:"resourceType"`
169-
Protocols ocmd.Protocols `json:"protocols"`
169+
Protocols ocmd.Protocols `json:"protocol"`
170170
}
171171

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

185185
// NewShare creates a new share.
186-
// https://github.com/cs3org/OCM-API/blob/223285aa4d828ed85c361c7382efd08c42b5e719/spec.yaml
186+
// https://github.com/cs3org/OCM-API/blob/develop/spec.yaml
187187
func (c *OCMClient) NewShare(ctx context.Context, endpoint string, r *NewShareRequest) (*NewShareResponse, error) {
188188
url, err := url.JoinPath(endpoint, "shares")
189189
if err != nil {

pkg/ocm/share/repository/sql/conversions.go

+7-6
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import (
2323
"strconv"
2424
"strings"
2525

26-
providerv1beta1 "github.com/cs3org/go-cs3apis/cs3/app/provider/v1beta1"
26+
appprovider "github.com/cs3org/go-cs3apis/cs3/app/provider/v1beta1"
2727
userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
2828
ocm "github.com/cs3org/go-cs3apis/cs3/sharing/ocm/v1beta1"
2929
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
@@ -78,8 +78,8 @@ const (
7878
const (
7979
// WebDAVProtocol is the WebDav protocol.
8080
WebDAVProtocol Protocol = iota
81-
// WebappProtcol is the Webapp protocol.
82-
WebappProtcol
81+
// WebappProtocol is the Webapp protocol.
82+
WebappProtocol
8383
// TransferProtocol is the Transfer protocol.
8484
TransferProtocol
8585
)
@@ -171,6 +171,7 @@ type dbProtocol struct {
171171
WebDAVSharedSecret *string
172172
WebDavPermissions *int
173173
WebappURITemplate *string
174+
WebappViewMode *int
174175
TransferSourceURI *string
175176
TransferSharedSecret *string
176177
TransferSize *int
@@ -272,7 +273,7 @@ func convertToCS3AccessMethod(m *dbAccessMethod) *ocm.AccessMethod {
272273
case WebDAVAccessMethod:
273274
return share.NewWebDavAccessMethod(conversions.RoleFromOCSPermissions(conversions.Permissions(*m.WebDAVPermissions)).CS3ResourcePermissions())
274275
case WebappAccessMethod:
275-
return share.NewWebappAccessMethod(providerv1beta1.ViewMode(*m.WebAppViewMode))
276+
return share.NewWebappAccessMethod(appprovider.ViewMode(*m.WebAppViewMode))
276277
case TransferAccessMethod:
277278
return share.NewTransferAccessMethod()
278279
}
@@ -285,8 +286,8 @@ func convertToCS3Protocol(p *dbProtocol) *ocm.Protocol {
285286
return share.NewWebDAVProtocol(*p.WebDAVURI, *p.WebDAVSharedSecret, &ocm.SharePermissions{
286287
Permissions: conversions.RoleFromOCSPermissions(conversions.Permissions(*p.WebDavPermissions)).CS3ResourcePermissions(),
287288
})
288-
case WebappProtcol:
289-
return share.NewWebappProtocol(*p.WebappURITemplate)
289+
case WebappProtocol:
290+
return share.NewWebappProtocol(*p.WebappURITemplate, appprovider.ViewMode(*p.WebappViewMode))
290291
case TransferProtocol:
291292
return share.NewTransferProtocol(*p.TransferSourceURI, *p.TransferSharedSecret, uint64(*p.TransferSize))
292293
}

pkg/ocm/share/repository/sql/init.sql

+1
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ CREATE TABLE IF NOT EXISTS ocm_protocol_webdav (
7070
CREATE TABLE IF NOT EXISTS ocm_protocol_webapp (
7171
ocm_protocol_id INTEGER NOT NULL PRIMARY KEY,
7272
uri_template VARCHAR(255) NOT NULL,
73+
view_mode INTEGER NOT NULL,
7374
FOREIGN KEY (ocm_protocol_id) REFERENCES ocm_received_share_protocols(id) ON DELETE CASCADE
7475
);
7576

0 commit comments

Comments
 (0)