From 57024006f2910acfe2cfb7593bc032623924f2db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Duffeck?= Date: Tue, 7 Nov 2023 10:50:29 +0100 Subject: [PATCH 01/11] Start and wire the ocmreceived storage provider --- services/gateway/pkg/revaconfig/config.go | 12 ++++++++++++ services/ocm/pkg/revaconfig/config.go | 4 ++++ 2 files changed, 16 insertions(+) diff --git a/services/gateway/pkg/revaconfig/config.go b/services/gateway/pkg/revaconfig/config.go index 858b369e8c5..252a1364eee 100644 --- a/services/gateway/pkg/revaconfig/config.go +++ b/services/gateway/pkg/revaconfig/config.go @@ -185,6 +185,18 @@ func spacesProviders(cfg *config.Config, logger log.Logger) map[string]map[strin }, }, }, + cfg.OCMEndpoint: { + "providerid": utils.OCMStorageProviderID, + "spaces": map[string]interface{}{ + "grant": map[string]interface{}{ + "mount_point": ".", + }, + "mountpoint": map[string]interface{}{ + "mount_point": "/ocm", + "path_template": "/ocm/{{.Space.Root.OpaqueId}}", + }, + }, + }, // medatada storage not part of the global namespace } } diff --git a/services/ocm/pkg/revaconfig/config.go b/services/ocm/pkg/revaconfig/config.go index 40a10768ee5..6742db5efe7 100644 --- a/services/ocm/pkg/revaconfig/config.go +++ b/services/ocm/pkg/revaconfig/config.go @@ -101,6 +101,10 @@ func OCMConfigFromStruct(cfg *config.Config, logger log.Logger) map[string]inter }, }, }, + "storageprovider": map[string]interface{}{ + "driver": "ocmreceived", + "data_server_url": "http://" + cfg.HTTP.Addr + "/data", + }, "authprovider": map[string]interface{}{ "auth_manager": "ocmshares", "auth_managers": map[string]interface{}{ From 5b1c550460614a143152a2cc52a95ca83f240105 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Duffeck?= Date: Tue, 7 Nov 2023 11:33:11 +0100 Subject: [PATCH 02/11] Start the dataprovider for received ocm shares --- services/ocm/pkg/revaconfig/config.go | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/services/ocm/pkg/revaconfig/config.go b/services/ocm/pkg/revaconfig/config.go index 6742db5efe7..7dbd7c25573 100644 --- a/services/ocm/pkg/revaconfig/config.go +++ b/services/ocm/pkg/revaconfig/config.go @@ -51,6 +51,30 @@ func OCMConfigFromStruct(cfg *config.Config, logger log.Logger) map[string]inter "gatewaysvc": cfg.Reva.Address, "expose_recipient_display_name": cfg.OCMD.ExposeRecipientDisplayName, }, + "dataprovider": map[string]interface{}{ + "prefix": "data", + "driver": "ocmreceived", + "drivers": map[string]interface{}{ + "ocmreceived": map[string]interface{}{}, + }, + "data_txs": map[string]interface{}{ + "simple": map[string]interface{}{ + "cache_store": "noop", + "cache_database": "system", + "cache_table": "stat", + }, + "spaces": map[string]interface{}{ + "cache_store": "noop", + "cache_database": "system", + "cache_table": "stat", + }, + "tus": map[string]interface{}{ + "cache_store": "noop", + "cache_database": "system", + "cache_table": "stat", + }, + }, + }, }, }, "grpc": map[string]interface{}{ From 73c0b022b308b3951dd1ef19aa76ff88de42e8c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Duffeck?= Date: Thu, 9 Nov 2023 09:11:13 +0100 Subject: [PATCH 03/11] Make mesh directory URL configurable (empty by default) --- services/ocm/pkg/config/config.go | 3 ++- services/ocm/pkg/revaconfig/config.go | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/services/ocm/pkg/config/config.go b/services/ocm/pkg/config/config.go index 065c2f64776..e4860efc441 100644 --- a/services/ocm/pkg/config/config.go +++ b/services/ocm/pkg/config/config.go @@ -72,7 +72,8 @@ type GRPCConfig struct { } type ScienceMesh struct { - Prefix string `yaml:"prefix" env:"OCM_SCIENCEMESH_PREFIX" desc:"URL path prefix for the ScienceMesh service. Note that the string must not start with '/'."` + Prefix string `yaml:"prefix" env:"OCM_SCIENCEMESH_PREFIX" desc:"URL path prefix for the ScienceMesh service. Note that the string must not start with '/'."` + MeshDirectoryURL string `yaml:"science_mesh_directory_url" env:"OCM_MESH_DIRECTORY_URL" desc:"URL of the mesh directory service."` } type OCMD struct { diff --git a/services/ocm/pkg/revaconfig/config.go b/services/ocm/pkg/revaconfig/config.go index 7dbd7c25573..ec39143da98 100644 --- a/services/ocm/pkg/revaconfig/config.go +++ b/services/ocm/pkg/revaconfig/config.go @@ -43,7 +43,7 @@ func OCMConfigFromStruct(cfg *config.Config, logger log.Logger) map[string]inter "prefix": cfg.ScienceMesh.Prefix, "smtp_credentials": map[string]string{}, "gatewaysvc": cfg.Reva.Address, - "mesh_directory_url": cfg.Commons.OcisURL, + "mesh_directory_url": cfg.ScienceMesh.MeshDirectoryURL, "provider_domain": cfg.Commons.OcisURL, }, "ocmd": map[string]interface{}{ From 4a16b3b64442f1aa1db27f954e427bdcb199098b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Duffeck?= Date: Fri, 10 Nov 2023 14:44:27 +0100 Subject: [PATCH 04/11] Expose variable to configure webapp template --- services/ocm/pkg/config/config.go | 7 ++++--- services/ocm/pkg/revaconfig/config.go | 1 + 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/services/ocm/pkg/config/config.go b/services/ocm/pkg/config/config.go index e4860efc441..9f681dbba90 100644 --- a/services/ocm/pkg/config/config.go +++ b/services/ocm/pkg/config/config.go @@ -118,9 +118,10 @@ type OCMCoreJSONDriver struct { } type OCMShareProvider struct { - Driver string `yaml:"driver" env:"OCM_OCM_SHARE_PROVIDER_DRIVER" desc:"Driver to be used for the OCM share provider. Supported value is only 'json'."` - Drivers OCMShareProviderDrivers `yaml:"drivers"` - Insecure bool `yaml:"insecure" env:"OCM_OCM_SHARE_PROVIDER_INSECURE" desc:"Disable TLS certificate validation for the OCM connections. Do not set this in production environments."` + Driver string `yaml:"driver" env:"OCM_OCM_SHARE_PROVIDER_DRIVER" desc:"Driver to be used for the OCM share provider. Supported value is only 'json'."` + Drivers OCMShareProviderDrivers `yaml:"drivers"` + Insecure bool `yaml:"insecure" env:"OCM_OCM_SHARE_PROVIDER_INSECURE" desc:"Disable TLS certificate validation for the OCM connections. Do not set this in production environments."` + WebappTemplate string `yaml:"webapp_template" env:"OCM_WEBAPP_TEMPLATE" desc:"Template for the webapp url."` } type OCMShareProviderDrivers struct { diff --git a/services/ocm/pkg/revaconfig/config.go b/services/ocm/pkg/revaconfig/config.go index ec39143da98..b82b35495a4 100644 --- a/services/ocm/pkg/revaconfig/config.go +++ b/services/ocm/pkg/revaconfig/config.go @@ -115,6 +115,7 @@ func OCMConfigFromStruct(cfg *config.Config, logger log.Logger) map[string]inter "gatewaysvc": cfg.Reva.Address, "provider_domain": cfg.Commons.OcisURL, "webdav_endpoint": cfg.Commons.OcisURL, + "webapp_template": cfg.OCMShareProvider.WebappTemplate, "client_insecure": cfg.OCMShareProvider.Insecure, }, "ocmcore": map[string]interface{}{ From b17fa1d72bebd56c8fa04caf4740c1a800453ee9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Duffeck?= Date: Fri, 10 Nov 2023 16:17:35 +0100 Subject: [PATCH 05/11] Make /remote.php/dav/ocm/ work as well --- services/proxy/pkg/middleware/authentication.go | 1 + 1 file changed, 1 insertion(+) diff --git a/services/proxy/pkg/middleware/authentication.go b/services/proxy/pkg/middleware/authentication.go index f173e94dade..558510b2072 100644 --- a/services/proxy/pkg/middleware/authentication.go +++ b/services/proxy/pkg/middleware/authentication.go @@ -25,6 +25,7 @@ var ( _publicPaths = [...]string{ "/dav/public-files/", + "/remote.php/dav/ocm/", "/dav/ocm/", "/ocm/", "/remote.php/dav/public-files/", From 15051c99ad83aaf8eff86f87f508e8486878ee5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Duffeck?= Date: Tue, 21 Nov 2023 08:59:45 +0100 Subject: [PATCH 06/11] Allow for disabling the certificate check for webdav connections --- services/ocm/pkg/config/config.go | 10 +++++++--- services/ocm/pkg/revaconfig/config.go | 11 +++++++++-- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/services/ocm/pkg/config/config.go b/services/ocm/pkg/config/config.go index 9f681dbba90..8cedd27d05d 100644 --- a/services/ocm/pkg/config/config.go +++ b/services/ocm/pkg/config/config.go @@ -31,6 +31,7 @@ type Config struct { OCMProviderAuthorizerDrivers OCMProviderAuthorizerDrivers `yaml:"ocm_provider_authorizer_drivers"` OCMShareProvider OCMShareProvider `yaml:"ocm_share_provider"` OCMCore OCMCore `yaml:"ocm_core"` + OCMStorageProvider OCMStorageProvider `yaml:"ocm_storage_provider"` Supervised bool `yaml:"-"` Context context.Context `yaml:"-"` @@ -108,6 +109,9 @@ type OCMCore struct { Driver string `yaml:"driver" env:"OCM_OCM_CORE_DRIVER" desc:"Driver to be used for the OCM core. Supported value is only 'json'."` Drivers OCMCoreDrivers `yaml:"drivers"` } +type OCMStorageProvider struct { + Insecure bool `yaml:"insecure" env:"OCM_OCM_STORAGE_PROVIDER_INSECURE" desc:"Disable TLS certificate validation for the OCM connections. Do not set this in production environments."` +} type OCMCoreDrivers struct { JSON OCMCoreJSONDriver `yaml:"json"` @@ -118,9 +122,9 @@ type OCMCoreJSONDriver struct { } type OCMShareProvider struct { - Driver string `yaml:"driver" env:"OCM_OCM_SHARE_PROVIDER_DRIVER" desc:"Driver to be used for the OCM share provider. Supported value is only 'json'."` - Drivers OCMShareProviderDrivers `yaml:"drivers"` - Insecure bool `yaml:"insecure" env:"OCM_OCM_SHARE_PROVIDER_INSECURE" desc:"Disable TLS certificate validation for the OCM connections. Do not set this in production environments."` + Driver string `yaml:"driver" env:"OCM_OCM_SHARE_PROVIDER_DRIVER" desc:"Driver to be used for the OCM share provider. Supported value is only 'json'."` + Drivers OCMShareProviderDrivers `yaml:"drivers"` + Insecure bool `yaml:"insecure" env:"OCM_OCM_SHARE_PROVIDER_INSECURE" desc:"Disable TLS certificate validation for the OCM connections. Do not set this in production environments."` WebappTemplate string `yaml:"webapp_template" env:"OCM_WEBAPP_TEMPLATE" desc:"Template for the webapp url."` } diff --git a/services/ocm/pkg/revaconfig/config.go b/services/ocm/pkg/revaconfig/config.go index b82b35495a4..b6ae64ddac1 100644 --- a/services/ocm/pkg/revaconfig/config.go +++ b/services/ocm/pkg/revaconfig/config.go @@ -55,7 +55,9 @@ func OCMConfigFromStruct(cfg *config.Config, logger log.Logger) map[string]inter "prefix": "data", "driver": "ocmreceived", "drivers": map[string]interface{}{ - "ocmreceived": map[string]interface{}{}, + "ocmreceived": map[string]interface{}{ + "insecure": cfg.OCMStorageProvider.Insecure, + }, }, "data_txs": map[string]interface{}{ "simple": map[string]interface{}{ @@ -127,7 +129,12 @@ func OCMConfigFromStruct(cfg *config.Config, logger log.Logger) map[string]inter }, }, "storageprovider": map[string]interface{}{ - "driver": "ocmreceived", + "driver": "ocmreceived", + "drivers": map[string]interface{}{ + "ocmreceived": map[string]interface{}{ + "insecure": cfg.OCMStorageProvider.Insecure, + }, + }, "data_server_url": "http://" + cfg.HTTP.Addr + "/data", }, "authprovider": map[string]interface{}{ From 2ffec5c79f1c85732c6054995ff30fd431bf3612 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Duffeck?= Date: Mon, 27 Nov 2023 09:15:21 +0100 Subject: [PATCH 07/11] Give the ocm storage provider some storage space --- services/ocm/pkg/config/config.go | 3 ++- services/ocm/pkg/config/defaults/defaultconfig.go | 12 ++++++++---- services/ocm/pkg/revaconfig/config.go | 3 ++- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/services/ocm/pkg/config/config.go b/services/ocm/pkg/config/config.go index 8cedd27d05d..94faef5e146 100644 --- a/services/ocm/pkg/config/config.go +++ b/services/ocm/pkg/config/config.go @@ -110,7 +110,8 @@ type OCMCore struct { Drivers OCMCoreDrivers `yaml:"drivers"` } type OCMStorageProvider struct { - Insecure bool `yaml:"insecure" env:"OCM_OCM_STORAGE_PROVIDER_INSECURE" desc:"Disable TLS certificate validation for the OCM connections. Do not set this in production environments."` + Insecure bool `yaml:"insecure" env:"OCM_OCM_STORAGE_PROVIDER_INSECURE" desc:"Disable TLS certificate validation for the OCM connections. Do not set this in production environments."` + StorageRoot string `yaml:"insecure" env:"OCM_OCM_STORAGE_PROVIDER_STORAGE_ROOT" desc:"Directory where the ocm storage provider persists its data like tus upload info files."` } type OCMCoreDrivers struct { diff --git a/services/ocm/pkg/config/defaults/defaultconfig.go b/services/ocm/pkg/config/defaults/defaultconfig.go index df1102631b7..9c020f9af5f 100644 --- a/services/ocm/pkg/config/defaults/defaultconfig.go +++ b/services/ocm/pkg/config/defaults/defaultconfig.go @@ -94,7 +94,7 @@ func DefaultConfig() *config.Config { Driver: "json", Drivers: config.OCMInviteManagerDrivers{ JSON: config.OCMInviteManagerJSONDriver{ - File: filepath.Join(defaults.BaseDataPath(), "storage", "ocminvites.json"), + File: filepath.Join(defaults.BaseDataPath(), "storage", "ocm", "ocminvites.json"), }, }, Insecure: false, @@ -102,14 +102,14 @@ func DefaultConfig() *config.Config { OCMProviderAuthorizerDriver: "json", OCMProviderAuthorizerDrivers: config.OCMProviderAuthorizerDrivers{ JSON: config.OCMProviderAuthorizerJSONDriver{ - Providers: filepath.Join(defaults.BaseDataPath(), "storage", "ocmproviders.json"), + Providers: filepath.Join(defaults.BaseDataPath(), "storage", "ocm", "ocmproviders.json"), }, }, OCMShareProvider: config.OCMShareProvider{ Driver: "json", Drivers: config.OCMShareProviderDrivers{ JSON: config.OCMShareProviderJSONDriver{ - File: filepath.Join(defaults.BaseDataPath(), "storage", "ocmshares.json"), + File: filepath.Join(defaults.BaseDataPath(), "storage", "ocm", "ocmshares.json"), }, }, Insecure: false, @@ -118,10 +118,14 @@ func DefaultConfig() *config.Config { Driver: "json", Drivers: config.OCMCoreDrivers{ JSON: config.OCMCoreJSONDriver{ - File: filepath.Join(defaults.BaseDataPath(), "storage", "ocmshares.json"), + File: filepath.Join(defaults.BaseDataPath(), "storage", "ocm", "ocmshares.json"), }, }, }, + OCMStorageProvider: config.OCMStorageProvider{ + Insecure: false, + StorageRoot: filepath.Join(defaults.BaseDataPath(), "storage", "ocm"), + }, } } diff --git a/services/ocm/pkg/revaconfig/config.go b/services/ocm/pkg/revaconfig/config.go index b6ae64ddac1..d221501ed47 100644 --- a/services/ocm/pkg/revaconfig/config.go +++ b/services/ocm/pkg/revaconfig/config.go @@ -132,7 +132,8 @@ func OCMConfigFromStruct(cfg *config.Config, logger log.Logger) map[string]inter "driver": "ocmreceived", "drivers": map[string]interface{}{ "ocmreceived": map[string]interface{}{ - "insecure": cfg.OCMStorageProvider.Insecure, + "insecure": cfg.OCMStorageProvider.Insecure, + "storage_root": cfg.OCMStorageProvider.StorageRoot, }, }, "data_server_url": "http://" + cfg.HTTP.Addr + "/data", From 4ccfc66432610a4f5aafb71077c318ced740a324 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Duffeck?= Date: Tue, 12 Dec 2023 12:08:31 +0100 Subject: [PATCH 08/11] Add a flag for including ocm sharees --- go.mod | 2 + go.sum | 4 +- services/frontend/pkg/config/config.go | 1 + .../pkg/config/defaults/defaultconfig.go | 1 + services/frontend/pkg/revaconfig/config.go | 1 + .../github.com/studio-b12/gowebdav/Makefile | 17 +- .../github.com/studio-b12/gowebdav/README.md | 362 ++++++++++++---- vendor/github.com/studio-b12/gowebdav/auth.go | 409 ++++++++++++++++++ .../studio-b12/gowebdav/basicAuth.go | 38 +- .../github.com/studio-b12/gowebdav/client.go | 283 ++++++------ .../studio-b12/gowebdav/digestAuth.go | 50 ++- .../github.com/studio-b12/gowebdav/errors.go | 12 +- vendor/github.com/studio-b12/gowebdav/file.go | 28 +- .../studio-b12/gowebdav/passportAuth.go | 181 ++++++++ .../studio-b12/gowebdav/requests.go | 108 ++--- .../github.com/studio-b12/gowebdav/utils.go | 23 - vendor/modules.txt | 3 +- 17 files changed, 1170 insertions(+), 353 deletions(-) create mode 100644 vendor/github.com/studio-b12/gowebdav/auth.go create mode 100644 vendor/github.com/studio-b12/gowebdav/passportAuth.go diff --git a/go.mod b/go.mod index cc763a09c60..44f05c8c891 100644 --- a/go.mod +++ b/go.mod @@ -350,3 +350,5 @@ require ( replace github.com/go-micro/plugins/v4/store/nats-js => github.com/kobergj/plugins/v4/store/nats-js v1.2.1-0.20231020092801-9463c820c19a replace github.com/go-micro/plugins/v4/store/nats-js-kv => github.com/kobergj/plugins/v4/store/nats-js-kv v0.0.0-20231207143248-4d424e3ae348 + +replace github.com/studio-b12/gowebdav => github.com/aduffeck/gowebdav v0.0.0-20231123085457-ff658b6ea159 diff --git a/go.sum b/go.sum index 6689ddd529d..0d2d4b43d35 100644 --- a/go.sum +++ b/go.sum @@ -827,6 +827,8 @@ github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWX github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= github.com/acomagu/bufpipe v1.0.3 h1:fxAGrHZTgQ9w5QqVItgzwj235/uYZYgbXitB+dLupOk= github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4= +github.com/aduffeck/gowebdav v0.0.0-20231123085457-ff658b6ea159 h1:m63hhLqbqmLGGPtyTtjTdxae61d9tMbRdKvMaDHWcDs= +github.com/aduffeck/gowebdav v0.0.0-20231123085457-ff658b6ea159/go.mod h1:bHA7t77X/QFExdeAnDzK6vKM34kEZAcE1OX4MfiwjkE= github.com/agnivade/levenshtein v1.1.1 h1:QY8M92nrzkmr798gCo3kmMyqXFzdQVpxLlGPRBij0P8= github.com/agnivade/levenshtein v1.1.1/go.mod h1:veldBMzWxcCG2ZvUTKD2kJNRdCk5hVbJomOvKkmgYbo= github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU= @@ -2000,8 +2002,6 @@ github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/studio-b12/gowebdav v0.0.0-20221015232716-17255f2e7423 h1:Wd8WDEEusB5+En4PiRWJp1cP59QLNsQun+mOTW8+s6s= -github.com/studio-b12/gowebdav v0.0.0-20221015232716-17255f2e7423/go.mod h1:bHA7t77X/QFExdeAnDzK6vKM34kEZAcE1OX4MfiwjkE= github.com/stvp/go-udp-testing v0.0.0-20201019212854-469649b16807/go.mod h1:7jxmlfBCDBXRzr0eAQJ48XC1hBu1np4CS5+cHEYfwpc= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/tchap/go-patricia/v2 v2.3.1 h1:6rQp39lgIYZ+MHmdEq4xzuk1t7OdC35z/xm0BGhTkes= diff --git a/services/frontend/pkg/config/config.go b/services/frontend/pkg/config/config.go index d4bb9c62c1a..42262ebf515 100644 --- a/services/frontend/pkg/config/config.go +++ b/services/frontend/pkg/config/config.go @@ -142,6 +142,7 @@ type OCS struct { ListOCMShares bool `yaml:"list_ocm_shares" env:"FRONTEND_OCS_LIST_OCM_SHARES" desc:"Include OCM shares when listing shares. See the OCM service documentation for more details."` PublicShareMustHavePassword bool `yaml:"public_sharing_share_must_have_password" env:"OCIS_SHARING_PUBLIC_SHARE_MUST_HAVE_PASSWORD;FRONTEND_OCS_PUBLIC_SHARE_MUST_HAVE_PASSWORD" desc:"Set this to true if you want to enforce passwords on all public shares."` WriteablePublicShareMustHavePassword bool `yaml:"public_sharing_writeableshare_must_have_password" env:"OCIS_SHARING_PUBLIC_WRITEABLE_SHARE_MUST_HAVE_PASSWORD;FRONTEND_OCS_PUBLIC_WRITEABLE_SHARE_MUST_HAVE_PASSWORD" desc:"Set this to true if you want to enforce passwords on Uploader, Editor or Contributor shares."` + IncludeOCMSharees bool `yaml:"include_ocm_sharees" env:"FRONTEND_OCS_INCLUDE_OCM_SHAREES" desc:"Include OCM sharees when listing sharees."` } type CacheWarmupDrivers struct { diff --git a/services/frontend/pkg/config/defaults/defaultconfig.go b/services/frontend/pkg/config/defaults/defaultconfig.go index b589aa2f97a..1ef32c67100 100644 --- a/services/frontend/pkg/config/defaults/defaultconfig.go +++ b/services/frontend/pkg/config/defaults/defaultconfig.go @@ -116,6 +116,7 @@ func DefaultConfig() *config.Config { StatCacheTTL: 300 * time.Second, ListOCMShares: true, PublicShareMustHavePassword: true, + IncludeOCMSharees: false, }, Middleware: config.Middleware{ Auth: config.Auth{ diff --git a/services/frontend/pkg/revaconfig/config.go b/services/frontend/pkg/revaconfig/config.go index be692a6d9a7..38dadd16b25 100644 --- a/services/frontend/pkg/revaconfig/config.go +++ b/services/frontend/pkg/revaconfig/config.go @@ -347,6 +347,7 @@ func FrontendConfigFromStruct(cfg *config.Config, logger log.Logger) (map[string "productversion": version.GetString(), }, }, + "include_ocm_sharees": cfg.OCS.IncludeOCMSharees, }, }, }, diff --git a/vendor/github.com/studio-b12/gowebdav/Makefile b/vendor/github.com/studio-b12/gowebdav/Makefile index da4be259d38..48dbb4e7233 100644 --- a/vendor/github.com/studio-b12/gowebdav/Makefile +++ b/vendor/github.com/studio-b12/gowebdav/Makefile @@ -11,22 +11,31 @@ ${BIN}: ${SRC} test: go test -modfile=go_test.mod -v -short -cover ./... -api: +api: .go/bin/godoc2md @sed '/^## API$$/,$$d' -i README.md @echo '## API' >> README.md - @godoc2md github.com/studio-b12/gowebdav | sed '/^$$/N;/^\n$$/D' |\ + @$< github.com/studio-b12/gowebdav | sed '/^$$/N;/^\n$$/D' |\ sed '2d' |\ sed 's/\/src\/github.com\/studio-b12\/gowebdav\//https:\/\/github.com\/studio-b12\/gowebdav\/blob\/master\//g' |\ sed 's/\/src\/target\//https:\/\/github.com\/studio-b12\/gowebdav\/blob\/master\//g' |\ sed 's/^#/##/g' >> README.md -check: +check: .go/bin/gocyclo gofmt -w -s $(SRC) @echo - gocyclo -over 15 . + .go/bin/gocyclo -over 15 . @echo go vet -modfile=go_test.mod ./... + +.go/bin/godoc2md: + @mkdir -p $(@D) + @GOPATH="$(CURDIR)/.go" go install github.com/davecheney/godoc2md@latest + +.go/bin/gocyclo: + @mkdir -p $(@D) + @GOPATH="$(CURDIR)/.go" go install github.com/fzipp/gocyclo/cmd/gocyclo@latest + clean: @rm -f ${BIN} diff --git a/vendor/github.com/studio-b12/gowebdav/README.md b/vendor/github.com/studio-b12/gowebdav/README.md index 50fc0d85675..5355bdbbc26 100644 --- a/vendor/github.com/studio-b12/gowebdav/README.md +++ b/vendor/github.com/studio-b12/gowebdav/README.md @@ -5,11 +5,12 @@ [![GoDoc](https://godoc.org/github.com/studio-b12/gowebdav?status.svg)](https://godoc.org/github.com/studio-b12/gowebdav) [![Go Report Card](https://goreportcard.com/badge/github.com/studio-b12/gowebdav)](https://goreportcard.com/report/github.com/studio-b12/gowebdav) -A golang WebDAV client library. +A pure Golang WebDAV client library that comes with a [reference implementation](https://github.com/studio-b12/gowebdav/tree/master/cmd/gowebdav). -## Main features +## Features at a glance + +Our `gowebdav` library allows to perform following actions on the remote WebDAV server: -`gowebdav` library allows to perform following actions on the remote WebDAV server: * [create path](#create-path-on-a-webdav-server) * [get files list](#get-files-list) * [download file](#download-file-to-byte-array) @@ -19,6 +20,17 @@ A golang WebDAV client library. * [copy file to another location](#copy-file-to-another-location) * [delete file](#delete-file) +It also provides an [authentication API](#type-authenticator) that makes it easy to encapsulate and control complex authentication challenges. +The default implementation negotiates the algorithm based on the user's preferences and the methods offered by the remote server. + +Out-of-box authentication support for: + +* [BasicAuth](https://en.wikipedia.org/wiki/Basic_access_authentication) +* [DigestAuth](https://en.wikipedia.org/wiki/Digest_access_authentication) +* [MS-PASS](https://github.com/studio-b12/gowebdav/pull/70#issuecomment-1421713726) +* [WIP Kerberos](https://github.com/studio-b12/gowebdav/pull/71#issuecomment-1416465334) +* [WIP Bearer Token](https://github.com/studio-b12/gowebdav/issues/61) + ## Usage First of all you should create `Client` instance using `NewClient()` function: @@ -29,11 +41,13 @@ user := "user" password := "password" c := gowebdav.NewClient(root, user, password) +c.Connect() +// kick of your work! ``` After you can use this `Client` to perform actions, described below. -**NOTICE:** we will not check errors in examples, to focus you on the `gowebdav` library's code, but you should do it in your code! +**NOTICE:** We will not check for errors in the examples, to focus you on the `gowebdav` library's code, but you should do it in your code! ### Create path on a WebDAV server ```go @@ -59,7 +73,7 @@ webdavFilePath := "folder/subfolder/file.txt" localFilePath := "/tmp/webdav/file.txt" bytes, _ := c.Read(webdavFilePath) -ioutil.WriteFile(localFilePath, bytes, 0644) +os.WriteFile(localFilePath, bytes, 0644) ``` ### Download file via reader @@ -81,7 +95,7 @@ io.Copy(file, reader) webdavFilePath := "folder/subfolder/file.txt" localFilePath := "/tmp/webdav/file.txt" -bytes, _ := ioutil.ReadFile(localFilePath) +bytes, _ := os.ReadFile(localFilePath) c.Write(webdavFilePath, bytes, 0644) ``` @@ -161,21 +175,34 @@ Package gowebdav is a WebDAV client library with a command line tool included. ### Index +* [Constants](#pkg-constants) +* [Variables](#pkg-variables) * [func FixSlash(s string) string](#FixSlash) * [func FixSlashes(s string) string](#FixSlashes) * [func IsErrCode(err error, code int) bool](#IsErrCode) * [func IsErrNotFound(err error) bool](#IsErrNotFound) * [func Join(path0 string, path1 string) string](#Join) +* [func NewPathError(op string, path string, statusCode int) error](#NewPathError) +* [func NewPathErrorErr(op string, path string, err error) error](#NewPathErrorErr) * [func PathEscape(path string) string](#PathEscape) * [func ReadConfig(uri, netrc string) (string, string)](#ReadConfig) * [func String(r io.Reader) string](#String) +* [type AuthFactory](#AuthFactory) * [type Authenticator](#Authenticator) + * [func NewDigestAuth(login, secret string, rs *http.Response) (Authenticator, error)](#NewDigestAuth) + * [func NewPassportAuth(c *http.Client, user, pw, partnerURL string, header *http.Header) (Authenticator, error)](#NewPassportAuth) +* [type Authorizer](#Authorizer) + * [func NewAutoAuth(login string, secret string) Authorizer](#NewAutoAuth) + * [func NewEmptyAuth() Authorizer](#NewEmptyAuth) + * [func NewPreemptiveAuth(auth Authenticator) Authorizer](#NewPreemptiveAuth) * [type BasicAuth](#BasicAuth) - * [func (b *BasicAuth) Authorize(req *http.Request, method string, path string)](#BasicAuth.Authorize) - * [func (b *BasicAuth) Pass() string](#BasicAuth.Pass) - * [func (b *BasicAuth) Type() string](#BasicAuth.Type) - * [func (b *BasicAuth) User() string](#BasicAuth.User) + * [func (b *BasicAuth) Authorize(c *http.Client, rq *http.Request, path string) error](#BasicAuth.Authorize) + * [func (b *BasicAuth) Clone() Authenticator](#BasicAuth.Clone) + * [func (b *BasicAuth) Close() error](#BasicAuth.Close) + * [func (b *BasicAuth) String() string](#BasicAuth.String) + * [func (b *BasicAuth) Verify(c *http.Client, rs *http.Response, path string) (redo bool, err error)](#BasicAuth.Verify) * [type Client](#Client) + * [func NewAuthClient(uri string, auth Authorizer) *Client](#NewAuthClient) * [func NewClient(uri, user, pw string) *Client](#NewClient) * [func (c *Client) Connect() error](#Client.Connect) * [func (c *Client) Copy(oldpath, newpath string, overwrite bool) error](#Client.Copy) @@ -190,16 +217,18 @@ included. * [func (c *Client) Rename(oldpath, newpath string, overwrite bool) error](#Client.Rename) * [func (c *Client) SetHeader(key, value string)](#Client.SetHeader) * [func (c *Client) SetInterceptor(interceptor func(method string, rq *http.Request))](#Client.SetInterceptor) + * [func (c *Client) SetJar(jar http.CookieJar)](#Client.SetJar) * [func (c *Client) SetTimeout(timeout time.Duration)](#Client.SetTimeout) * [func (c *Client) SetTransport(transport http.RoundTripper)](#Client.SetTransport) * [func (c *Client) Stat(path string) (os.FileInfo, error)](#Client.Stat) * [func (c *Client) Write(path string, data []byte, _ os.FileMode) (err error)](#Client.Write) * [func (c *Client) WriteStream(path string, stream io.Reader, _ os.FileMode) (err error)](#Client.WriteStream) * [type DigestAuth](#DigestAuth) - * [func (d *DigestAuth) Authorize(req *http.Request, method string, path string)](#DigestAuth.Authorize) - * [func (d *DigestAuth) Pass() string](#DigestAuth.Pass) - * [func (d *DigestAuth) Type() string](#DigestAuth.Type) - * [func (d *DigestAuth) User() string](#DigestAuth.User) + * [func (d *DigestAuth) Authorize(c *http.Client, rq *http.Request, path string) error](#DigestAuth.Authorize) + * [func (d *DigestAuth) Clone() Authenticator](#DigestAuth.Clone) + * [func (d *DigestAuth) Close() error](#DigestAuth.Close) + * [func (d *DigestAuth) String() string](#DigestAuth.String) + * [func (d *DigestAuth) Verify(c *http.Client, rs *http.Response, path string) (redo bool, err error)](#DigestAuth.Verify) * [type File](#File) * [func (f File) ContentType() string](#File.ContentType) * [func (f File) ETag() string](#File.ETag) @@ -211,11 +240,12 @@ included. * [func (f File) Size() int64](#File.Size) * [func (f File) String() string](#File.String) * [func (f File) Sys() interface{}](#File.Sys) -* [type NoAuth](#NoAuth) - * [func (n *NoAuth) Authorize(req *http.Request, method string, path string)](#NoAuth.Authorize) - * [func (n *NoAuth) Pass() string](#NoAuth.Pass) - * [func (n *NoAuth) Type() string](#NoAuth.Type) - * [func (n *NoAuth) User() string](#NoAuth.User) +* [type PassportAuth](#PassportAuth) + * [func (p *PassportAuth) Authorize(c *http.Client, rq *http.Request, path string) error](#PassportAuth.Authorize) + * [func (p *PassportAuth) Clone() Authenticator](#PassportAuth.Clone) + * [func (p *PassportAuth) Close() error](#PassportAuth.Close) + * [func (p *PassportAuth) String() string](#PassportAuth.String) + * [func (p *PassportAuth) Verify(c *http.Client, rs *http.Response, path string) (redo bool, err error)](#PassportAuth.Verify) * [type StatusError](#StatusError) * [func (se StatusError) Error() string](#StatusError.Error) @@ -223,7 +253,24 @@ included. * [PathEscape](#example_PathEscape) ##### Package files -[basicAuth.go](https://github.com/studio-b12/gowebdav/blob/master/basicAuth.go) [client.go](https://github.com/studio-b12/gowebdav/blob/master/client.go) [digestAuth.go](https://github.com/studio-b12/gowebdav/blob/master/digestAuth.go) [doc.go](https://github.com/studio-b12/gowebdav/blob/master/doc.go) [errors.go](https://github.com/studio-b12/gowebdav/blob/master/errors.go) [file.go](https://github.com/studio-b12/gowebdav/blob/master/file.go) [netrc.go](https://github.com/studio-b12/gowebdav/blob/master/netrc.go) [requests.go](https://github.com/studio-b12/gowebdav/blob/master/requests.go) [utils.go](https://github.com/studio-b12/gowebdav/blob/master/utils.go) +[auth.go](https://github.com/studio-b12/gowebdav/blob/master/auth.go) [basicAuth.go](https://github.com/studio-b12/gowebdav/blob/master/basicAuth.go) [client.go](https://github.com/studio-b12/gowebdav/blob/master/client.go) [digestAuth.go](https://github.com/studio-b12/gowebdav/blob/master/digestAuth.go) [doc.go](https://github.com/studio-b12/gowebdav/blob/master/doc.go) [errors.go](https://github.com/studio-b12/gowebdav/blob/master/errors.go) [file.go](https://github.com/studio-b12/gowebdav/blob/master/file.go) [netrc.go](https://github.com/studio-b12/gowebdav/blob/master/netrc.go) [passportAuth.go](https://github.com/studio-b12/gowebdav/blob/master/passportAuth.go) [requests.go](https://github.com/studio-b12/gowebdav/blob/master/requests.go) [utils.go](https://github.com/studio-b12/gowebdav/blob/master/utils.go) + +### Constants +``` go +const XInhibitRedirect = "X-Gowebdav-Inhibit-Redirect" +``` + +### Variables +``` go +var ErrAuthChanged = errors.New("authentication failed, change algorithm") +``` +ErrAuthChanged must be returned from the Verify method as an error +to trigger a re-authentication / negotiation with a new authenticator. + +``` go +var ErrTooManyRedirects = errors.New("stopped after 10 redirects") +``` +ErrTooManyRedirects will be used as return error if a request exceeds 10 redirects. ### func [FixSlash](https://github.com/studio-b12/gowebdav/blob/master/utils.go?s=354:384#L23) ``` go @@ -237,7 +284,7 @@ func FixSlashes(s string) string ``` FixSlashes appends and prepends a / if they are missing -### func [IsErrCode](https://github.com/studio-b12/gowebdav/blob/master/errors.go?s=355:395#L21) +### func [IsErrCode](https://github.com/studio-b12/gowebdav/blob/master/errors.go?s=740:780#L29) ``` go func IsErrCode(err error, code int) bool ``` @@ -245,7 +292,7 @@ IsErrCode returns true if the given error is an os.PathError wrapping a StatusError with the given status code. -### func [IsErrNotFound](https://github.com/studio-b12/gowebdav/blob/master/errors.go?s=587:621#L31) +### func [IsErrNotFound](https://github.com/studio-b12/gowebdav/blob/master/errors.go?s=972:1006#L39) ``` go func IsErrNotFound(err error) bool ``` @@ -258,6 +305,16 @@ func Join(path0 string, path1 string) string ``` Join joins two paths +### func [NewPathError](https://github.com/studio-b12/gowebdav/blob/master/errors.go?s=1040:1103#L43) +``` go +func NewPathError(op string, path string, statusCode int) error +``` + +### func [NewPathErrorErr](https://github.com/studio-b12/gowebdav/blob/master/errors.go?s=1194:1255#L51) +``` go +func NewPathErrorErr(op string, path string, err error) error +``` + ### func [PathEscape](https://github.com/studio-b12/gowebdav/blob/master/utils.go?s=153:188#L14) ``` go func PathEscape(path string) string @@ -277,18 +334,119 @@ func String(r io.Reader) string ``` String pulls a string out of our io.Reader -### type [Authenticator](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=388:507#L29) +### type [AuthFactory](https://github.com/studio-b12/gowebdav/blob/master/auth.go?s=150:251#L13) +``` go +type AuthFactory func(c *http.Client, rs *http.Response, path string) (auth Authenticator, err error) +``` +AuthFactory prototype function to create a new Authenticator + +### type [Authenticator](https://github.com/studio-b12/gowebdav/blob/master/auth.go?s=2155:2695#L56) ``` go type Authenticator interface { - Type() string - User() string - Pass() string - Authorize(*http.Request, string, string) + // Authorizes a request. Usually by adding some authorization headers. + Authorize(c *http.Client, rq *http.Request, path string) error + // Verifies the response if the authorization was successful. + // May trigger some round trips to pass the authentication. + // May also trigger a new Authenticator negotiation by returning `ErrAuthChenged` + Verify(c *http.Client, rs *http.Response, path string) (redo bool, err error) + // Creates a copy of the underlying Authenticator. + Clone() Authenticator + io.Closer } ``` -Authenticator stub +A Authenticator implements a specific way to authorize requests. +Each request is bound to a separate Authenticator instance. + +The authentication flow itself is broken down into `Authorize` +and `Verify` steps. The former method runs before, and the latter +runs after the `Request` is submitted. +This makes it easy to encapsulate and control complex +authentication challenges. + +Some authentication flows causing authentication round trips, +which can be archived by returning the `redo` of the Verify +method. `True` restarts the authentication process for the +current action: A new `Request` is spawned, which must be +authorized, sent, and re-verified again, until the action +is successfully submitted. +The preferred way is to handle the authentication ping-pong +within `Verify`, and then `redo` with fresh credentials. + +The result of the `Verify` method can also trigger an +`Authenticator` change by returning the `ErrAuthChanged` +as an error. Depending on the `Authorizer` this may trigger +an `Authenticator` negotiation. + +Set the `XInhibitRedirect` header to '1' in the `Authorize` +method to get control over request redirection. +Attention! You must handle the incoming request yourself. + +To store a shared session state the `Clone` method **must** +return a new instance, initialized with the shared state. + +#### func [NewDigestAuth](https://github.com/studio-b12/gowebdav/blob/master/digestAuth.go?s=324:406#L21) +``` go +func NewDigestAuth(login, secret string, rs *http.Response) (Authenticator, error) +``` +NewDigestAuth creates a new instance of our Digest Authenticator -### type [BasicAuth](https://github.com/studio-b12/gowebdav/blob/master/basicAuth.go?s=106:157#L9) +#### func [NewPassportAuth](https://github.com/studio-b12/gowebdav/blob/master/passportAuth.go?s=386:495#L21) +``` go +func NewPassportAuth(c *http.Client, user, pw, partnerURL string, header *http.Header) (Authenticator, error) +``` +constructor for PassportAuth creates a new PassportAuth object and +automatically authenticates against the given partnerURL + +### type [Authorizer](https://github.com/studio-b12/gowebdav/blob/master/auth.go?s=349:764#L17) +``` go +type Authorizer interface { + // Creates a new Authenticator Shim per request. + // It may track request related states and perform payload buffering + // for authentication round trips. + // The underlying Authenticator will perform the real authentication. + NewAuthenticator(body io.Reader) (Authenticator, io.Reader) + // Registers a new Authenticator factory to a key. + AddAuthenticator(key string, fn AuthFactory) +} +``` +Authorizer our Authenticator factory which creates an +`Authenticator` per action/request. + +#### func [NewAutoAuth](https://github.com/studio-b12/gowebdav/blob/master/auth.go?s=3789:3845#L109) +``` go +func NewAutoAuth(login string, secret string) Authorizer +``` +NewAutoAuth creates an auto Authenticator factory. +It negotiates the default authentication method +based on the order of the registered Authenticators +and the remotely offered authentication methods. +First In, First Out. + +#### func [NewEmptyAuth](https://github.com/studio-b12/gowebdav/blob/master/auth.go?s=4694:4724#L132) +``` go +func NewEmptyAuth() Authorizer +``` +NewEmptyAuth creates an empty Authenticator factory +The order of adding the Authenticator matters. +First In, First Out. +It offers the `NewAutoAuth` features. + +#### func [NewPreemptiveAuth](https://github.com/studio-b12/gowebdav/blob/master/auth.go?s=5300:5353#L148) +``` go +func NewPreemptiveAuth(auth Authenticator) Authorizer +``` +NewPreemptiveAuth creates a preemptive Authenticator +The preemptive authorizer uses the provided Authenticator +for every request regardless of any `Www-Authenticate` header. + +It may only have one authentication method, +so calling `AddAuthenticator` **will panic**! + +Look out!! This offers the skinniest and slickest implementation +without any synchronisation!! +Still applicable with `BasicAuth` within go routines. + +### type [BasicAuth](https://github.com/studio-b12/gowebdav/blob/master/basicAuth.go?s=94:145#L9) ``` go type BasicAuth struct { // contains filtered or unexported fields @@ -297,31 +455,37 @@ type BasicAuth struct { ``` BasicAuth structure holds our credentials -#### func (\*BasicAuth) [Authorize](https://github.com/studio-b12/gowebdav/blob/master/basicAuth.go?s=473:549#L30) +#### func (\*BasicAuth) [Authorize](https://github.com/studio-b12/gowebdav/blob/master/basicAuth.go?s=180:262#L15) ``` go -func (b *BasicAuth) Authorize(req *http.Request, method string, path string) +func (b *BasicAuth) Authorize(c *http.Client, rq *http.Request, path string) error ``` Authorize the current request -#### func (\*BasicAuth) [Pass](https://github.com/studio-b12/gowebdav/blob/master/basicAuth.go?s=388:421#L25) +#### func (\*BasicAuth) [Clone](https://github.com/studio-b12/gowebdav/blob/master/basicAuth.go?s=666:707#L34) ``` go -func (b *BasicAuth) Pass() string +func (b *BasicAuth) Clone() Authenticator ``` -Pass holds the BasicAuth password +Clone creates a Copy of itself -#### func (\*BasicAuth) [Type](https://github.com/studio-b12/gowebdav/blob/master/basicAuth.go?s=201:234#L15) +#### func (\*BasicAuth) [Close](https://github.com/studio-b12/gowebdav/blob/master/basicAuth.go?s=581:614#L29) ``` go -func (b *BasicAuth) Type() string +func (b *BasicAuth) Close() error ``` -Type identifies the BasicAuthenticator +Close cleans up all resources -#### func (\*BasicAuth) [User](https://github.com/studio-b12/gowebdav/blob/master/basicAuth.go?s=297:330#L20) +#### func (\*BasicAuth) [String](https://github.com/studio-b12/gowebdav/blob/master/basicAuth.go?s=778:813#L40) ``` go -func (b *BasicAuth) User() string +func (b *BasicAuth) String() string ``` -User holds the BasicAuth username +String toString -### type [Client](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=172:364#L18) +#### func (\*BasicAuth) [Verify](https://github.com/studio-b12/gowebdav/blob/master/basicAuth.go?s=352:449#L21) +``` go +func (b *BasicAuth) Verify(c *http.Client, rs *http.Response, path string) (redo bool, err error) +``` +Verify verifies if the authentication + +### type [Client](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=220:388#L19) ``` go type Client struct { // contains filtered or unexported fields @@ -330,55 +494,61 @@ type Client struct { ``` Client defines our structure -#### func [NewClient](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=1019:1063#L62) +#### func [NewAuthClient](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=608:663#L33) +``` go +func NewAuthClient(uri string, auth Authorizer) *Client +``` +NewAuthClient creates a new client instance with a custom Authorizer + +#### func [NewClient](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=436:480#L28) ``` go func NewClient(uri, user, pw string) *Client ``` NewClient creates a new instance of client -#### func (\*Client) [Connect](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=1843:1875#L87) +#### func (\*Client) [Connect](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=1829:1861#L74) ``` go func (c *Client) Connect() error ``` Connect connects to our dav server -#### func (\*Client) [Copy](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=6818:6886#L323) +#### func (\*Client) [Copy](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=6815:6883#L310) ``` go func (c *Client) Copy(oldpath, newpath string, overwrite bool) error ``` Copy copies a file from A to B -#### func (\*Client) [Mkdir](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=5793:5855#L272) +#### func (\*Client) [Mkdir](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=5790:5852#L259) ``` go func (c *Client) Mkdir(path string, _ os.FileMode) (err error) ``` Mkdir makes a directory -#### func (\*Client) [MkdirAll](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=6068:6133#L286) +#### func (\*Client) [MkdirAll](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=6065:6130#L273) ``` go func (c *Client) MkdirAll(path string, _ os.FileMode) (err error) ``` MkdirAll like mkdir -p, but for webdav -#### func (\*Client) [Read](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=6992:7042#L328) +#### func (\*Client) [Read](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=6989:7039#L315) ``` go func (c *Client) Read(path string) ([]byte, error) ``` Read reads the contents of a remote file -#### func (\*Client) [ReadDir](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=2869:2929#L130) +#### func (\*Client) [ReadDir](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=2855:2915#L117) ``` go func (c *Client) ReadDir(path string) ([]os.FileInfo, error) ``` ReadDir reads the contents of a remote directory -#### func (\*Client) [ReadStream](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=7353:7416#L346) +#### func (\*Client) [ReadStream](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=7350:7413#L333) ``` go func (c *Client) ReadStream(path string) (io.ReadCloser, error) ``` ReadStream reads the stream for a given path -#### func (\*Client) [ReadStreamRange](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=8165:8255#L368) +#### func (\*Client) [ReadStreamRange](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=8162:8252#L355) ``` go func (c *Client) ReadStreamRange(path string, offset, length int64) (io.ReadCloser, error) ``` @@ -391,61 +561,67 @@ If the server does not support partial content requests and returns full content this function will emulate the behavior by skipping `offset` bytes and limiting the result to `length`. -#### func (\*Client) [Remove](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=5299:5341#L249) +#### func (\*Client) [Remove](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=5296:5338#L236) ``` go func (c *Client) Remove(path string) error ``` Remove removes a remote file -#### func (\*Client) [RemoveAll](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=5407:5452#L254) +#### func (\*Client) [RemoveAll](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=5404:5449#L241) ``` go func (c *Client) RemoveAll(path string) error ``` RemoveAll removes remote files -#### func (\*Client) [Rename](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=6652:6722#L318) +#### func (\*Client) [Rename](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=6649:6719#L305) ``` go func (c *Client) Rename(oldpath, newpath string, overwrite bool) error ``` Rename moves a file from A to B -#### func (\*Client) [SetHeader](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=1235:1280#L67) +#### func (\*Client) [SetHeader](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=1092:1137#L49) ``` go func (c *Client) SetHeader(key, value string) ``` SetHeader lets us set arbitrary headers for a given client -#### func (\*Client) [SetInterceptor](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=1387:1469#L72) +#### func (\*Client) [SetInterceptor](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=1244:1326#L54) ``` go func (c *Client) SetInterceptor(interceptor func(method string, rq *http.Request)) ``` SetInterceptor lets us set an arbitrary interceptor for a given client -#### func (\*Client) [SetTimeout](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=1571:1621#L77) +#### func (\*Client) [SetJar](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=1727:1770#L69) +``` go +func (c *Client) SetJar(jar http.CookieJar) +``` +SetJar exposes the ability to set a cookie jar to the client. + +#### func (\*Client) [SetTimeout](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=1428:1478#L59) ``` go func (c *Client) SetTimeout(timeout time.Duration) ``` SetTimeout exposes the ability to set a time limit for requests -#### func (\*Client) [SetTransport](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=1714:1772#L82) +#### func (\*Client) [SetTransport](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=1571:1629#L64) ``` go func (c *Client) SetTransport(transport http.RoundTripper) ``` SetTransport exposes the ability to define custom transports -#### func (\*Client) [Stat](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=4255:4310#L197) +#### func (\*Client) [Stat](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=4241:4296#L184) ``` go func (c *Client) Stat(path string) (os.FileInfo, error) ``` Stat returns the file stats for a specified path -#### func (\*Client) [Write](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=9260:9335#L402) +#### func (\*Client) [Write](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=9272:9347#L389) ``` go func (c *Client) Write(path string, data []byte, _ os.FileMode) (err error) ``` Write writes data to a given path -#### func (\*Client) [WriteStream](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=9759:9845#L432) +#### func (\*Client) [WriteStream](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=9771:9857#L419) ``` go func (c *Client) WriteStream(path string, stream io.Reader, _ os.FileMode) (err error) ``` @@ -460,29 +636,35 @@ type DigestAuth struct { ``` DigestAuth structure holds our credentials -#### func (\*DigestAuth) [Authorize](https://github.com/studio-b12/gowebdav/blob/master/digestAuth.go?s=577:654#L36) +#### func (\*DigestAuth) [Authorize](https://github.com/studio-b12/gowebdav/blob/master/digestAuth.go?s=525:608#L26) ``` go -func (d *DigestAuth) Authorize(req *http.Request, method string, path string) +func (d *DigestAuth) Authorize(c *http.Client, rq *http.Request, path string) error ``` Authorize the current request -#### func (\*DigestAuth) [Pass](https://github.com/studio-b12/gowebdav/blob/master/digestAuth.go?s=491:525#L31) +#### func (\*DigestAuth) [Clone](https://github.com/studio-b12/gowebdav/blob/master/digestAuth.go?s=1228:1270#L49) +``` go +func (d *DigestAuth) Clone() Authenticator +``` +Clone creates a copy of itself + +#### func (\*DigestAuth) [Close](https://github.com/studio-b12/gowebdav/blob/master/digestAuth.go?s=1142:1176#L44) ``` go -func (d *DigestAuth) Pass() string +func (d *DigestAuth) Close() error ``` -Pass holds the DigestAuth password +Close cleans up all resources -#### func (\*DigestAuth) [Type](https://github.com/studio-b12/gowebdav/blob/master/digestAuth.go?s=299:333#L21) +#### func (\*DigestAuth) [String](https://github.com/studio-b12/gowebdav/blob/master/digestAuth.go?s=1466:1502#L58) ``` go -func (d *DigestAuth) Type() string +func (d *DigestAuth) String() string ``` -Type identifies the DigestAuthenticator +String toString -#### func (\*DigestAuth) [User](https://github.com/studio-b12/gowebdav/blob/master/digestAuth.go?s=398:432#L26) +#### func (\*DigestAuth) [Verify](https://github.com/studio-b12/gowebdav/blob/master/digestAuth.go?s=912:1010#L36) ``` go -func (d *DigestAuth) User() string +func (d *DigestAuth) Verify(c *http.Client, rs *http.Response, path string) (redo bool, err error) ``` -User holds the DigestAuth username +Verify checks for authentication issues and may trigger a re-authentication ### type [File](https://github.com/studio-b12/gowebdav/blob/master/file.go?s=93:253#L10) ``` go @@ -553,40 +735,46 @@ func (f File) Sys() interface{} ``` Sys ???? -### type [NoAuth](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=551:599#L37) +### type [PassportAuth](https://github.com/studio-b12/gowebdav/blob/master/passportAuth.go?s=125:254#L12) ``` go -type NoAuth struct { +type PassportAuth struct { // contains filtered or unexported fields } ``` -NoAuth structure holds our credentials +PassportAuth structure holds our credentials -#### func (\*NoAuth) [Authorize](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=894:967#L58) +#### func (\*PassportAuth) [Authorize](https://github.com/studio-b12/gowebdav/blob/master/passportAuth.go?s=690:775#L32) ``` go -func (n *NoAuth) Authorize(req *http.Request, method string, path string) +func (p *PassportAuth) Authorize(c *http.Client, rq *http.Request, path string) error ``` Authorize the current request -#### func (\*NoAuth) [Pass](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=812:842#L53) +#### func (\*PassportAuth) [Clone](https://github.com/studio-b12/gowebdav/blob/master/passportAuth.go?s=1701:1745#L69) +``` go +func (p *PassportAuth) Clone() Authenticator +``` +Clone creates a Copy of itself + +#### func (\*PassportAuth) [Close](https://github.com/studio-b12/gowebdav/blob/master/passportAuth.go?s=1613:1649#L64) ``` go -func (n *NoAuth) Pass() string +func (p *PassportAuth) Close() error ``` -Pass returns the current password +Close cleans up all resources -#### func (\*NoAuth) [Type](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=638:668#L43) +#### func (\*PassportAuth) [String](https://github.com/studio-b12/gowebdav/blob/master/passportAuth.go?s=2048:2086#L83) ``` go -func (n *NoAuth) Type() string +func (p *PassportAuth) String() string ``` -Type identifies the authenticator +String toString -#### func (\*NoAuth) [User](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=724:754#L48) +#### func (\*PassportAuth) [Verify](https://github.com/studio-b12/gowebdav/blob/master/passportAuth.go?s=1075:1175#L46) ``` go -func (n *NoAuth) User() string +func (p *PassportAuth) Verify(c *http.Client, rs *http.Response, path string) (redo bool, err error) ``` -User returns the current user +Verify verifies if the authentication is good -### type [StatusError](https://github.com/studio-b12/gowebdav/blob/master/errors.go?s=114:153#L10) +### type [StatusError](https://github.com/studio-b12/gowebdav/blob/master/errors.go?s=499:538#L18) ``` go type StatusError struct { Status int @@ -596,7 +784,7 @@ type StatusError struct { StatusError implements error and wraps an erroneous status code. -#### func (StatusError) [Error](https://github.com/studio-b12/gowebdav/blob/master/errors.go?s=155:191#L14) +#### func (StatusError) [Error](https://github.com/studio-b12/gowebdav/blob/master/errors.go?s=540:576#L22) ``` go func (se StatusError) Error() string ``` diff --git a/vendor/github.com/studio-b12/gowebdav/auth.go b/vendor/github.com/studio-b12/gowebdav/auth.go new file mode 100644 index 00000000000..7842bee1988 --- /dev/null +++ b/vendor/github.com/studio-b12/gowebdav/auth.go @@ -0,0 +1,409 @@ +package gowebdav + +import ( + "bytes" + "errors" + "io" + "net/http" + "strings" + "sync" +) + +// AuthFactory prototype function to create a new Authenticator +type AuthFactory func(c *http.Client, rs *http.Response, path string) (auth Authenticator, err error) + +// Authorizer our Authenticator factory which creates an +// `Authenticator` per action/request. +type Authorizer interface { + // Creates a new Authenticator Shim per request. + // It may track request related states and perform payload buffering + // for authentication round trips. + // The underlying Authenticator will perform the real authentication. + NewAuthenticator(body io.Reader) (Authenticator, io.Reader) + // Registers a new Authenticator factory to a key. + AddAuthenticator(key string, fn AuthFactory) +} + +// A Authenticator implements a specific way to authorize requests. +// Each request is bound to a separate Authenticator instance. +// +// The authentication flow itself is broken down into `Authorize` +// and `Verify` steps. The former method runs before, and the latter +// runs after the `Request` is submitted. +// This makes it easy to encapsulate and control complex +// authentication challenges. +// +// Some authentication flows causing authentication round trips, +// which can be archived by returning the `redo` of the Verify +// method. `True` restarts the authentication process for the +// current action: A new `Request` is spawned, which must be +// authorized, sent, and re-verified again, until the action +// is successfully submitted. +// The preferred way is to handle the authentication ping-pong +// within `Verify`, and then `redo` with fresh credentials. +// +// The result of the `Verify` method can also trigger an +// `Authenticator` change by returning the `ErrAuthChanged` +// as an error. Depending on the `Authorizer` this may trigger +// an `Authenticator` negotiation. +// +// Set the `XInhibitRedirect` header to '1' in the `Authorize` +// method to get control over request redirection. +// Attention! You must handle the incoming request yourself. +// +// To store a shared session state the `Clone` method **must** +// return a new instance, initialized with the shared state. +type Authenticator interface { + // Authorizes a request. Usually by adding some authorization headers. + Authorize(c *http.Client, rq *http.Request, path string) error + // Verifies the response if the authorization was successful. + // May trigger some round trips to pass the authentication. + // May also trigger a new Authenticator negotiation by returning `ErrAuthChenged` + Verify(c *http.Client, rs *http.Response, path string) (redo bool, err error) + // Creates a copy of the underlying Authenticator. + Clone() Authenticator + io.Closer +} + +type authfactory struct { + key string + create AuthFactory +} + +// authorizer structure holds our Authenticator create functions +type authorizer struct { + factories []authfactory + defAuthMux sync.Mutex + defAuth Authenticator +} + +// preemptiveAuthorizer structure holds the preemptive Authenticator +type preemptiveAuthorizer struct { + auth Authenticator +} + +// authShim structure that wraps the real Authenticator +type authShim struct { + factory AuthFactory + body io.Reader + auth Authenticator +} + +// negoAuth structure holds the authenticators that are going to be negotiated +type negoAuth struct { + auths []Authenticator + setDefaultAuthenticator func(auth Authenticator) +} + +// nullAuth initializes the whole authentication flow +type nullAuth struct{} + +// noAuth structure to perform no authentication at all +type noAuth struct{} + +// NewAutoAuth creates an auto Authenticator factory. +// It negotiates the default authentication method +// based on the order of the registered Authenticators +// and the remotely offered authentication methods. +// First In, First Out. +func NewAutoAuth(login string, secret string) Authorizer { + fmap := make([]authfactory, 0) + az := &authorizer{factories: fmap, defAuthMux: sync.Mutex{}, defAuth: &nullAuth{}} + + az.AddAuthenticator("basic", func(c *http.Client, rs *http.Response, path string) (auth Authenticator, err error) { + return &BasicAuth{user: login, pw: secret}, nil + }) + + az.AddAuthenticator("digest", func(c *http.Client, rs *http.Response, path string) (auth Authenticator, err error) { + return NewDigestAuth(login, secret, rs) + }) + + az.AddAuthenticator("passport1.4", func(c *http.Client, rs *http.Response, path string) (auth Authenticator, err error) { + return NewPassportAuth(c, login, secret, rs.Request.URL.String(), &rs.Header) + }) + + return az +} + +// NewEmptyAuth creates an empty Authenticator factory +// The order of adding the Authenticator matters. +// First In, First Out. +// It offers the `NewAutoAuth` features. +func NewEmptyAuth() Authorizer { + fmap := make([]authfactory, 0) + az := &authorizer{factories: fmap, defAuthMux: sync.Mutex{}, defAuth: &nullAuth{}} + return az +} + +// NewPreemptiveAuth creates a preemptive Authenticator +// The preemptive authorizer uses the provided Authenticator +// for every request regardless of any `Www-Authenticate` header. +// +// It may only have one authentication method, +// so calling `AddAuthenticator` **will panic**! +// +// Look out!! This offers the skinniest and slickest implementation +// without any synchronisation!! +// Still applicable with `BasicAuth` within go routines. +func NewPreemptiveAuth(auth Authenticator) Authorizer { + return &preemptiveAuthorizer{auth: auth} +} + +// NewAuthenticator creates an Authenticator (Shim) per request +func (a *authorizer) NewAuthenticator(body io.Reader) (Authenticator, io.Reader) { + var retryBuf io.Reader = body + if body != nil { + // If the authorization fails, we will need to restart reading + // from the passed body stream. + // When body is seekable, use seek to reset the streams + // cursor to the start. + // Otherwise, copy the stream into a buffer while uploading + // and use the buffers content on retry. + if _, ok := retryBuf.(io.Seeker); ok { + body = io.NopCloser(body) + } else { + buff := &bytes.Buffer{} + retryBuf = buff + body = io.TeeReader(body, buff) + } + } + a.defAuthMux.Lock() + defAuth := a.defAuth.Clone() + a.defAuthMux.Unlock() + + return &authShim{factory: a.factory, body: retryBuf, auth: defAuth}, body +} + +// AddAuthenticator appends the AuthFactory to our factories. +// It converts the key to lower case and preserves the order. +func (a *authorizer) AddAuthenticator(key string, fn AuthFactory) { + key = strings.ToLower(key) + for _, f := range a.factories { + if f.key == key { + panic("Authenticator exists: " + key) + } + } + a.factories = append(a.factories, authfactory{key, fn}) +} + +// factory picks all valid Authenticators based on Www-Authenticate headers +func (a *authorizer) factory(c *http.Client, rs *http.Response, path string) (auth Authenticator, err error) { + headers := rs.Header.Values("Www-Authenticate") + if len(headers) > 0 { + auths := make([]Authenticator, 0) + for _, f := range a.factories { + for _, header := range headers { + headerLower := strings.ToLower(header) + if strings.Contains(headerLower, f.key) { + rs.Header.Set("Www-Authenticate", header) + if auth, err = f.create(c, rs, path); err == nil { + auths = append(auths, auth) + break + } + } + } + } + + switch len(auths) { + case 0: + return nil, NewPathError("NoAuthenticator", path, rs.StatusCode) + case 1: + auth = auths[0] + default: + auth = &negoAuth{auths: auths, setDefaultAuthenticator: a.setDefaultAuthenticator} + } + } else { + auth = &noAuth{} + } + + a.setDefaultAuthenticator(auth) + + return auth, nil +} + +// setDefaultAuthenticator sets the default Authenticator +func (a *authorizer) setDefaultAuthenticator(auth Authenticator) { + a.defAuthMux.Lock() + a.defAuth.Close() + a.defAuth = auth + a.defAuthMux.Unlock() +} + +// Authorize the current request +func (s *authShim) Authorize(c *http.Client, rq *http.Request, path string) error { + if err := s.auth.Authorize(c, rq, path); err != nil { + return err + } + body := s.body + rq.GetBody = func() (io.ReadCloser, error) { + if body != nil { + if sk, ok := body.(io.Seeker); ok { + if _, err := sk.Seek(0, io.SeekStart); err != nil { + return nil, err + } + } + return io.NopCloser(body), nil + } + return nil, nil + } + return nil +} + +// Verify checks for authentication issues and may trigger a re-authentication. +// Catches AlgoChangedErr to update the current Authenticator +func (s *authShim) Verify(c *http.Client, rs *http.Response, path string) (redo bool, err error) { + redo, err = s.auth.Verify(c, rs, path) + if err != nil && errors.Is(err, ErrAuthChanged) { + if auth, aerr := s.factory(c, rs, path); aerr == nil { + s.auth.Close() + s.auth = auth + return true, nil + } else { + return false, aerr + } + } + return +} + +// Close closes all resources +func (s *authShim) Close() error { + s.auth.Close() + s.auth, s.factory = nil, nil + if s.body != nil { + if closer, ok := s.body.(io.Closer); ok { + return closer.Close() + } + } + return nil +} + +// It's not intend to Clone the shim +// therefore it returns a noAuth instance +func (s *authShim) Clone() Authenticator { + return &noAuth{} +} + +// String toString +func (s *authShim) String() string { + return "AuthShim" +} + +// Authorize authorizes the current request with the top most Authorizer +func (n *negoAuth) Authorize(c *http.Client, rq *http.Request, path string) error { + if len(n.auths) == 0 { + return NewPathError("NoAuthenticator", path, 400) + } + return n.auths[0].Authorize(c, rq, path) +} + +// Verify verifies the authentication and selects the next one based on the result +func (n *negoAuth) Verify(c *http.Client, rs *http.Response, path string) (redo bool, err error) { + if len(n.auths) == 0 { + return false, NewPathError("NoAuthenticator", path, 400) + } + redo, err = n.auths[0].Verify(c, rs, path) + if err != nil { + if len(n.auths) > 1 { + n.auths[0].Close() + n.auths = n.auths[1:] + return true, nil + } + } else if redo { + return + } else { + auth := n.auths[0] + n.auths = n.auths[1:] + n.setDefaultAuthenticator(auth) + return + } + + return false, NewPathError("NoAuthenticator", path, rs.StatusCode) +} + +// Close will close the underlying authenticators. +func (n *negoAuth) Close() error { + for _, a := range n.auths { + a.Close() + } + n.setDefaultAuthenticator = nil + return nil +} + +// Clone clones the underlying authenticators. +func (n *negoAuth) Clone() Authenticator { + auths := make([]Authenticator, len(n.auths)) + for i, e := range n.auths { + auths[i] = e.Clone() + } + return &negoAuth{auths: auths, setDefaultAuthenticator: n.setDefaultAuthenticator} +} + +func (n *negoAuth) String() string { + return "NegoAuth" +} + +// Authorize the current request +func (n *noAuth) Authorize(c *http.Client, rq *http.Request, path string) error { + return nil +} + +// Verify checks for authentication issues and may trigger a re-authentication +func (n *noAuth) Verify(c *http.Client, rs *http.Response, path string) (redo bool, err error) { + if "" != rs.Header.Get("Www-Authenticate") { + err = ErrAuthChanged + } + return +} + +// Close closes all resources +func (n *noAuth) Close() error { + return nil +} + +// Clone creates a copy of itself +func (n *noAuth) Clone() Authenticator { + // no copy due to read only access + return n +} + +// String toString +func (n *noAuth) String() string { + return "NoAuth" +} + +// Authorize the current request +func (n *nullAuth) Authorize(c *http.Client, rq *http.Request, path string) error { + rq.Header.Set(XInhibitRedirect, "1") + return nil +} + +// Verify checks for authentication issues and may trigger a re-authentication +func (n *nullAuth) Verify(c *http.Client, rs *http.Response, path string) (redo bool, err error) { + return true, ErrAuthChanged +} + +// Close closes all resources +func (n *nullAuth) Close() error { + return nil +} + +// Clone creates a copy of itself +func (n *nullAuth) Clone() Authenticator { + // no copy due to read only access + return n +} + +// String toString +func (n *nullAuth) String() string { + return "NullAuth" +} + +// NewAuthenticator creates an Authenticator (Shim) per request +func (b *preemptiveAuthorizer) NewAuthenticator(body io.Reader) (Authenticator, io.Reader) { + return b.auth.Clone(), body +} + +// AddAuthenticator Will PANIC because it may only have a single authentication method +func (b *preemptiveAuthorizer) AddAuthenticator(key string, fn AuthFactory) { + panic("You're funny! A preemptive authorizer may only have a single authentication method") +} diff --git a/vendor/github.com/studio-b12/gowebdav/basicAuth.go b/vendor/github.com/studio-b12/gowebdav/basicAuth.go index bdb86da580c..5511a60f6cb 100644 --- a/vendor/github.com/studio-b12/gowebdav/basicAuth.go +++ b/vendor/github.com/studio-b12/gowebdav/basicAuth.go @@ -1,7 +1,7 @@ package gowebdav import ( - "encoding/base64" + "fmt" "net/http" ) @@ -11,24 +11,32 @@ type BasicAuth struct { pw string } -// Type identifies the BasicAuthenticator -func (b *BasicAuth) Type() string { - return "BasicAuth" +// Authorize the current request +func (b *BasicAuth) Authorize(c *http.Client, rq *http.Request, path string) error { + rq.SetBasicAuth(b.user, b.pw) + return nil } -// User holds the BasicAuth username -func (b *BasicAuth) User() string { - return b.user +// Verify verifies if the authentication +func (b *BasicAuth) Verify(c *http.Client, rs *http.Response, path string) (redo bool, err error) { + if rs.StatusCode == 401 { + err = NewPathError("Authorize", path, rs.StatusCode) + } + return } -// Pass holds the BasicAuth password -func (b *BasicAuth) Pass() string { - return b.pw +// Close cleans up all resources +func (b *BasicAuth) Close() error { + return nil } -// Authorize the current request -func (b *BasicAuth) Authorize(req *http.Request, method string, path string) { - a := b.user + ":" + b.pw - auth := "Basic " + base64.StdEncoding.EncodeToString([]byte(a)) - req.Header.Set("Authorization", auth) +// Clone creates a Copy of itself +func (b *BasicAuth) Clone() Authenticator { + // no copy due to read only access + return b +} + +// String toString +func (b *BasicAuth) String() string { + return fmt.Sprintf("BasicAuth login: %s", b.user) } diff --git a/vendor/github.com/studio-b12/gowebdav/client.go b/vendor/github.com/studio-b12/gowebdav/client.go index 7ea68d2447c..4a4bb1b6ca7 100644 --- a/vendor/github.com/studio-b12/gowebdav/client.go +++ b/vendor/github.com/studio-b12/gowebdav/client.go @@ -9,58 +9,43 @@ import ( "net/url" "os" pathpkg "path" + "strconv" "strings" - "sync" "time" ) +const XInhibitRedirect = "X-Gowebdav-Inhibit-Redirect" + +var defaultProps = []string{"displayname", "resourcetype", "getcontentlength", "getcontenttype", "getetag", "getlastmodified"} + // Client defines our structure type Client struct { root string headers http.Header interceptor func(method string, rq *http.Request) c *http.Client - - authMutex sync.Mutex - auth Authenticator -} - -// Authenticator stub -type Authenticator interface { - Type() string - User() string - Pass() string - Authorize(*http.Request, string, string) -} - -// NoAuth structure holds our credentials -type NoAuth struct { - user string - pw string -} - -// Type identifies the authenticator -func (n *NoAuth) Type() string { - return "NoAuth" -} - -// User returns the current user -func (n *NoAuth) User() string { - return n.user -} - -// Pass returns the current password -func (n *NoAuth) Pass() string { - return n.pw -} - -// Authorize the current request -func (n *NoAuth) Authorize(req *http.Request, method string, path string) { + auth Authorizer } // NewClient creates a new instance of client func NewClient(uri, user, pw string) *Client { - return &Client{FixSlash(uri), make(http.Header), nil, &http.Client{}, sync.Mutex{}, &NoAuth{user, pw}} + return NewAuthClient(uri, NewAutoAuth(user, pw)) +} + +// NewAuthClient creates a new client instance with a custom Authorizer +func NewAuthClient(uri string, auth Authorizer) *Client { + c := &http.Client{ + CheckRedirect: func(rq *http.Request, via []*http.Request) error { + if len(via) >= 10 { + return ErrTooManyRedirects + } + if via[0].Header.Get(XInhibitRedirect) != "" { + return http.ErrUseLastResponse + } + return nil + }, + } + return &Client{root: FixSlash(uri), headers: make(http.Header), interceptor: nil, c: c, auth: auth} } // SetHeader lets us set arbitrary headers for a given client @@ -83,6 +68,11 @@ func (c *Client) SetTransport(transport http.RoundTripper) { c.c.Transport = transport } +// SetJar exposes the ability to set a cookie jar to the client. +func (c *Client) SetJar(jar http.CookieJar) { + c.c.Jar = jar +} + // Connect connects to our dav server func (c *Client) Connect() error { rs, err := c.options("/") @@ -96,29 +86,86 @@ func (c *Client) Connect() error { } if rs.StatusCode != 200 { - return newPathError("Connect", c.root, rs.StatusCode) + return NewPathError("Connect", c.root, rs.StatusCode) } return nil } -type props struct { - Status string `xml:"DAV: status"` - Name string `xml:"DAV: prop>displayname,omitempty"` - Type xml.Name `xml:"DAV: prop>resourcetype>collection,omitempty"` - Size string `xml:"DAV: prop>getcontentlength,omitempty"` - ContentType string `xml:"DAV: prop>getcontenttype,omitempty"` - ETag string `xml:"DAV: prop>getetag,omitempty"` - Modified string `xml:"DAV: prop>getlastmodified,omitempty"` +type Props struct { + m map[xml.Name]interface{} +} + +func (c *Props) GetString(key xml.Name) string { + return fmt.Sprintf("%v", c.m[key]) +} + +func (c *Props) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { + c.m = make(map[xml.Name]interface{}) + + type flatProp struct { + XMLName xml.Name + Content string `xml:",innerxml"` + } + type flatProps struct { + Props []flatProp `xml:",any"` + } + parsedProps := flatProps{} + if err := d.DecodeElement(&parsedProps, &start); err != nil { + return err + } + for _, v := range parsedProps.Props { + c.m[v.XMLName] = v.Content + } + return nil +} + +type propstat struct { + Status string `xml:"DAV: status"` + + Props Props `xml:"DAV: prop"` +} + +func (p *propstat) Type() string { + if p.Props.GetString(xml.Name{Space: "DAV:", Local: "resourcetype"}) == "" { + return "file" + } + return "collection" +} + +func (p *propstat) Name() string { + return p.Props.GetString(xml.Name{Space: "DAV:", Local: "displayname"}) +} + +func (p *propstat) ContentType() string { + return p.Props.GetString(xml.Name{Space: "DAV:", Local: "getcontenttype"}) +} + +func (p *propstat) Size() int64 { + if n, e := strconv.ParseInt(p.Props.GetString(xml.Name{Space: "DAV:", Local: "getcontentlength"}), 10, 64); e == nil { + return n + } + return 0 +} + +func (p *propstat) ETag() string { + return p.Props.GetString(xml.Name{Space: "DAV:", Local: "getetag"}) +} + +func (p *propstat) Modified() time.Time { + if t, e := time.Parse(time.RFC1123, p.Props.GetString(xml.Name{Space: "DAV:", Local: "getlastmodified"})); e == nil { + return t + } + return time.Unix(0, 0) } type response struct { - Href string `xml:"DAV: href"` - Props []props `xml:"DAV: propstat"` + Href string `xml:"DAV: href"` + Propstats []propstat `xml:"DAV: propstat"` } -func getProps(r *response, status string) *props { - for _, prop := range r.Props { +func getPropstat(r *response, status string) *propstat { + for _, prop := range r.Propstats { if strings.Contains(prop.Status, status) { return &prop } @@ -128,7 +175,16 @@ func getProps(r *response, status string) *props { // ReadDir reads the contents of a remote directory func (c *Client) ReadDir(path string) ([]os.FileInfo, error) { - path = FixSlashes(path) + return c.ReadDirWithProps(path, defaultProps) +} + +// ReadDirWithProps reads the contents of the directory at the given path, along with the specified properties. +func (c *Client) ReadDirWithProps(path string, props []string) ([]os.FileInfo, error) { + propfindprops := "" + if len(props) > 0 { + propfindprops = `` + } + files := make([]os.FileInfo, 0) skipSelf := true parse := func(resp interface{}) error { @@ -136,113 +192,76 @@ func (c *Client) ReadDir(path string) ([]os.FileInfo, error) { if skipSelf { skipSelf = false - if p := getProps(r, "200"); p != nil && p.Type.Local == "collection" { - r.Props = nil + if p := getPropstat(r, "200"); p != nil && p.Type() == "collection" { + r.Propstats = nil return nil } - return newPathError("ReadDir", path, 405) + return NewPathError("ReadDir", path, 405) } - if p := getProps(r, "200"); p != nil { - f := new(File) + if p := getPropstat(r, "200"); p != nil { + var name string if ps, err := url.PathUnescape(r.Href); err == nil { - f.name = pathpkg.Base(ps) - } else { - f.name = p.Name - } - f.path = path + f.name - f.modified = parseModified(&p.Modified) - f.etag = p.ETag - f.contentType = p.ContentType - - if p.Type.Local == "collection" { - f.path += "/" - f.size = 0 - f.isdir = true + name = pathpkg.Base(ps) } else { - f.size = parseInt64(&p.Size) - f.isdir = false + name = p.Name() } - - files = append(files, *f) + files = append(files, *newFile(path, name, p)) } - r.Props = nil + r.Propstats = nil return nil } err := c.propfind(path, false, - ` - - - - - - - - - `, + ``+propfindprops+``, &response{}, parse) if err != nil { if _, ok := err.(*os.PathError); !ok { - err = newPathErrorErr("ReadDir", path, err) + err = NewPathErrorErr("ReadDir", path, err) } } return files, err } -// Stat returns the file stats for a specified path +// Stat returns the file stats for a specified path with the default properties func (c *Client) Stat(path string) (os.FileInfo, error) { + return c.StatWithProps(path, defaultProps) +} + +// StatWithProps returns the FileInfo for the specified path along with the specified properties. +func (c *Client) StatWithProps(path string, props []string) (os.FileInfo, error) { var f *File parse := func(resp interface{}) error { r := resp.(*response) - if p := getProps(r, "200"); p != nil && f == nil { - f = new(File) - f.name = p.Name - f.path = path - f.etag = p.ETag - f.contentType = p.ContentType - - if p.Type.Local == "collection" { - if !strings.HasSuffix(f.path, "/") { - f.path += "/" - } - f.size = 0 - f.modified = time.Unix(0, 0) - f.isdir = true - } else { - f.size = parseInt64(&p.Size) - f.modified = parseModified(&p.Modified) - f.isdir = false - } + if p := getPropstat(r, "200"); p != nil && f == nil { + f = newFile(".", path, p) } - r.Props = nil + r.Propstats = nil return nil } + propXML := "" + for _, prop := range props { + propXML += "" + } + propXML += "" + err := c.propfind(path, true, - ` - - - - - - - - - `, + propXML, &response{}, parse) if err != nil { if _, ok := err.(*os.PathError); !ok { - err = newPathErrorErr("ReadDir", path, err) + return nil, NewPathErrorErr("ReadDir", path, err) } + return nil, err } - return f, err + return *f, err } // Remove removes a remote file @@ -254,7 +273,7 @@ func (c *Client) Remove(path string) error { func (c *Client) RemoveAll(path string) error { rs, err := c.req("DELETE", path, nil, nil) if err != nil { - return newPathError("Remove", path, 400) + return NewPathError("Remove", path, 400) } err = rs.Body.Close() if err != nil { @@ -265,7 +284,7 @@ func (c *Client) RemoveAll(path string) error { return nil } - return newPathError("Remove", path, rs.StatusCode) + return NewPathError("Remove", path, rs.StatusCode) } // Mkdir makes a directory @@ -279,7 +298,7 @@ func (c *Client) Mkdir(path string, _ os.FileMode) (err error) { return nil } - return newPathError("Mkdir", path, status) + return NewPathError("Mkdir", path, status) } // MkdirAll like mkdir -p, but for webdav @@ -305,13 +324,13 @@ func (c *Client) MkdirAll(path string, _ os.FileMode) (err error) { return } if status != 201 { - return newPathError("MkdirAll", sub, status) + return NewPathError("MkdirAll", sub, status) } } return nil } - return newPathError("MkdirAll", path, status) + return NewPathError("MkdirAll", path, status) } // Rename moves a file from A to B @@ -346,7 +365,7 @@ func (c *Client) Read(path string) ([]byte, error) { func (c *Client) ReadStream(path string) (io.ReadCloser, error) { rs, err := c.req("GET", path, nil, nil) if err != nil { - return nil, newPathErrorErr("ReadStream", path, err) + return nil, NewPathErrorErr("ReadStream", path, err) } if rs.StatusCode == 200 { @@ -354,7 +373,7 @@ func (c *Client) ReadStream(path string) (io.ReadCloser, error) { } rs.Body.Close() - return nil, newPathError("ReadStream", path, rs.StatusCode) + return nil, NewPathError("ReadStream", path, rs.StatusCode) } // ReadStreamRange reads the stream representing a subset of bytes for a given path, @@ -374,7 +393,7 @@ func (c *Client) ReadStreamRange(path string, offset, length int64) (io.ReadClos } }) if err != nil { - return nil, newPathErrorErr("ReadStreamRange", path, err) + return nil, NewPathErrorErr("ReadStreamRange", path, err) } if rs.StatusCode == http.StatusPartialContent { @@ -387,15 +406,15 @@ func (c *Client) ReadStreamRange(path string, offset, length int64) (io.ReadClos if rs.StatusCode == 200 { // discard first 'offset' bytes. if _, err := io.Copy(io.Discard, io.LimitReader(rs.Body, offset)); err != nil { - return nil, newPathErrorErr("ReadStreamRange", path, err) + return nil, NewPathErrorErr("ReadStreamRange", path, err) } // return a io.ReadCloser that is limited to `length` bytes. - return &limitedReadCloser{rs.Body, int(length)}, nil + return &limitedReadCloser{rc: rs.Body, remaining: int(length)}, nil } rs.Body.Close() - return nil, newPathError("ReadStream", path, rs.StatusCode) + return nil, NewPathError("ReadStream", path, rs.StatusCode) } // Write writes data to a given path @@ -425,7 +444,7 @@ func (c *Client) Write(path string, data []byte, _ os.FileMode) (err error) { } } - return newPathError("Write", path, s) + return NewPathError("Write", path, s) } // WriteStream writes a stream @@ -446,6 +465,6 @@ func (c *Client) WriteStream(path string, stream io.Reader, _ os.FileMode) (err return nil default: - return newPathError("WriteStream", path, s) + return NewPathError("WriteStream", path, s) } } diff --git a/vendor/github.com/studio-b12/gowebdav/digestAuth.go b/vendor/github.com/studio-b12/gowebdav/digestAuth.go index 4a5eb62f2fb..1bcc1de3d07 100644 --- a/vendor/github.com/studio-b12/gowebdav/digestAuth.go +++ b/vendor/github.com/studio-b12/gowebdav/digestAuth.go @@ -17,28 +17,46 @@ type DigestAuth struct { digestParts map[string]string } -// Type identifies the DigestAuthenticator -func (d *DigestAuth) Type() string { - return "DigestAuth" -} - -// User holds the DigestAuth username -func (d *DigestAuth) User() string { - return d.user -} - -// Pass holds the DigestAuth password -func (d *DigestAuth) Pass() string { - return d.pw +// NewDigestAuth creates a new instance of our Digest Authenticator +func NewDigestAuth(login, secret string, rs *http.Response) (Authenticator, error) { + return &DigestAuth{user: login, pw: secret, digestParts: digestParts(rs)}, nil } // Authorize the current request -func (d *DigestAuth) Authorize(req *http.Request, method string, path string) { +func (d *DigestAuth) Authorize(c *http.Client, rq *http.Request, path string) error { d.digestParts["uri"] = path - d.digestParts["method"] = method + d.digestParts["method"] = rq.Method d.digestParts["username"] = d.user d.digestParts["password"] = d.pw - req.Header.Set("Authorization", getDigestAuthorization(d.digestParts)) + rq.Header.Set("Authorization", getDigestAuthorization(d.digestParts)) + return nil +} + +// Verify checks for authentication issues and may trigger a re-authentication +func (d *DigestAuth) Verify(c *http.Client, rs *http.Response, path string) (redo bool, err error) { + if rs.StatusCode == 401 { + err = NewPathError("Authorize", path, rs.StatusCode) + } + return +} + +// Close cleans up all resources +func (d *DigestAuth) Close() error { + return nil +} + +// Clone creates a copy of itself +func (d *DigestAuth) Clone() Authenticator { + parts := make(map[string]string, len(d.digestParts)) + for k, v := range d.digestParts { + parts[k] = v + } + return &DigestAuth{user: d.user, pw: d.pw, digestParts: parts} +} + +// String toString +func (d *DigestAuth) String() string { + return fmt.Sprintf("DigestAuth login: %s", d.user) } func digestParts(resp *http.Response) map[string]string { diff --git a/vendor/github.com/studio-b12/gowebdav/errors.go b/vendor/github.com/studio-b12/gowebdav/errors.go index bbf1e929eb8..afc1af6f81f 100644 --- a/vendor/github.com/studio-b12/gowebdav/errors.go +++ b/vendor/github.com/studio-b12/gowebdav/errors.go @@ -1,10 +1,18 @@ package gowebdav import ( + "errors" "fmt" "os" ) +// ErrAuthChanged must be returned from the Verify method as an error +// to trigger a re-authentication / negotiation with a new authenticator. +var ErrAuthChanged = errors.New("authentication failed, change algorithm") + +// ErrTooManyRedirects will be used as return error if a request exceeds 10 redirects. +var ErrTooManyRedirects = errors.New("stopped after 10 redirects") + // StatusError implements error and wraps // an erroneous status code. type StatusError struct { @@ -32,7 +40,7 @@ func IsErrNotFound(err error) bool { return IsErrCode(err, 404) } -func newPathError(op string, path string, statusCode int) error { +func NewPathError(op string, path string, statusCode int) error { return &os.PathError{ Op: op, Path: path, @@ -40,7 +48,7 @@ func newPathError(op string, path string, statusCode int) error { } } -func newPathErrorErr(op string, path string, err error) error { +func NewPathErrorErr(op string, path string, err error) error { return &os.PathError{ Op: op, Path: path, diff --git a/vendor/github.com/studio-b12/gowebdav/file.go b/vendor/github.com/studio-b12/gowebdav/file.go index ae2303fc8e0..8c5a0918926 100644 --- a/vendor/github.com/studio-b12/gowebdav/file.go +++ b/vendor/github.com/studio-b12/gowebdav/file.go @@ -3,6 +3,7 @@ package gowebdav import ( "fmt" "os" + "path/filepath" "time" ) @@ -15,6 +16,31 @@ type File struct { modified time.Time etag string isdir bool + props Props +} + +func newFile(path, name string, p *propstat) *File { + f := &File{ + props: p.Props, + } + path = FixSlashes(path) + + f.name = name + f.path = filepath.Clean(filepath.Join(path, f.name)) + f.modified = p.Modified() + f.etag = p.ETag() + f.contentType = p.ContentType() + f.props = p.Props + + if p.Type() == "collection" { + f.path = filepath.Clean(f.path + "/") + f.size = 0 + f.isdir = true + } else { + f.size = p.Size() + f.isdir = false + } + return f } // Path returns the full path of a file @@ -64,7 +90,7 @@ func (f File) IsDir() bool { // Sys ???? func (f File) Sys() interface{} { - return nil + return f.props } // String lets us see file information diff --git a/vendor/github.com/studio-b12/gowebdav/passportAuth.go b/vendor/github.com/studio-b12/gowebdav/passportAuth.go new file mode 100644 index 00000000000..5e55c536ab2 --- /dev/null +++ b/vendor/github.com/studio-b12/gowebdav/passportAuth.go @@ -0,0 +1,181 @@ +package gowebdav + +import ( + "fmt" + "io" + "net/http" + "net/url" + "strings" +) + +// PassportAuth structure holds our credentials +type PassportAuth struct { + user string + pw string + cookies []http.Cookie + inhibitRedirect bool +} + +// constructor for PassportAuth creates a new PassportAuth object and +// automatically authenticates against the given partnerURL +func NewPassportAuth(c *http.Client, user, pw, partnerURL string, header *http.Header) (Authenticator, error) { + p := &PassportAuth{ + user: user, + pw: pw, + inhibitRedirect: true, + } + err := p.genCookies(c, partnerURL, header) + return p, err +} + +// Authorize the current request +func (p *PassportAuth) Authorize(c *http.Client, rq *http.Request, path string) error { + // prevent redirects to detect subsequent authentication requests + if p.inhibitRedirect { + rq.Header.Set(XInhibitRedirect, "1") + } else { + p.inhibitRedirect = true + } + for _, cookie := range p.cookies { + rq.AddCookie(&cookie) + } + return nil +} + +// Verify verifies if the authentication is good +func (p *PassportAuth) Verify(c *http.Client, rs *http.Response, path string) (redo bool, err error) { + switch rs.StatusCode { + case 301, 302, 307, 308: + redo = true + if rs.Header.Get("Www-Authenticate") != "" { + // re-authentication required as we are redirected to the login page + err = p.genCookies(c, rs.Request.URL.String(), &rs.Header) + } else { + // just a redirect, follow it + p.inhibitRedirect = false + } + case 401: + err = NewPathError("Authorize", path, rs.StatusCode) + } + return +} + +// Close cleans up all resources +func (p *PassportAuth) Close() error { + return nil +} + +// Clone creates a Copy of itself +func (p *PassportAuth) Clone() Authenticator { + // create a copy to allow independent cookie updates + clonedCookies := make([]http.Cookie, len(p.cookies)) + copy(clonedCookies, p.cookies) + + return &PassportAuth{ + user: p.user, + pw: p.pw, + cookies: clonedCookies, + inhibitRedirect: true, + } +} + +// String toString +func (p *PassportAuth) String() string { + return fmt.Sprintf("PassportAuth login: %s", p.user) +} + +func (p *PassportAuth) genCookies(c *http.Client, partnerUrl string, header *http.Header) error { + // For more details refer to: + // https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-pass/2c80637d-438c-4d4b-adc5-903170a779f3 + // Skipping step 1 and 2 as we already have the partner server challenge + + baseAuthenticationServer := header.Get("Location") + baseAuthenticationServerURL, err := url.Parse(baseAuthenticationServer) + if err != nil { + return err + } + + // Skipping step 3 and 4 as we already know that we need and have the user's credentials + // Step 5 (Sign-in request) + authenticationServerUrl := url.URL{ + Scheme: baseAuthenticationServerURL.Scheme, + Host: baseAuthenticationServerURL.Host, + Path: "/login2.srf", + } + + partnerServerChallenge := strings.Split(header.Get("Www-Authenticate"), " ")[1] + + req := http.Request{ + Method: "GET", + URL: &authenticationServerUrl, + Header: http.Header{ + "Authorization": []string{"Passport1.4 sign-in=" + url.QueryEscape(p.user) + ",pwd=" + url.QueryEscape(p.pw) + ",OrgVerb=GET,OrgUrl=" + partnerUrl + "," + partnerServerChallenge}, + }, + } + + rs, err := c.Do(&req) + if err != nil { + return err + } + io.Copy(io.Discard, rs.Body) + rs.Body.Close() + if rs.StatusCode != 200 { + return NewPathError("Authorize", "/", rs.StatusCode) + } + + // Step 6 (Token Response from Authentication Server) + tokenResponseHeader := rs.Header.Get("Authentication-Info") + if tokenResponseHeader == "" { + return NewPathError("Authorize", "/", 401) + } + tokenResponseHeaderList := strings.Split(tokenResponseHeader, ",") + token := "" + for _, tokenResponseHeader := range tokenResponseHeaderList { + if strings.HasPrefix(tokenResponseHeader, "from-PP='") { + token = tokenResponseHeader + break + } + } + if token == "" { + return NewPathError("Authorize", "/", 401) + } + + // Step 7 (First Authentication Request to Partner Server) + origUrl, err := url.Parse(partnerUrl) + if err != nil { + return err + } + req = http.Request{ + Method: "GET", + URL: origUrl, + Header: http.Header{ + "Authorization": []string{"Passport1.4 " + token}, + }, + } + + rs, err = c.Do(&req) + if err != nil { + return err + } + io.Copy(io.Discard, rs.Body) + rs.Body.Close() + if rs.StatusCode != 200 && rs.StatusCode != 302 { + return NewPathError("Authorize", "/", rs.StatusCode) + } + + // Step 8 (Set Token Message from Partner Server) + cookies := rs.Header.Values("Set-Cookie") + p.cookies = make([]http.Cookie, len(cookies)) + for i, cookie := range cookies { + cookieParts := strings.Split(cookie, ";") + cookieName := strings.Split(cookieParts[0], "=")[0] + cookieValue := strings.Split(cookieParts[0], "=")[1] + + p.cookies[i] = http.Cookie{ + Name: cookieName, + Value: cookieValue, + } + } + + return nil +} diff --git a/vendor/github.com/studio-b12/gowebdav/requests.go b/vendor/github.com/studio-b12/gowebdav/requests.go index 0966d1ab0b8..8e362e86648 100644 --- a/vendor/github.com/studio-b12/gowebdav/requests.go +++ b/vendor/github.com/studio-b12/gowebdav/requests.go @@ -1,7 +1,6 @@ package gowebdav import ( - "bytes" "io" "log" "net/http" @@ -9,83 +8,52 @@ import ( "strings" ) -func (c *Client) req(method, path string, body io.Reader, intercept func(*http.Request)) (req *http.Response, err error) { +func (c *Client) req(method, path string, body io.Reader, intercept func(*http.Request)) (rs *http.Response, err error) { + var redo bool var r *http.Request - var retryBuf io.Reader - - if body != nil { - // If the authorization fails, we will need to restart reading - // from the passed body stream. - // When body is seekable, use seek to reset the streams - // cursor to the start. - // Otherwise, copy the stream into a buffer while uploading - // and use the buffers content on retry. - if sk, ok := body.(io.Seeker); ok { - if _, err = sk.Seek(0, io.SeekStart); err != nil { - return - } - retryBuf = body - } else { - buff := &bytes.Buffer{} - retryBuf = buff - body = io.TeeReader(body, buff) - } - r, err = http.NewRequest(method, PathEscape(Join(c.root, path)), body) - } else { - r, err = http.NewRequest(method, PathEscape(Join(c.root, path)), nil) - } + var uri = PathEscape(Join(c.root, path)) + auth, body := c.auth.NewAuthenticator(body) + defer auth.Close() - if err != nil { - return nil, err - } - - for k, vals := range c.headers { - for _, v := range vals { - r.Header.Add(k, v) + for { // TODO auth.continue() strategy(true|n times|until)? + if r, err = http.NewRequest(method, uri, body); err != nil { + return } - } - - // make sure we read 'c.auth' only once since it will be substituted below - // and that is unsafe to do when multiple goroutines are running at the same time. - c.authMutex.Lock() - auth := c.auth - c.authMutex.Unlock() - auth.Authorize(r, method, path) + for k, vals := range c.headers { + for _, v := range vals { + r.Header.Add(k, v) + } + } - if intercept != nil { - intercept(r) - } + if err = auth.Authorize(c.c, r, path); err != nil { + return + } - if c.interceptor != nil { - c.interceptor(method, r) - } + if intercept != nil { + intercept(r) + } - rs, err := c.c.Do(r) - if err != nil { - return nil, err - } + if c.interceptor != nil { + c.interceptor(method, r) + } - if rs.StatusCode == 401 && auth.Type() == "NoAuth" { - wwwAuthenticateHeader := strings.ToLower(rs.Header.Get("Www-Authenticate")) - - if strings.Index(wwwAuthenticateHeader, "digest") > -1 { - c.authMutex.Lock() - c.auth = &DigestAuth{auth.User(), auth.Pass(), digestParts(rs)} - c.authMutex.Unlock() - } else if strings.Index(wwwAuthenticateHeader, "basic") > -1 { - c.authMutex.Lock() - c.auth = &BasicAuth{auth.User(), auth.Pass()} - c.authMutex.Unlock() - } else { - return rs, newPathError("Authorize", c.root, rs.StatusCode) + if rs, err = c.c.Do(r); err != nil { + return } - // retryBuf will be nil if body was nil initially so no check - // for body == nil is required here. - return c.req(method, path, retryBuf, intercept) - } else if rs.StatusCode == 401 { - return rs, newPathError("Authorize", c.root, rs.StatusCode) + if redo, err = auth.Verify(c.c, rs, path); err != nil { + rs.Body.Close() + return nil, err + } + if redo { + rs.Body.Close() + if body, err = r.GetBody(); err != nil { + return nil, err + } + continue + } + break } return rs, err @@ -131,7 +99,7 @@ func (c *Client) propfind(path string, self bool, body string, resp interface{}, defer rs.Body.Close() if rs.StatusCode != 207 { - return newPathError("PROPFIND", path, rs.StatusCode) + return NewPathError("PROPFIND", path, rs.StatusCode) } return parseXML(rs.Body, resp, parse) @@ -189,7 +157,7 @@ func (c *Client) copymove(method string, oldpath string, newpath string, overwri return c.copymove(method, oldpath, newpath, overwrite) } - return newPathError(method, oldpath, s) + return NewPathError(method, oldpath, s) } func (c *Client) put(path string, stream io.Reader) (status int, err error) { diff --git a/vendor/github.com/studio-b12/gowebdav/utils.go b/vendor/github.com/studio-b12/gowebdav/utils.go index d466160e866..4fa64b142b9 100644 --- a/vendor/github.com/studio-b12/gowebdav/utils.go +++ b/vendor/github.com/studio-b12/gowebdav/utils.go @@ -5,9 +5,7 @@ import ( "encoding/xml" "io" "net/url" - "strconv" "strings" - "time" ) // PathEscape escapes all segments of a given path @@ -49,27 +47,6 @@ func String(r io.Reader) string { return buf.String() } -func parseUint(s *string) uint { - if n, e := strconv.ParseUint(*s, 10, 32); e == nil { - return uint(n) - } - return 0 -} - -func parseInt64(s *string) int64 { - if n, e := strconv.ParseInt(*s, 10, 64); e == nil { - return n - } - return 0 -} - -func parseModified(s *string) time.Time { - if t, e := time.Parse(time.RFC1123, *s); e == nil { - return t - } - return time.Unix(0, 0) -} - func parseXML(data io.Reader, resp interface{}, parse func(resp interface{}) error) error { decoder := xml.NewDecoder(data) for t, _ := decoder.Token(); t != nil; t, _ = decoder.Token() { diff --git a/vendor/modules.txt b/vendor/modules.txt index 0910d0f218c..84d58d0619e 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1721,7 +1721,7 @@ github.com/stretchr/objx github.com/stretchr/testify/assert github.com/stretchr/testify/mock github.com/stretchr/testify/require -# github.com/studio-b12/gowebdav v0.0.0-20221015232716-17255f2e7423 +# github.com/studio-b12/gowebdav v0.0.0-20221015232716-17255f2e7423 => github.com/aduffeck/gowebdav v0.0.0-20231123085457-ff658b6ea159 ## explicit; go 1.17 github.com/studio-b12/gowebdav # github.com/tchap/go-patricia/v2 v2.3.1 @@ -2299,3 +2299,4 @@ stash.kopano.io/kgol/oidc-go stash.kopano.io/kgol/rndm # github.com/go-micro/plugins/v4/store/nats-js => github.com/kobergj/plugins/v4/store/nats-js v1.2.1-0.20231020092801-9463c820c19a # github.com/go-micro/plugins/v4/store/nats-js-kv => github.com/kobergj/plugins/v4/store/nats-js-kv v0.0.0-20231207143248-4d424e3ae348 +# github.com/studio-b12/gowebdav => github.com/aduffeck/gowebdav v0.0.0-20231123085457-ff658b6ea159 From a530192dd4b449132edb296a71e51c7870a93e73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Duffeck?= Date: Thu, 14 Dec 2023 10:00:20 +0100 Subject: [PATCH 09/11] Bump reva --- go.mod | 2 +- go.sum | 4 +- .../internal/http/services/ocmd/protocols.go | 7 ++ .../owncloud/ocdav/propfind/propfind.go | 2 +- .../services/owncloud/ocs/config/config.go | 1 + .../handlers/apps/sharing/sharees/sharees.go | 53 ++++++++-- .../handlers/apps/sharing/shares/pending.go | 83 +++++++++++++++ .../handlers/apps/sharing/shares/remote.go | 2 +- .../handlers/apps/sharing/shares/shares.go | 65 ++++++++++++ .../ocs/handlers/apps/sharing/shares/user.go | 100 ++++++++++++++++++ .../http/services/sciencemesh/sciencemesh.go | 2 +- .../http/services/sciencemesh/token.go | 8 +- .../cs3org/reva/v2/pkg/conversions/main.go | 24 ++++- .../v2/pkg/ocm/invite/repository/json/json.go | 36 ++++++- .../reva/v2/pkg/ocm/storage/received/ocm.go | 86 ++++++++++++--- .../cs3org/reva/v2/pkg/utils/utils.go | 5 + vendor/modules.txt | 2 +- 17 files changed, 439 insertions(+), 43 deletions(-) diff --git a/go.mod b/go.mod index 44f05c8c891..064ef8ecfc4 100644 --- a/go.mod +++ b/go.mod @@ -13,7 +13,7 @@ require ( github.com/coreos/go-oidc v2.2.1+incompatible github.com/coreos/go-oidc/v3 v3.9.0 github.com/cs3org/go-cs3apis v0.0.0-20231023073225-7748710e0781 - github.com/cs3org/reva/v2 v2.17.0 + github.com/cs3org/reva/v2 v2.17.1-0.20231214082636-a8467337208a github.com/dhowden/tag v0.0.0-20230630033851-978a0926ee25 github.com/disintegration/imaging v1.6.2 github.com/dutchcoders/go-clamd v0.0.0-20170520113014-b970184f4d9e diff --git a/go.sum b/go.sum index 0d2d4b43d35..80f4a4d8e38 100644 --- a/go.sum +++ b/go.sum @@ -1021,8 +1021,8 @@ github.com/crewjam/saml v0.4.14 h1:g9FBNx62osKusnFzs3QTN5L9CVA/Egfgm+stJShzw/c= github.com/crewjam/saml v0.4.14/go.mod h1:UVSZCf18jJkk6GpWNVqcyQJMD5HsRugBPf4I1nl2mME= github.com/cs3org/go-cs3apis v0.0.0-20231023073225-7748710e0781 h1:BUdwkIlf8IS2FasrrPg8gGPHQPOrQ18MS1Oew2tmGtY= github.com/cs3org/go-cs3apis v0.0.0-20231023073225-7748710e0781/go.mod h1:UXha4TguuB52H14EMoSsCqDj7k8a/t7g4gVP+bgY5LY= -github.com/cs3org/reva/v2 v2.17.0 h1:cp7WXY+mZGLie4CKvIe3K+D/wG3sKVYrZJfs9Qnzioo= -github.com/cs3org/reva/v2 v2.17.0/go.mod h1:9hmBNVK+RSMSupWci9MQLmmj1NsJ8Bv49tqKbxMdxJY= +github.com/cs3org/reva/v2 v2.17.1-0.20231214082636-a8467337208a h1:ri98OWOuAY5tF3jpeKSItna3O4I0SedxzVgPQJPZXoM= +github.com/cs3org/reva/v2 v2.17.1-0.20231214082636-a8467337208a/go.mod h1:oX1YtLKGr7jatGk0CpPM4GKbSEIdHhmsQuSAYElnN1U= github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= diff --git a/vendor/github.com/cs3org/reva/v2/internal/http/services/ocmd/protocols.go b/vendor/github.com/cs3org/reva/v2/internal/http/services/ocmd/protocols.go index 0c773583876..882f42b4a9f 100644 --- a/vendor/github.com/cs3org/reva/v2/internal/http/services/ocmd/protocols.go +++ b/vendor/github.com/cs3org/reva/v2/internal/http/services/ocmd/protocols.go @@ -59,11 +59,18 @@ func (w *WebDAV) ToOCMProtocol() *ocm.Protocol { switch p { case "read": perms.Permissions.GetPath = true + perms.Permissions.GetQuota = true perms.Permissions.InitiateFileDownload = true perms.Permissions.ListContainer = true + perms.Permissions.ListRecycle = true perms.Permissions.Stat = true case "write": perms.Permissions.InitiateFileUpload = true + perms.Permissions.RestoreRecycleItem = true + perms.Permissions.CreateContainer = true + perms.Permissions.Delete = true + perms.Permissions.Move = true + perms.Permissions.ListGrants = true case "share": perms.Reshare = true } diff --git a/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/propfind/propfind.go b/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/propfind/propfind.go index d410bfcba78..b2d098fffcf 100644 --- a/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/propfind/propfind.go +++ b/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/propfind/propfind.go @@ -1078,7 +1078,6 @@ func mdToPropResponse(ctx context.Context, pf *XML, md *provider.ResourceInfo, p } shareTypes = utils.ReadPlainFromOpaque(md.Opaque, "share-types") } - role := conversions.RoleFromResourcePermissions(md.PermissionSet, ls != nil) if md.Space != nil && md.Space.SpaceType != "grant" && utils.ResourceIDEqual(md.Space.Root, id) { @@ -1174,6 +1173,7 @@ func mdToPropResponse(ctx context.Context, pf *XML, md *provider.ResourceInfo, p if md.Name != "" { appendToOK(prop.Escaped("oc:name", md.Name)) + appendToOK(prop.Escaped("d:displayname", md.Name)) } if md.Etag != "" { diff --git a/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocs/config/config.go b/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocs/config/config.go index 01a815d80ad..09c5baf43d2 100644 --- a/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocs/config/config.go +++ b/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocs/config/config.go @@ -50,6 +50,7 @@ type Config struct { OCMMountPoint string `mapstructure:"ocm_mount_point"` ListOCMShares bool `mapstructure:"list_ocm_shares"` Notifications map[string]interface{} `mapstructure:"notifications"` + IncludeOCMSharees bool `mapstructure:"include_ocm_sharees"` } // Init sets sane defaults diff --git a/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocs/handlers/apps/sharing/sharees/sharees.go b/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocs/handlers/apps/sharing/sharees/sharees.go index 3feaf9096c8..e3e3bbbeca6 100644 --- a/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocs/handlers/apps/sharing/sharees/sharees.go +++ b/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocs/handlers/apps/sharing/sharees/sharees.go @@ -24,6 +24,8 @@ import ( grouppb "github.com/cs3org/go-cs3apis/cs3/identity/group/v1beta1" userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" + invitepb "github.com/cs3org/go-cs3apis/cs3/ocm/invite/v1beta1" + rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1" "github.com/cs3org/reva/v2/pkg/conversions" "github.com/cs3org/reva/v2/internal/http/services/owncloud/ocs/config" @@ -37,12 +39,14 @@ import ( type Handler struct { gatewayAddr string additionalInfoAttribute string + includeOCMSharees bool } // Init initializes this and any contained handlers func (h *Handler) Init(c *config.Config) { h.gatewayAddr = c.GatewaySvc h.additionalInfoAttribute = c.AdditionalInfoAttribute + h.includeOCMSharees = c.IncludeOCMSharees } // FindSharees implements the /apps/files_sharing/api/v1/sharees endpoint @@ -79,6 +83,27 @@ func (h *Handler) FindSharees(w http.ResponseWriter, r *http.Request) { } } + if h.includeOCMSharees { + remoteUsersRes, err := gwc.FindAcceptedUsers(r.Context(), &invitepb.FindAcceptedUsersRequest{Filter: term}) + if err != nil { + response.WriteOCSError(w, r, response.MetaServerError.StatusCode, "error searching remote users", err) + return + } + if remoteUsersRes.Status.Code != rpc.Code_CODE_OK { + response.WriteOCSError(w, r, response.MetaServerError.StatusCode, "error searching remote users", nil) + return + } + for _, user := range remoteUsersRes.GetAcceptedUsers() { + match := h.userAsMatch(user) + log.Debug().Interface("user", user).Interface("match", match).Msg("mapped") + if h.isExactMatch(match, term) { + exactUserMatches = append(exactUserMatches, match) + } else { + userMatches = append(userMatches, match) + } + } + } + groupsRes, err := gwc.FindGroups(r.Context(), &grouppb.FindGroupsRequest{Filter: term, SkipFetchingMembers: true}) if err != nil { response.WriteOCSError(w, r, response.MetaServerError.StatusCode, "error searching groups", err) @@ -111,21 +136,27 @@ func (h *Handler) FindSharees(w http.ResponseWriter, r *http.Request) { } func (h *Handler) userAsMatch(u *userpb.User) *conversions.MatchData { - var ocsUserType int - if u.Id.Type == userpb.UserType_USER_TYPE_GUEST || u.Id.Type == userpb.UserType_USER_TYPE_LIGHTWEIGHT { - ocsUserType = 1 + data := &conversions.MatchValueData{ + ShareType: int(conversions.ShareTypeUser), + // api compatibility with oc10: mark guest users in share invite dialogue + UserType: 0, + // api compatibility with oc10: always use the username + ShareWith: u.Username, + ShareWithAdditionalInfo: h.getAdditionalInfoAttribute(u), + } + + switch u.Id.Type { + case userpb.UserType_USER_TYPE_GUEST, userpb.UserType_USER_TYPE_LIGHTWEIGHT: + data.UserType = 1 + case userpb.UserType_USER_TYPE_FEDERATED: + data.ShareType = int(conversions.ShareTypeFederatedCloudShare) + data.ShareWith = u.Id.OpaqueId + data.ShareWithProvider = u.Id.Idp } return &conversions.MatchData{ Label: u.DisplayName, - Value: &conversions.MatchValueData{ - ShareType: int(conversions.ShareTypeUser), - // api compatibility with oc10: mark guest users in share invite dialogue - UserType: ocsUserType, - // api compatibility with oc10: always use the username - ShareWith: u.Username, - ShareWithAdditionalInfo: h.getAdditionalInfoAttribute(u), - }, + Value: data, } } diff --git a/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocs/handlers/apps/sharing/shares/pending.go b/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocs/handlers/apps/sharing/shares/pending.go index 8709502c6b7..b53d18021bb 100644 --- a/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocs/handlers/apps/sharing/shares/pending.go +++ b/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocs/handlers/apps/sharing/shares/pending.go @@ -31,6 +31,7 @@ import ( gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1" rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1" collaboration "github.com/cs3org/go-cs3apis/cs3/sharing/collaboration/v1beta1" + ocmv1beta1 "github.com/cs3org/go-cs3apis/cs3/sharing/ocm/v1beta1" provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" "github.com/cs3org/reva/v2/internal/http/services/owncloud/ocs/response" "github.com/cs3org/reva/v2/pkg/appctx" @@ -50,6 +51,12 @@ const ( func (h *Handler) AcceptReceivedShare(w http.ResponseWriter, r *http.Request) { ctx := r.Context() shareID := chi.URLParam(r, shareidkey) + + if h.isFederatedReceivedShare(r, shareID) { + h.updateReceivedFederatedShare(w, r, shareID, false) + return + } + client, err := h.getClient() if err != nil { response.WriteOCSError(w, r, response.MetaServerError.StatusCode, "error getting grpc gateway client", err) @@ -148,6 +155,12 @@ func (h *Handler) AcceptReceivedShare(w http.ResponseWriter, r *http.Request) { // RejectReceivedShare handles DELETE Requests on /apps/files_sharing/api/v1/shares/{shareid} func (h *Handler) RejectReceivedShare(w http.ResponseWriter, r *http.Request) { shareID := chi.URLParam(r, "shareid") + + if h.isFederatedReceivedShare(r, shareID) { + h.updateReceivedFederatedShare(w, r, shareID, true) + return + } + // we need to add a path to the share receivedShare := &collaboration.ReceivedShare{ Share: &collaboration.Share{ @@ -254,6 +267,76 @@ func (h *Handler) updateReceivedShare(w http.ResponseWriter, r *http.Request, re return data } +func (h *Handler) updateReceivedFederatedShare(w http.ResponseWriter, r *http.Request, shareID string, rejectShare bool) { + ctx := r.Context() + + client, err := h.getClient() + if err != nil { + response.WriteOCSError(w, r, response.MetaServerError.StatusCode, "error getting grpc gateway client", err) + return + } + + share, err := client.GetReceivedOCMShare(ctx, &ocmv1beta1.GetReceivedOCMShareRequest{ + Ref: &ocmv1beta1.ShareReference{ + Spec: &ocmv1beta1.ShareReference_Id{ + Id: &ocmv1beta1.ShareId{ + OpaqueId: shareID, + }, + }, + }, + }) + if err != nil { + response.WriteOCSError(w, r, response.MetaServerError.StatusCode, "grpc update received share request failed", err) + return + } + if share.Status.Code != rpc.Code_CODE_OK { + if share.Status.Code == rpc.Code_CODE_NOT_FOUND { + response.WriteOCSError(w, r, response.MetaNotFound.StatusCode, "not found", nil) + return + } + response.WriteOCSError(w, r, response.MetaServerError.StatusCode, "grpc update received share request failed", errors.Errorf("code: %d, message: %s", share.Status.Code, share.Status.Message)) + return + } + + req := &ocmv1beta1.UpdateReceivedOCMShareRequest{ + Share: &ocmv1beta1.ReceivedShare{ + Id: &ocmv1beta1.ShareId{ + OpaqueId: shareID, + }, + }, + UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"state"}}, + } + if rejectShare { + req.Share.State = ocmv1beta1.ShareState_SHARE_STATE_REJECTED + } else { + req.Share.State = ocmv1beta1.ShareState_SHARE_STATE_ACCEPTED + } + + updateRes, err := client.UpdateReceivedOCMShare(ctx, req) + if err != nil { + response.WriteOCSError(w, r, response.MetaServerError.StatusCode, "grpc update received share request failed", err) + return + } + + if updateRes.Status.Code != rpc.Code_CODE_OK { + if updateRes.Status.Code == rpc.Code_CODE_NOT_FOUND { + response.WriteOCSError(w, r, response.MetaNotFound.StatusCode, "not found", nil) + return + } + response.WriteOCSError(w, r, response.MetaServerError.StatusCode, "grpc update received share request failed", errors.Errorf("code: %d, message: %s", updateRes.Status.Code, updateRes.Status.Message)) + return + } + + data, err := conversions.ReceivedOCMShare2ShareData(share.Share, h.ocmLocalMount(share.Share)) + if err != nil { + response.WriteOCSError(w, r, response.MetaServerError.StatusCode, "grpc update received share request failed", err) + return + } + h.mapUserIdsReceivedFederatedShare(ctx, client, data) + data.State = mapOCMState(req.Share.State) + response.WriteOCSSuccess(w, r, []*conversions.ShareData{data}) +} + // getReceivedShareHideFlagFromShareId returns the hide flag of a received share based on its ID. func (h *Handler) getReceivedShareHideFlagFromShareID(ctx context.Context, shareID string) bool { client, err := h.getClient() diff --git a/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocs/handlers/apps/sharing/shares/remote.go b/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocs/handlers/apps/sharing/shares/remote.go index c118f559c86..7cbf4946822 100644 --- a/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocs/handlers/apps/sharing/shares/remote.go +++ b/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocs/handlers/apps/sharing/shares/remote.go @@ -49,7 +49,7 @@ func (h *Handler) createFederatedCloudShare(w http.ResponseWriter, r *http.Reque return } - shareWithUser, shareWithProvider := r.FormValue("shareWithUser"), r.FormValue("shareWithProvider") + shareWithUser, shareWithProvider := r.FormValue("shareWith"), r.FormValue("shareWithProvider") if shareWithUser == "" || shareWithProvider == "" { response.WriteOCSError(w, r, response.MetaBadRequest.StatusCode, "missing shareWith parameters", nil) return diff --git a/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocs/handlers/apps/sharing/shares/shares.go b/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocs/handlers/apps/sharing/shares/shares.go index 2621a331326..a2552b69ea1 100644 --- a/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocs/handlers/apps/sharing/shares/shares.go +++ b/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocs/handlers/apps/sharing/shares/shares.go @@ -48,6 +48,7 @@ import ( "google.golang.org/grpc/metadata" "google.golang.org/protobuf/types/known/fieldmaskpb" + ocm "github.com/cs3org/go-cs3apis/cs3/sharing/ocm/v1beta1" ocmv1beta1 "github.com/cs3org/go-cs3apis/cs3/sharing/ocm/v1beta1" "github.com/cs3org/reva/v2/internal/http/services/owncloud/ocs/config" "github.com/cs3org/reva/v2/internal/http/services/owncloud/ocs/response" @@ -642,6 +643,66 @@ func (h *Handler) GetShare(w http.ResponseWriter, r *http.Request) { } } + if share == nil { + // check if we have a federated share + req := &ocm.GetOCMShareRequest{ + Ref: &ocm.ShareReference{ + Spec: &ocm.ShareReference_Id{ + Id: &ocm.ShareId{ + OpaqueId: shareID, + }, + }, + }, + } + ocmShareResponse, err := client.GetOCMShare(ctx, req) + if err != nil { + response.WriteOCSError(w, r, response.MetaServerError.StatusCode, "error sending a grpc get ocm share request", err) + return + } + + ocmShare := ocmShareResponse.GetShare() + if ocmShare != nil { + resourceID = ocmShare.ResourceId + share, err = conversions.OCMShare2ShareData(ocmShare) + if err != nil { + response.WriteOCSError(w, r, response.MetaServerError.StatusCode, "error mapping share data", err) + return + } + } + } + + if share == nil { + // check if we have an incoming federated share + req := &ocm.GetReceivedOCMShareRequest{ + Ref: &ocm.ShareReference{ + Spec: &ocm.ShareReference_Id{ + Id: &ocm.ShareId{ + OpaqueId: shareID, + }, + }, + }, + } + ocmShareResponse, err := client.GetReceivedOCMShare(ctx, req) + if err != nil { + response.WriteOCSError(w, r, response.MetaServerError.StatusCode, "error sending a grpc get ocm share request", err) + return + } + + ocmShare := ocmShareResponse.GetShare() + if ocmShare != nil { + resourceID = &provider.ResourceId{ + StorageId: utils.OCMStorageProviderID, + SpaceId: ocmShare.Id.OpaqueId, + OpaqueId: ocmShare.Id.OpaqueId, + } + share, err = conversions.ReceivedOCMShare2ShareData(ocmShare, h.ocmLocalMount(ocmShare)) + if err != nil { + response.WriteOCSError(w, r, response.MetaServerError.StatusCode, "error mapping share data", err) + return + } + } + } + if share == nil { sublog.Debug().Msg("no share found with this id") response.WriteOCSError(w, r, response.MetaNotFound.StatusCode, "share not found", nil) @@ -857,6 +918,10 @@ func (h *Handler) RemoveShare(w http.ResponseWriter, r *http.Request) { h.removeUserShare(w, r, share) return } + if h.isFederatedShare(r, shareID) { + h.removeFederatedShare(w, r, shareID) + return + } if prov, ok := h.isSpaceShare(r, shareID); ok { // The request is a remove space member request. diff --git a/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocs/handlers/apps/sharing/shares/user.go b/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocs/handlers/apps/sharing/shares/user.go index d67d0f2d2e7..04498b22057 100644 --- a/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocs/handlers/apps/sharing/shares/user.go +++ b/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocs/handlers/apps/sharing/shares/user.go @@ -155,6 +155,106 @@ func (h *Handler) isUserShare(r *http.Request, oid string) (*collaboration.Share return getShareRes.GetShare(), getShareRes.GetShare() != nil } +func (h *Handler) isFederatedShare(r *http.Request, shareID string) bool { + log := appctx.GetLogger(r.Context()) + client, err := pool.GetGatewayServiceClient(h.gatewayAddr) + if err != nil { + log.Err(err).Send() + return false + } + + getShareRes, err := client.GetOCMShare(r.Context(), &ocmpb.GetOCMShareRequest{ + Ref: &ocmpb.ShareReference{ + Spec: &ocmpb.ShareReference_Id{ + Id: &ocmpb.ShareId{ + OpaqueId: shareID, + }, + }, + }, + }) + if err != nil { + log.Err(err).Send() + return false + } + + return getShareRes.GetShare() != nil +} + +func (h *Handler) removeFederatedShare(w http.ResponseWriter, r *http.Request, shareID string) { + ctx := r.Context() + + client, err := pool.GetGatewayServiceClient(h.gatewayAddr) + if err != nil { + response.WriteOCSError(w, r, response.MetaServerError.StatusCode, "error getting grpc gateway client", err) + return + } + + shareRef := &ocmpb.ShareReference_Id{Id: &ocmpb.ShareId{OpaqueId: shareID}} + // Get the share, so that we can include it in the response. + getShareResp, err := client.GetOCMShare(ctx, &ocmpb.GetOCMShareRequest{Ref: &ocmpb.ShareReference{Spec: shareRef}}) + if err != nil { + response.WriteOCSError(w, r, response.MetaServerError.StatusCode, "error sending a grpc delete share request", err) + return + } + if getShareResp.Status.Code != rpc.Code_CODE_OK { + if getShareResp.Status.Code == rpc.Code_CODE_NOT_FOUND { + response.WriteOCSError(w, r, response.MetaNotFound.StatusCode, "not found", nil) + return + } + response.WriteOCSError(w, r, response.MetaServerError.StatusCode, "deleting share failed", err) + return + } + + data, err := conversions.OCMShare2ShareData(getShareResp.Share) + if err != nil { + response.WriteOCSError(w, r, response.MetaServerError.StatusCode, "deleting share failed", err) + return + } + // A deleted share should not have an ID. + data.ID = "" + + uRes, err := client.RemoveOCMShare(ctx, &ocmpb.RemoveOCMShareRequest{Ref: &ocmpb.ShareReference{Spec: shareRef}}) + if err != nil { + response.WriteOCSError(w, r, response.MetaServerError.StatusCode, "error sending a grpc delete share request", err) + return + } + + if uRes.Status.Code != rpc.Code_CODE_OK { + if uRes.Status.Code == rpc.Code_CODE_NOT_FOUND { + response.WriteOCSError(w, r, response.MetaNotFound.StatusCode, "not found", nil) + return + } + response.WriteOCSError(w, r, response.MetaServerError.StatusCode, "grpc delete share request failed", err) + return + } + response.WriteOCSSuccess(w, r, data) +} + +func (h *Handler) isFederatedReceivedShare(r *http.Request, shareID string) bool { + log := appctx.GetLogger(r.Context()) + client, err := pool.GetGatewayServiceClient(h.gatewayAddr) + if err != nil { + log.Err(err).Send() + return false + } + + getShareRes, err := client.GetReceivedOCMShare(r.Context(), &ocmpb.GetReceivedOCMShareRequest{ + Ref: &ocmpb.ShareReference{ + Spec: &ocmpb.ShareReference_Id{ + Id: &ocmpb.ShareId{ + OpaqueId: shareID, + }, + }, + }, + }) + if err != nil { + log.Err(err).Send() + return false + } + + return getShareRes.GetShare() != nil +} + func (h *Handler) removeUserShare(w http.ResponseWriter, r *http.Request, share *collaboration.Share) { ctx := r.Context() diff --git a/vendor/github.com/cs3org/reva/v2/internal/http/services/sciencemesh/sciencemesh.go b/vendor/github.com/cs3org/reva/v2/internal/http/services/sciencemesh/sciencemesh.go index e98d49d9fe2..49dbde86b51 100644 --- a/vendor/github.com/cs3org/reva/v2/internal/http/services/sciencemesh/sciencemesh.go +++ b/vendor/github.com/cs3org/reva/v2/internal/http/services/sciencemesh/sciencemesh.go @@ -63,8 +63,8 @@ type config struct { Prefix string `mapstructure:"prefix"` SMTPCredentials *smtpclient.SMTPCredentials `mapstructure:"smtp_credentials" validate:"required"` GatewaySvc string `mapstructure:"gatewaysvc" validate:"required"` - MeshDirectoryURL string `mapstructure:"mesh_directory_url" validate:"required"` ProviderDomain string `mapstructure:"provider_domain" validate:"required"` + MeshDirectoryURL string `mapstructure:"mesh_directory_url"` SubjectTemplate string `mapstructure:"subject_template"` BodyTemplatePath string `mapstructure:"body_template_path"` OCMMountPoint string `mapstructure:"ocm_mount_point"` diff --git a/vendor/github.com/cs3org/reva/v2/internal/http/services/sciencemesh/token.go b/vendor/github.com/cs3org/reva/v2/internal/http/services/sciencemesh/token.go index 946aa974301..4879a9400f7 100644 --- a/vendor/github.com/cs3org/reva/v2/internal/http/services/sciencemesh/token.go +++ b/vendor/github.com/cs3org/reva/v2/internal/http/services/sciencemesh/token.go @@ -76,7 +76,7 @@ type token struct { Token string `json:"token"` Description string `json:"description,omitempty"` Expiration uint64 `json:"expiration,omitempty"` - InviteLink string `json:"invite_link"` + InviteLink string `json:"invite_link,omitempty"` } // Generate generates an invitation token and if a recipient is specified, @@ -122,7 +122,9 @@ func (h *tokenHandler) prepareGenerateTokenResponse(tkn *invitepb.InviteToken) * res := &token{ Token: tkn.Token, Description: tkn.Description, - InviteLink: h.meshDirectoryURL + "?token=" + tkn.Token + "&providerDomain=" + h.providerDomain, + } + if h.meshDirectoryURL != "" { + res.InviteLink = h.meshDirectoryURL + "?token=" + tkn.Token + "&providerDomain=" + h.providerDomain } if tkn.Expiration != nil { res.Expiration = tkn.Expiration.Seconds @@ -187,7 +189,7 @@ func (h *tokenHandler) AcceptInvite(w http.ResponseWriter, r *http.Request) { reqres.WriteError(w, r, reqres.APIErrorAlreadyExist, "user already known", nil) return case rpc.Code_CODE_PERMISSION_DENIED: - reqres.WriteError(w, r, reqres.APIErrorUnauthenticated, "remove service not trusted", nil) + reqres.WriteError(w, r, reqres.APIErrorUnauthenticated, "remote service not trusted", nil) return default: reqres.WriteError(w, r, reqres.APIErrorServerError, "unexpected error: "+forwardInviteResponse.Status.Message, errors.New(forwardInviteResponse.Status.Message)) diff --git a/vendor/github.com/cs3org/reva/v2/pkg/conversions/main.go b/vendor/github.com/cs3org/reva/v2/pkg/conversions/main.go index 755faa64ad3..8ef5dc395ab 100644 --- a/vendor/github.com/cs3org/reva/v2/pkg/conversions/main.go +++ b/vendor/github.com/cs3org/reva/v2/pkg/conversions/main.go @@ -21,15 +21,19 @@ package conversions import ( "context" + "encoding/base64" "fmt" "net/http" "path" + "path/filepath" "time" "github.com/cs3org/reva/v2/pkg/errtypes" "github.com/cs3org/reva/v2/pkg/mime" "github.com/cs3org/reva/v2/pkg/publicshare" + "github.com/cs3org/reva/v2/pkg/storagespace" "github.com/cs3org/reva/v2/pkg/user" + "github.com/cs3org/reva/v2/pkg/utils" grouppb "github.com/cs3org/go-cs3apis/cs3/identity/group/v1beta1" userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" @@ -222,6 +226,7 @@ type MatchData struct { type MatchValueData struct { ShareType int `json:"shareType" xml:"shareType"` ShareWith string `json:"shareWith" xml:"shareWith"` + ShareWithProvider string `json:"shareWithProvider" xml:"shareWithProvider"` ShareWithAdditionalInfo string `json:"shareWithAdditionalInfo" xml:"shareWithAdditionalInfo,omitempty"` UserType int `json:"userType" xml:"userType"` } @@ -325,6 +330,10 @@ func ReceivedOCMShare2ShareData(share *ocm.ReceivedShare, path string) (*ShareDa return nil, errtypes.InternalError("webdav endpoint not in share") } + opaqueid := base64.StdEncoding.EncodeToString([]byte("/")) + + shareTarget := filepath.Join("/Shares", share.Name) + s := &ShareData{ ID: share.Id.OpaqueId, UIDOwner: formatRemoteUser(share.Creator), @@ -332,13 +341,18 @@ func ReceivedOCMShare2ShareData(share *ocm.ReceivedShare, path string) (*ShareDa ShareWith: share.Grantee.GetUserId().OpaqueId, Permissions: RoleFromResourcePermissions(webdav.GetPermissions().GetPermissions(), false).OCSPermissions(), ShareType: ShareTypeFederatedCloudShare, - Path: path, - FileTarget: path, + Path: shareTarget, + FileTarget: shareTarget, MimeType: mime.Detect(share.ResourceType == provider.ResourceType_RESOURCE_TYPE_CONTAINER, share.Name), ItemType: ResourceType(share.ResourceType).String(), - ItemSource: path, - STime: share.Ctime.Seconds, - Name: share.Name, + ItemSource: storagespace.FormatResourceID(provider.ResourceId{ + StorageId: utils.OCMStorageProviderID, + SpaceId: share.Id.OpaqueId, + OpaqueId: opaqueid, + }), + STime: share.Ctime.Seconds, + Name: share.Name, + SpaceID: storagespace.FormatStorageID(utils.OCMStorageProviderID, share.Id.OpaqueId), } if share.Expiration != nil { diff --git a/vendor/github.com/cs3org/reva/v2/pkg/ocm/invite/repository/json/json.go b/vendor/github.com/cs3org/reva/v2/pkg/ocm/invite/repository/json/json.go index b5a64ef10d3..9fd92b0efba 100644 --- a/vendor/github.com/cs3org/reva/v2/pkg/ocm/invite/repository/json/json.go +++ b/vendor/github.com/cs3org/reva/v2/pkg/ocm/invite/repository/json/json.go @@ -22,7 +22,9 @@ import ( "context" "encoding/json" "io" + "net/url" "os" + "path/filepath" "strings" "sync" "time" @@ -89,6 +91,9 @@ func New(m map[string]interface{}) (invite.Repository, error) { func loadOrCreate(file string) (*inviteModel, error) { _, err := os.Stat(file) if os.IsNotExist(err) { + if err := os.MkdirAll(filepath.Dir(file), 0700); err != nil { + return nil, errors.Wrap(err, "error creating the ocm storage dir: "+filepath.Dir(file)) + } if err := os.WriteFile(file, []byte("{}"), 0700); err != nil { return nil, errors.Wrap(err, "error creating the invite storage file: "+file) } @@ -169,7 +174,7 @@ func (m *manager) ListTokens(ctx context.Context, initiator *userpb.UserId) ([]* } func tokenIsExpired(token *invitepb.InviteToken) bool { - return token.Expiration != nil && token.Expiration.Seconds > uint64(time.Now().Unix()) + return token.Expiration != nil && token.Expiration.Seconds < uint64(time.Now().Unix()) } func (m *manager) AddRemoteUser(ctx context.Context, initiator *userpb.UserId, remoteUser *userpb.User) error { @@ -177,7 +182,7 @@ func (m *manager) AddRemoteUser(ctx context.Context, initiator *userpb.UserId, r defer m.Unlock() for _, acceptedUser := range m.model.AcceptedUsers[initiator.GetOpaqueId()] { - if acceptedUser.Id.GetOpaqueId() == remoteUser.Id.OpaqueId && acceptedUser.Id.GetIdp() == remoteUser.Id.Idp { + if acceptedUser.Id.GetOpaqueId() == remoteUser.Id.OpaqueId && idpsEqual(acceptedUser.Id.GetIdp(), remoteUser.Id.Idp) { return invite.ErrUserAlreadyAccepted } } @@ -189,6 +194,31 @@ func (m *manager) AddRemoteUser(ctx context.Context, initiator *userpb.UserId, r return nil } +func idpsEqual(idp1, idp2 string) bool { + normalizeIDP := func(s string) (string, error) { + u, err := url.Parse(s) + if err != nil { + return "", errors.New("could not parse url") + } + + if u.Scheme == "" { + return strings.ToLower(u.Path), nil // the string is just a hostname + } + return strings.ToLower(u.Hostname()), nil + } + + domain1, err := normalizeIDP(idp1) + if err != nil { + return false + } + domain2, err := normalizeIDP(idp2) + if err != nil { + return false + } + + return domain1 == domain2 +} + func (m *manager) GetRemoteUser(ctx context.Context, initiator *userpb.UserId, remoteUserID *userpb.UserId) (*userpb.User, error) { m.RLock() defer m.RUnlock() @@ -201,7 +231,7 @@ func (m *manager) GetRemoteUser(ctx context.Context, initiator *userpb.UserId, r acceptedUser.Id.GetOpaqueId(), acceptedUser.Id.GetIdp(), ) - if (acceptedUser.Id.GetOpaqueId() == remoteUserID.OpaqueId) && (remoteUserID.Idp == "" || acceptedUser.Id.GetIdp() == remoteUserID.Idp) { + if (acceptedUser.Id.GetOpaqueId() == remoteUserID.OpaqueId) && (remoteUserID.Idp == "" || idpsEqual(acceptedUser.Id.GetIdp(), remoteUserID.Idp)) { return acceptedUser, nil } } diff --git a/vendor/github.com/cs3org/reva/v2/pkg/ocm/storage/received/ocm.go b/vendor/github.com/cs3org/reva/v2/pkg/ocm/storage/received/ocm.go index 7c74a59c1a2..95ab95492ea 100644 --- a/vendor/github.com/cs3org/reva/v2/pkg/ocm/storage/received/ocm.go +++ b/vendor/github.com/cs3org/reva/v2/pkg/ocm/storage/received/ocm.go @@ -20,6 +20,8 @@ package ocm import ( "context" + "crypto/tls" + "encoding/base64" "io" "io/fs" "net/http" @@ -58,12 +60,39 @@ type driver struct { type config struct { GatewaySVC string `mapstructure:"gatewaysvc"` + Insecure bool `mapstructure:"insecure"` } func (c *config) ApplyDefaults() { c.GatewaySVC = sharedconf.GetGatewaySVC(c.GatewaySVC) } +// BearerAuthenticator represents an authenticator that adds a Bearer token to the Authorization header of HTTP requests. +type BearerAuthenticator struct { + token string +} + +// Authorize adds the Bearer token to the Authorization header of the provided HTTP request. +func (b BearerAuthenticator) Authorize(_ *http.Client, r *http.Request, _ string) error { + r.Header.Add("Authorization", "Bearer "+b.token) + return nil +} + +// Verify is not implemented for the BearerAuthenticator. It always returns false and nil error. +func (BearerAuthenticator) Verify(*http.Client, *http.Response, string) (bool, error) { + return false, nil +} + +// Clone creates a new instance of the BearerAuthenticator. +func (BearerAuthenticator) Clone() gowebdav.Authenticator { + return BearerAuthenticator{} +} + +// Close is not implemented for the BearerAuthenticator. It always returns nil. +func (BearerAuthenticator) Close() error { + return nil +} + // New creates an OCM storage driver. func New(m map[string]interface{}, _ events.Stream) (storage.FS, error) { var c config @@ -95,7 +124,16 @@ func shareInfoFromReference(ref *provider.Reference) (*ocmpb.ShareId, string) { return shareInfoFromPath(ref.Path) } - return &ocmpb.ShareId{OpaqueId: ref.ResourceId.OpaqueId}, ref.Path + if ref.ResourceId.SpaceId == ref.ResourceId.OpaqueId { + return &ocmpb.ShareId{OpaqueId: ref.ResourceId.SpaceId}, ref.Path + } + decodedBytes, err := base64.StdEncoding.DecodeString(ref.ResourceId.OpaqueId) + if err != nil { + // this should never happen + return &ocmpb.ShareId{OpaqueId: ref.ResourceId.SpaceId}, ref.Path + } + return &ocmpb.ShareId{OpaqueId: ref.ResourceId.SpaceId}, filepath.Join(string(decodedBytes), ref.Path) + } func (d *driver) getWebDAVFromShare(ctx context.Context, shareID *ocmpb.ShareId) (*ocmpb.ReceivedShare, string, string, error) { @@ -150,8 +188,12 @@ func (d *driver) webdavClient(ctx context.Context, ref *provider.Reference) (*go // FIXME: it's still not clear from the OCM APIs how to use the shared secret // will use as a token in the bearer authentication as this is the reva implementation - c := gowebdav.NewClient(endpoint, "", "") - c.SetHeader("Authorization", "Bearer "+secret) + c := gowebdav.NewAuthClient(endpoint, gowebdav.NewPreemptiveAuth(BearerAuthenticator{token: secret})) + if d.c.Insecure { + c.SetTransport(&http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + }) + } return c, share, rel, nil } @@ -194,7 +236,7 @@ func getPathFromShareIDAndRelPath(shareID *ocmpb.ShareId, relPath string) string return filepath.Join("/", shareID.OpaqueId, relPath) } -func convertStatToResourceInfo(ref *provider.Reference, f fs.FileInfo, share *ocmpb.ReceivedShare, relPath string) *provider.ResourceInfo { +func convertStatToResourceInfo(ref *provider.Reference, f fs.FileInfo, share *ocmpb.ReceivedShare) (*provider.ResourceInfo, error) { t := provider.ResourceType_RESOURCE_TYPE_FILE if f.IsDir() { t = provider.ResourceType_RESOURCE_TYPE_CONTAINER @@ -207,24 +249,36 @@ func convertStatToResourceInfo(ref *provider.Reference, f fs.FileInfo, share *oc name = f.Name() } - webdav, _ := getWebDAVProtocol(share.Protocols) + webdavFile, ok := f.(gowebdav.File) + if !ok { + return nil, errtypes.InternalError("could not get webdav props") + } + opaqueid := base64.StdEncoding.EncodeToString([]byte(webdavFile.Path())) + + // ids are of the format $! + id := &provider.ResourceId{ + StorageId: utils.OCMStorageProviderID, + SpaceId: share.Id.OpaqueId, + OpaqueId: opaqueid, + } + webdavProtocol, _ := getWebDAVProtocol(share.Protocols) return &provider.ResourceInfo{ Type: t, - Id: ref.ResourceId, + Id: id, MimeType: mime.Detect(f.IsDir(), f.Name()), - Path: relPath, + Path: name, Name: name, Size: uint64(f.Size()), Mtime: &typepb.Timestamp{ Seconds: uint64(f.ModTime().Unix()), }, Owner: share.Creator, - PermissionSet: webdav.Permissions.Permissions, + PermissionSet: webdavProtocol.Permissions.Permissions, Checksum: &provider.ResourceChecksum{ Type: provider.ResourceChecksumType_RESOURCE_CHECKSUM_TYPE_INVALID, }, - } + }, nil } func (d *driver) GetMD(ctx context.Context, ref *provider.Reference, _ []string, _ []string) (*provider.ResourceInfo, error) { @@ -233,7 +287,7 @@ func (d *driver) GetMD(ctx context.Context, ref *provider.Reference, _ []string, return nil, err } - info, err := client.Stat(rel) + info, err := client.StatWithProps(rel, []string{}) // request all properties by giving an empty list if err != nil { if gowebdav.IsErrNotFound(err) { return nil, errtypes.NotFound(ref.GetPath()) @@ -241,7 +295,7 @@ func (d *driver) GetMD(ctx context.Context, ref *provider.Reference, _ []string, return nil, err } - return convertStatToResourceInfo(ref, info, share, rel), nil + return convertStatToResourceInfo(ref, info, share) } func (d *driver) ListFolder(ctx context.Context, ref *provider.Reference, _ []string, _ []string) ([]*provider.ResourceInfo, error) { @@ -250,14 +304,18 @@ func (d *driver) ListFolder(ctx context.Context, ref *provider.Reference, _ []st return nil, err } - list, err := client.ReadDir(rel) + list, err := client.ReadDirWithProps(rel, []string{}) // request all properties by giving an empty list if err != nil { return nil, err } res := make([]*provider.ResourceInfo, 0, len(list)) for _, r := range list { - res = append(res, convertStatToResourceInfo(ref, r, share, utils.MakeRelativePath(filepath.Join(rel, r.Name())))) + info, err := convertStatToResourceInfo(ref, r, share) + if err != nil { + return nil, err + } + res = append(res, info) } return res, nil } @@ -429,7 +487,7 @@ func (d *driver) ListStorageSpaces(ctx context.Context, filters []*provider.List if k == "mountpoint" { for _, share := range lrsRes.Shares { root := &provider.ResourceId{ - StorageId: utils.PublicStorageProviderID, + StorageId: utils.OCMStorageProviderID, SpaceId: share.Id.OpaqueId, OpaqueId: share.Id.OpaqueId, } diff --git a/vendor/github.com/cs3org/reva/v2/pkg/utils/utils.go b/vendor/github.com/cs3org/reva/v2/pkg/utils/utils.go index fe4dc130d83..8f97f7be819 100644 --- a/vendor/github.com/cs3org/reva/v2/pkg/utils/utils.go +++ b/vendor/github.com/cs3org/reva/v2/pkg/utils/utils.go @@ -59,6 +59,11 @@ var ( // PublicStorageSpaceID is the space id used by the sharestorageprovider PublicStorageSpaceID = "7993447f-687f-490d-875c-ac95e89a62a4" + // OCMStorageProviderID is the storage id used by the ocmreceived storageprovider + OCMStorageProviderID = "89f37a33-858b-45fa-8890-a1f2b27d90e1" + // OCMStorageSpaceID is the space id used by the ocmreceived storageprovider + OCMStorageSpaceID = "89f37a33-858b-45fa-8890-a1f2b27d90e1" + // SpaceGrant is used to signal the storageprovider that the grant is on a space SpaceGrant struct{} ) diff --git a/vendor/modules.txt b/vendor/modules.txt index 84d58d0619e..fe15ed96b05 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -362,7 +362,7 @@ github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1 github.com/cs3org/go-cs3apis/cs3/storage/registry/v1beta1 github.com/cs3org/go-cs3apis/cs3/tx/v1beta1 github.com/cs3org/go-cs3apis/cs3/types/v1beta1 -# github.com/cs3org/reva/v2 v2.17.0 +# github.com/cs3org/reva/v2 v2.17.1-0.20231214082636-a8467337208a ## explicit; go 1.21 github.com/cs3org/reva/v2/cmd/revad/internal/grace github.com/cs3org/reva/v2/cmd/revad/runtime From ca20077786eff5026fa0cae2304181af93383ff6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Duffeck?= Date: Thu, 14 Dec 2023 10:24:41 +0100 Subject: [PATCH 10/11] Add changelog --- changelog/unreleased/improve-ocm-support.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 changelog/unreleased/improve-ocm-support.md diff --git a/changelog/unreleased/improve-ocm-support.md b/changelog/unreleased/improve-ocm-support.md new file mode 100644 index 00000000000..7bd20368a11 --- /dev/null +++ b/changelog/unreleased/improve-ocm-support.md @@ -0,0 +1,5 @@ +Bugfix: Improve OCM support + +We improved functionality of the OCM support. + +https://github.com/owncloud/ocis/pull/7973 From 5cb059db46567c8ecf0a4d64e3d1afa58c9b37a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Duffeck?= Date: Thu, 14 Dec 2023 11:20:57 +0100 Subject: [PATCH 11/11] Fix yaml tag --- services/ocm/pkg/config/config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/ocm/pkg/config/config.go b/services/ocm/pkg/config/config.go index 94faef5e146..07c2ae7a321 100644 --- a/services/ocm/pkg/config/config.go +++ b/services/ocm/pkg/config/config.go @@ -111,7 +111,7 @@ type OCMCore struct { } type OCMStorageProvider struct { Insecure bool `yaml:"insecure" env:"OCM_OCM_STORAGE_PROVIDER_INSECURE" desc:"Disable TLS certificate validation for the OCM connections. Do not set this in production environments."` - StorageRoot string `yaml:"insecure" env:"OCM_OCM_STORAGE_PROVIDER_STORAGE_ROOT" desc:"Directory where the ocm storage provider persists its data like tus upload info files."` + StorageRoot string `yaml:"storage_root" env:"OCM_OCM_STORAGE_PROVIDER_STORAGE_ROOT" desc:"Directory where the ocm storage provider persists its data like tus upload info files."` } type OCMCoreDrivers struct {