mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-05-18 13:35:37 -04:00
Merge branch 'master' into backmerge-master
This commit is contained in:
11
.drone.star
11
.drone.star
@@ -16,7 +16,7 @@ OC_CI_GOLANG = "owncloudci/golang:1.21"
|
||||
OC_CI_NODEJS = "owncloudci/nodejs:%s"
|
||||
OC_CI_PHP = "owncloudci/php:%s"
|
||||
OC_CI_WAIT_FOR = "owncloudci/wait-for:latest"
|
||||
OC_CS3_API_VALIDATOR = "owncloud/cs3api-validator:0.2.0"
|
||||
OC_CS3_API_VALIDATOR = "owncloud/cs3api-validator:0.2.1"
|
||||
OC_LITMUS = "owncloudci/litmus:latest"
|
||||
OC_OC_TEST_MIDDLEWARE = "owncloud/owncloud-test-middleware:1.8.8"
|
||||
OC_UBUNTU = "owncloud/ubuntu:20.04"
|
||||
@@ -941,7 +941,7 @@ def wopiValidatorTests(ctx, storage, accounts_hash_difficulty = 4):
|
||||
[
|
||||
{
|
||||
"name": "wopiserver",
|
||||
"image": "cs3org/wopiserver:v10.2.2",
|
||||
"image": "cs3org/wopiserver:v10.3.0",
|
||||
"detach": True,
|
||||
"commands": [
|
||||
"cp %s/tests/config/drone/wopiserver.conf /etc/wopi/wopiserver.conf" % (dirs["base"]),
|
||||
@@ -2876,6 +2876,13 @@ def k6LoadTests(ctx):
|
||||
"sh %s/run_k6_tests.sh --ocis-log" % (dirs["base"]),
|
||||
],
|
||||
},
|
||||
{
|
||||
"name": "open-grafana-dashboard",
|
||||
"image": OC_CI_ALPINE,
|
||||
"commands": [
|
||||
"echo 'Grafana Dashboard: https://grafana.k6.infra.owncloud.works/d/P4D1D31A5B69203FF'",
|
||||
],
|
||||
},
|
||||
],
|
||||
"depends_on": [],
|
||||
"trigger": {
|
||||
|
||||
@@ -2,4 +2,6 @@ Enhancement: Update reva to latest edge version
|
||||
|
||||
We update reva to the latest edge version to get the latest fixes and features.
|
||||
|
||||
https://github.com/owncloud/ocis/pull/8278
|
||||
https://github.com/owncloud/ocis/pull/8264
|
||||
https://github.com/owncloud/ocis/pull/8100
|
||||
|
||||
5
changelog/unreleased/cleanup-graph-driveitemsgo.md
Normal file
5
changelog/unreleased/cleanup-graph-driveitemsgo.md
Normal file
@@ -0,0 +1,5 @@
|
||||
Bugfix: Cleanup graph/pkg/service/v0/driveitems.go
|
||||
|
||||
Main fix is using proto getters to avoid panics. But some other code improvements were also done
|
||||
|
||||
https://github.com/owncloud/ocis/pull/8228
|
||||
5
changelog/unreleased/cleanup-search-searchgo.md
Normal file
5
changelog/unreleased/cleanup-search-searchgo.md
Normal file
@@ -0,0 +1,5 @@
|
||||
Bugfix: Cleanup `search/pkg/search/search.go`
|
||||
|
||||
Now uses proto getters to avoid panics.
|
||||
|
||||
https://github.com/owncloud/ocis/pull/8230
|
||||
7
changelog/unreleased/disabled-password-policy-rework.md
Normal file
7
changelog/unreleased/disabled-password-policy-rework.md
Normal file
@@ -0,0 +1,7 @@
|
||||
Enhancement: Disable the password policy
|
||||
|
||||
We reworked and moved disabling the password policy logic from the reva to the ocis.
|
||||
|
||||
https://github.com/owncloud/ocis/pull/8152
|
||||
https://github.com/cs3org/reva/pull/4453
|
||||
https://github.com/owncloud/ocis/issues/7916
|
||||
7
changelog/unreleased/fix-concurrent-access-to-map.md
Normal file
7
changelog/unreleased/fix-concurrent-access-to-map.md
Normal file
@@ -0,0 +1,7 @@
|
||||
Bugfix: Fix concurrent access to a map
|
||||
|
||||
We fixed the race condition that led to concurrent map access in a publicshare manager.
|
||||
|
||||
https://github.com/owncloud/ocis/pull/8269
|
||||
https://github.com/cs3org/reva/pull/4472
|
||||
https://github.com/owncloud/ocis/issues/8255
|
||||
7
changelog/unreleased/fix-getdrives-response-code.md
Normal file
7
changelog/unreleased/fix-getdrives-response-code.md
Normal file
@@ -0,0 +1,7 @@
|
||||
Bugfix: fix PATCH/DELETE status code for drives that don't support them
|
||||
|
||||
Updating and Deleting the virtual drives for shares is currently not supported. Instead
|
||||
of returning a generic 500 status we return a 405 response now.
|
||||
|
||||
https://github.com/owncloud/ocis/pull/8235
|
||||
https://github.com/owncloud/ocis/issues/7881
|
||||
5
changelog/unreleased/fix-nats-authentication.md
Normal file
5
changelog/unreleased/fix-nats-authentication.md
Normal file
@@ -0,0 +1,5 @@
|
||||
Bugfix: Fix nats authentication
|
||||
|
||||
Fixes nats authentication for registry/events/stores
|
||||
|
||||
https://github.com/owncloud/ocis/pull/8236
|
||||
6
changelog/unreleased/fix-nats-registry.md
Normal file
6
changelog/unreleased/fix-nats-registry.md
Normal file
@@ -0,0 +1,6 @@
|
||||
Bugfix: Fix nats registry
|
||||
|
||||
The nats registry would behave badly when configuring `nats-js-kv` via envvar. Reason is the way go-micro initializes.
|
||||
It took 5 developers to find the issue and the fix so the details cannot be shared here. Just accept that it is working now
|
||||
|
||||
https://github.com/owncloud/ocis/pull/8281
|
||||
5
changelog/unreleased/fix-policies-jwt-config.md
Normal file
5
changelog/unreleased/fix-policies-jwt-config.md
Normal file
@@ -0,0 +1,5 @@
|
||||
Bugfix: Fix jwt config of policies service
|
||||
|
||||
Removes jwt config of policies service
|
||||
|
||||
https://github.com/owncloud/ocis/pull/7893
|
||||
11
changelog/unreleased/sharing-ng-empty-owner.md
Normal file
11
changelog/unreleased/sharing-ng-empty-owner.md
Normal file
@@ -0,0 +1,11 @@
|
||||
Bugfix: graph/sharedWithMe works for shares from project spaces now
|
||||
|
||||
We fixed a bug in the 'graph/v1beta1/me/drive/sharedWithMe' endpoint that
|
||||
caused an error response when the user received shares from project spaces.
|
||||
Additionally the endpoint now behaves more graceful in cases where the
|
||||
displayname of the owner or creator of a share or shared resource couldn't be
|
||||
resolved.
|
||||
|
||||
https://github.com/owncloud/ocis/pull/8233
|
||||
https://github.com/owncloud/ocis/issues/8027
|
||||
https://github.com/owncloud/ocis/issues/8215
|
||||
8
changelog/unreleased/sharing-ng-roleconditions.md
Normal file
8
changelog/unreleased/sharing-ng-roleconditions.md
Normal file
@@ -0,0 +1,8 @@
|
||||
Bugfix: apply role constraints when creating shares via the graph API
|
||||
|
||||
We fixed a bug in the graph API for creating and updating shares so that
|
||||
Spaceroot specific roles like 'Manager' and 'Co-owner' can no longer be
|
||||
assigned for shares on files or directories.
|
||||
|
||||
https://github.com/owncloud/ocis/pull/8247
|
||||
https://github.com/owncloud/ocis/issues/8131
|
||||
@@ -164,7 +164,7 @@ services:
|
||||
restart: always
|
||||
|
||||
wopiserver:
|
||||
image: cs3org/wopiserver:${WOPISERVER_DOCKER_TAG:-v10.2.2}
|
||||
image: cs3org/wopiserver:${WOPISERVER_DOCKER_TAG:-v10.3.0}
|
||||
networks:
|
||||
ocis-net:
|
||||
entrypoint:
|
||||
|
||||
@@ -63,9 +63,10 @@ func generateIntermediateCode(templatePath string, intermediateCodePath string,
|
||||
|
||||
func runIntermediateCode(intermediateCodePath string) {
|
||||
fmt.Println("Running intermediate go code for " + intermediateCodePath)
|
||||
defaultPath := "~/.ocis"
|
||||
os.Setenv("OCIS_BASE_DATA_PATH", defaultPath)
|
||||
os.Setenv("OCIS_CONFIG_DIR", path.Join(defaultPath, "config"))
|
||||
defaultConfigPath := "/etc/ocis"
|
||||
defaultDataPath := "/var/lib/ocis"
|
||||
os.Setenv("OCIS_BASE_DATA_PATH", defaultDataPath)
|
||||
os.Setenv("OCIS_CONFIG_DIR", defaultConfigPath)
|
||||
out, err := exec.Command("go", "run", intermediateCodePath).Output()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
|
||||
30
go.mod
30
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.18.1-0.20240104084554-e85441869c2b
|
||||
github.com/cs3org/reva/v2 v2.18.1-0.20240124094635-6eec406c0be7
|
||||
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
|
||||
@@ -40,7 +40,7 @@ require (
|
||||
github.com/go-micro/plugins/v4/wrapper/monitoring/prometheus v1.2.0
|
||||
github.com/go-micro/plugins/v4/wrapper/trace/opentelemetry v1.2.0
|
||||
github.com/go-ozzo/ozzo-validation/v4 v4.3.0
|
||||
github.com/go-playground/validator/v10 v10.16.0
|
||||
github.com/go-playground/validator/v10 v10.17.0
|
||||
github.com/gofrs/uuid v4.4.0+incompatible
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0
|
||||
github.com/golang/protobuf v1.5.3
|
||||
@@ -65,8 +65,8 @@ require (
|
||||
github.com/oklog/run v1.1.0
|
||||
github.com/olekukonko/tablewriter v0.0.5
|
||||
github.com/onsi/ginkgo v1.16.5
|
||||
github.com/onsi/ginkgo/v2 v2.13.2
|
||||
github.com/onsi/gomega v1.30.0
|
||||
github.com/onsi/ginkgo/v2 v2.15.0
|
||||
github.com/onsi/gomega v1.31.0
|
||||
github.com/open-policy-agent/opa v0.60.0
|
||||
github.com/orcaman/concurrent-map v1.0.0
|
||||
github.com/owncloud/libre-graph-api-go v1.0.5-0.20240115110609-b018a896364e
|
||||
@@ -88,13 +88,13 @@ require (
|
||||
github.com/xhit/go-simple-mail/v2 v2.16.0
|
||||
go-micro.dev/v4 v4.9.0
|
||||
go.etcd.io/bbolt v1.3.8
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1
|
||||
go.opentelemetry.io/contrib/zpages v0.46.1
|
||||
go.opentelemetry.io/otel v1.21.0
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0
|
||||
go.opentelemetry.io/contrib/zpages v0.47.0
|
||||
go.opentelemetry.io/otel v1.22.0
|
||||
go.opentelemetry.io/otel/exporters/jaeger v1.17.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0
|
||||
go.opentelemetry.io/otel/sdk v1.21.0
|
||||
go.opentelemetry.io/otel/trace v1.21.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.22.0
|
||||
go.opentelemetry.io/otel/sdk v1.22.0
|
||||
go.opentelemetry.io/otel/trace v1.22.0
|
||||
golang.org/x/crypto v0.18.0
|
||||
golang.org/x/image v0.15.0
|
||||
golang.org/x/net v0.20.0
|
||||
@@ -193,7 +193,7 @@ require (
|
||||
github.com/go-jose/go-jose/v3 v3.0.1 // indirect
|
||||
github.com/go-kit/log v0.2.1 // indirect
|
||||
github.com/go-logfmt/logfmt v0.5.1 // indirect
|
||||
github.com/go-logr/logr v1.3.0 // indirect
|
||||
github.com/go-logr/logr v1.4.1 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-micro/plugins/v4/events/natsjs v1.2.2-0.20231215124540-f7f8d3274bf9 // indirect
|
||||
github.com/go-micro/plugins/v4/store/nats-js v1.2.1-0.20231129143103-d72facc652f0 // indirect
|
||||
@@ -327,17 +327,17 @@ require (
|
||||
go.opencensus.io v0.24.0 // indirect
|
||||
go.opentelemetry.io/contrib v1.0.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.21.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.22.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.22.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.0.0 // indirect
|
||||
go.uber.org/atomic v1.10.0 // indirect
|
||||
go.uber.org/multierr v1.8.0 // indirect
|
||||
go.uber.org/zap v1.23.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
|
||||
golang.org/x/mod v0.13.0 // indirect
|
||||
golang.org/x/mod v0.14.0 // indirect
|
||||
golang.org/x/sys v0.16.0 // indirect
|
||||
golang.org/x/time v0.5.0 // indirect
|
||||
golang.org/x/tools v0.14.0 // indirect
|
||||
golang.org/x/tools v0.16.1 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
|
||||
google.golang.org/appengine v1.6.8 // indirect
|
||||
google.golang.org/genproto v0.0.0-20231212172506-995d672761c0 // indirect
|
||||
|
||||
60
go.sum
60
go.sum
@@ -1018,8 +1018,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.18.1-0.20240104084554-e85441869c2b h1:Nh0SZn2MyCWC/gmV6le7e9eVzux9WWGPQ/nECgh/gyg=
|
||||
github.com/cs3org/reva/v2 v2.18.1-0.20240104084554-e85441869c2b/go.mod h1:QW31Q1IQ9ZCJMFv3u8/SdHSyLfCcSVNcRbqIJj+Y+7o=
|
||||
github.com/cs3org/reva/v2 v2.18.1-0.20240124094635-6eec406c0be7 h1:g7vQAbo64ziFqqhKcim3JCjDW1zqHy9imAm2HZmmK8w=
|
||||
github.com/cs3org/reva/v2 v2.18.1-0.20240124094635-6eec406c0be7/go.mod h1:GCN3g6uYE0Nvd31dGlhaGGyUviUfbG2NkecPRv5oSc4=
|
||||
github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4=
|
||||
github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg=
|
||||
github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
|
||||
@@ -1186,8 +1186,8 @@ github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KE
|
||||
github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.2.1/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY=
|
||||
github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
|
||||
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.0/go.mod h1:YkVgnZu1ZjjL7xTxrfm/LLZBfkhTqSR1ydtm6jTKKwI=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
@@ -1235,8 +1235,8 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o
|
||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||
github.com/go-playground/validator/v10 v10.16.0 h1:x+plE831WK4vaKHO/jpgUGsvLKIqRRkz6M78GuJAfGE=
|
||||
github.com/go-playground/validator/v10 v10.16.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
|
||||
github.com/go-playground/validator/v10 v10.17.0 h1:SmVVlfAOtlZncTxRuinDPomC2DkXJ4E5T9gDA0AIH74=
|
||||
github.com/go-playground/validator/v10 v10.17.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
|
||||
github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI=
|
||||
github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
|
||||
github.com/go-resty/resty/v2 v2.1.1-0.20191201195748-d7b97669fe48/go.mod h1:dZGr0i9PLlaaTD4H/hoZIDjQ+r6xq8mgbRzHZf7f2J8=
|
||||
@@ -1770,13 +1770,13 @@ github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W
|
||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
|
||||
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
|
||||
github.com/onsi/ginkgo/v2 v2.13.2 h1:Bi2gGVkfn6gQcjNjZJVO8Gf0FHzMPf2phUei9tejVMs=
|
||||
github.com/onsi/ginkgo/v2 v2.13.2/go.mod h1:XStQ8QcGwLyF4HdfcZB8SFOS/MWCgDuXMSBe6zrvLgM=
|
||||
github.com/onsi/ginkgo/v2 v2.15.0 h1:79HwNRBAZHOEwrczrgSOPy+eFTTlIGELKy5as+ClttY=
|
||||
github.com/onsi/ginkgo/v2 v2.15.0/go.mod h1:HlxMHtYF57y6Dpf+mc5529KKmSq9h2FpCF+/ZkwUxKM=
|
||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||
github.com/onsi/gomega v1.30.0 h1:hvMK7xYz4D3HapigLTeGdId/NcfQx1VHMJc60ew99+8=
|
||||
github.com/onsi/gomega v1.30.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ=
|
||||
github.com/onsi/gomega v1.31.0 h1:54UJxxj6cPInHS3a35wm6BK/F9nHYueZ1NVujHDrnXE=
|
||||
github.com/onsi/gomega v1.31.0/go.mod h1:DW9aCi7U6Yi40wNVAvT6kzFnEVEI5n3DloYBiKiT6zk=
|
||||
github.com/open-policy-agent/opa v0.60.0 h1:ZPoPt4yeNs5UXCpd/P/btpSyR8CR0wfhVoh9BOwgJNs=
|
||||
github.com/open-policy-agent/opa v0.60.0/go.mod h1:aD5IK6AiLNYBjNXn7E02++yC8l4Z+bRDvgM6Ss0bBzA=
|
||||
github.com/opencontainers/runtime-spec v1.1.0-rc.1 h1:wHa9jroFfKGQqFHj0I1fMRKLl0pfj+ynAqBxo3v6u9w=
|
||||
@@ -2089,27 +2089,27 @@ go.opentelemetry.io/contrib v1.0.0 h1:khwDCxdSspjOLmFnvMuSHd/5rPzbTx0+l6aURwtQdf
|
||||
go.opentelemetry.io/contrib v1.0.0/go.mod h1:EH4yDYeNoaTqn/8yCWQmfNB78VHfGX2Jt2bvnvzBlGM=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.0 h1:PzIubN4/sjByhDRHLviCjJuweBXWFZWhghjg7cS28+M=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.0/go.mod h1:Ct6zzQEuGK3WpJs2n4dn+wfJYzd/+hNnxMRTWjGn30M=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1 h1:aFJWCqJMNjENlcleuuOkGAPH82y0yULBScfXcIEdS24=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1/go.mod h1:sEGXWArGqc3tVa+ekntsN65DmVbVeW+7lTKTjZF3/Fo=
|
||||
go.opentelemetry.io/contrib/zpages v0.46.1 h1:U8Hh84dc+vJTVgRnL+QKWtWD2iqTSKibrQ85EeQqsNg=
|
||||
go.opentelemetry.io/contrib/zpages v0.46.1/go.mod h1:1Wq9YTzkhr3Jkyi/sVrasFSppVzJQcvFf2Vc2ExZd6c=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0 h1:sv9kVfal0MK0wBMCOGr+HeJm9v803BkJxGrk2au7j08=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0/go.mod h1:SK2UL73Zy1quvRPonmOmRDiWk1KBV3LyIeeIxcEApWw=
|
||||
go.opentelemetry.io/contrib/zpages v0.47.0 h1:ekpdNa2wqOvAfwZIGDIIV02zmR+z08aWPt21KrPJnaU=
|
||||
go.opentelemetry.io/contrib/zpages v0.47.0/go.mod h1:rBeFA/UxnMjRlEGpmClIqzf1mCIKtl7ahjww3wsSdGs=
|
||||
go.opentelemetry.io/otel v1.3.0/go.mod h1:PWIKzi6JCp7sM0k9yZ43VX+T345uNbAkDKwHVjb2PTs=
|
||||
go.opentelemetry.io/otel v1.21.0 h1:hzLeKBZEL7Okw2mGzZ0cc4k/A7Fta0uoPgaJCr8fsFc=
|
||||
go.opentelemetry.io/otel v1.21.0/go.mod h1:QZzNPQPm1zLX4gZK4cMi+71eaorMSGT3A4znnUvNNEo=
|
||||
go.opentelemetry.io/otel v1.22.0 h1:xS7Ku+7yTFvDfDraDIJVpw7XPyuHlB9MCiqqX5mcJ6Y=
|
||||
go.opentelemetry.io/otel v1.22.0/go.mod h1:eoV4iAi3Ea8LkAEI9+GFT44O6T/D0GWAVFyZVCC6pMI=
|
||||
go.opentelemetry.io/otel/exporters/jaeger v1.17.0 h1:D7UpUy2Xc2wsi1Ras6V40q806WM07rqoCWzXu7Sqy+4=
|
||||
go.opentelemetry.io/otel/exporters/jaeger v1.17.0/go.mod h1:nPCqOnEH9rNLKqH/+rrUjiMzHJdV1BlpKcTwRTyKkKI=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0 h1:cl5P5/GIfFh4t6xyruOgJP5QiA1pw4fYYdv6nc6CBWw=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0/go.mod h1:zgBdWWAu7oEEMC06MMKc5NLbA/1YDXV1sMpSqEeLQLg=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0 h1:tIqheXEFWAZ7O8A7m+J0aPTmpJN3YQ7qetUAdkkkKpk=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0/go.mod h1:nUeKExfxAQVbiVFn32YXpXZZHZ61Cc3s3Rn1pDBGAb0=
|
||||
go.opentelemetry.io/otel/metric v1.21.0 h1:tlYWfeo+Bocx5kLEloTjbcDwBuELRrIFxwdQ36PlJu4=
|
||||
go.opentelemetry.io/otel/metric v1.21.0/go.mod h1:o1p3CA8nNHW8j5yuQLdc1eeqEaPfzug24uvsyIEJRWM=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.22.0 h1:9M3+rhx7kZCIQQhQRYaZCdNu1V73tm4TvXs2ntl98C4=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.22.0/go.mod h1:noq80iT8rrHP1SfybmPiRGc9dc5M8RPmGvtwo7Oo7tc=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.22.0 h1:H2JFgRcGiyHg7H7bwcwaQJYrNFqCqrbTQ8K4p1OvDu8=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.22.0/go.mod h1:WfCWp1bGoYK8MeULtI15MmQVczfR+bFkk0DF3h06QmQ=
|
||||
go.opentelemetry.io/otel/metric v1.22.0 h1:lypMQnGyJYeuYPhOM/bgjbFM6WE44W1/T45er4d8Hhg=
|
||||
go.opentelemetry.io/otel/metric v1.22.0/go.mod h1:evJGjVpZv0mQ5QBRJoBF64yMuOf4xCWdXjK8pzFvliY=
|
||||
go.opentelemetry.io/otel/sdk v1.3.0/go.mod h1:rIo4suHNhQwBIPg9axF8V9CA72Wz2mKF1teNrup8yzs=
|
||||
go.opentelemetry.io/otel/sdk v1.21.0 h1:FTt8qirL1EysG6sTQRZ5TokkU8d0ugCj8htOgThZXQ8=
|
||||
go.opentelemetry.io/otel/sdk v1.21.0/go.mod h1:Nna6Yv7PWTdgJHVRD9hIYywQBRx7pbox6nwBnZIxl/E=
|
||||
go.opentelemetry.io/otel/sdk v1.22.0 h1:6coWHw9xw7EfClIC/+O31R8IY3/+EiRFHevmHafB2Gw=
|
||||
go.opentelemetry.io/otel/sdk v1.22.0/go.mod h1:iu7luyVGYovrRpe2fmj3CVKouQNdTOkxtLzPvPz1DOc=
|
||||
go.opentelemetry.io/otel/trace v1.3.0/go.mod h1:c/VDhno8888bvQYmbYLqe41/Ldmr/KKunbvWM4/fEjk=
|
||||
go.opentelemetry.io/otel/trace v1.21.0 h1:WD9i5gzvoUPuXIXH24ZNBudiarZDKuekPqi/E8fpfLc=
|
||||
go.opentelemetry.io/otel/trace v1.21.0/go.mod h1:LGbsEB0f9LGjN+OZaQQ26sohbOmiMR+BaslueVtS/qQ=
|
||||
go.opentelemetry.io/otel/trace v1.22.0 h1:Hg6pPujv0XG9QaVbGOBVHunyuLcCC3jN7WEhPx83XD0=
|
||||
go.opentelemetry.io/otel/trace v1.22.0/go.mod h1:RbbHXVqKES9QhzZq/fE5UnOSILqRt40a21sPw2He1xo=
|
||||
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
|
||||
go.opentelemetry.io/proto/otlp v0.15.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U=
|
||||
go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U=
|
||||
@@ -2229,8 +2229,8 @@ golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY=
|
||||
golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
|
||||
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@@ -2621,8 +2621,8 @@ golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s=
|
||||
golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc=
|
||||
golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc=
|
||||
golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg=
|
||||
golang.org/x/tools v0.16.1 h1:TLyB3WofjdOEepBHAU20JdNC1Zbg87elYofWYAY5oZA=
|
||||
golang.org/x/tools v0.16.1/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
|
||||
@@ -10,7 +10,6 @@ import (
|
||||
|
||||
type storeOptionsKey struct{}
|
||||
type expiryKey struct{}
|
||||
type authKey struct{}
|
||||
|
||||
// StoreOptions sets the options for the underlying store
|
||||
func StoreOptions(opts []store.Option) registry.Option {
|
||||
@@ -31,13 +30,3 @@ func ServiceExpiry(t time.Duration) registry.Option {
|
||||
o.Context = context.WithValue(o.Context, expiryKey{}, t)
|
||||
}
|
||||
}
|
||||
|
||||
// Authenticate sets the username/password for the nats connection
|
||||
func Authenticate(username, password string) registry.Option {
|
||||
return func(o *registry.Options) {
|
||||
if o.Context == nil {
|
||||
o.Context = context.Background()
|
||||
}
|
||||
o.Context = context.WithValue(o.Context, authKey{}, []string{username, password})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
natsjskv "github.com/go-micro/plugins/v4/store/nats-js-kv"
|
||||
@@ -14,7 +16,12 @@ import (
|
||||
"go-micro.dev/v4/util/cmd"
|
||||
)
|
||||
|
||||
var _registryName = "nats-js-kv"
|
||||
var (
|
||||
_registryName = "nats-js-kv"
|
||||
_registryAddressEnv = "MICRO_REGISTRY_ADDRESS"
|
||||
_registryUsernameEnv = "MICRO_REGISTRY_AUTH_USERNAME"
|
||||
_registryPasswordEnv = "MICRO_REGISTRY_AUTH_PASSWORD"
|
||||
)
|
||||
|
||||
func init() {
|
||||
cmd.DefaultRegistries[_registryName] = NewRegistry
|
||||
@@ -127,18 +134,31 @@ func (n *storeregistry) String() string {
|
||||
|
||||
func storeOptions(opts registry.Options) []store.Option {
|
||||
storeoptions := []store.Option{
|
||||
store.Nodes(opts.Addrs...),
|
||||
store.Database("service-registry"),
|
||||
store.Table("service-registry"),
|
||||
natsjskv.DefaultMemory(),
|
||||
}
|
||||
|
||||
addr := []string{"127.0.0.1:9233"}
|
||||
if len(opts.Addrs) > 0 {
|
||||
addr = opts.Addrs
|
||||
} else if a := strings.Split(os.Getenv(_registryAddressEnv), ","); len(a) > 0 && a[0] != "" {
|
||||
addr = a
|
||||
}
|
||||
storeoptions = append(storeoptions, store.Nodes(addr...))
|
||||
|
||||
natsOptions := nats.GetDefaultOptions()
|
||||
natsOptions.Name = "nats-js-kv-registry"
|
||||
natsOptions.User, natsOptions.Password = getAuth()
|
||||
storeoptions = append(storeoptions, natsjskv.NatsOptions(natsOptions))
|
||||
|
||||
if so, ok := opts.Context.Value(storeOptionsKey{}).([]store.Option); ok {
|
||||
storeoptions = append(storeoptions, so...)
|
||||
}
|
||||
natsOptions := nats.GetDefaultOptions()
|
||||
natsOptions.Name = "nats-js-kv-registry"
|
||||
if auth, ok := opts.Context.Value(authKey{}).([]string); ok {
|
||||
natsOptions.User = auth[0]
|
||||
natsOptions.Password = auth[1]
|
||||
}
|
||||
return append(storeoptions, natsjskv.NatsOptions(natsOptions))
|
||||
|
||||
return storeoptions
|
||||
}
|
||||
|
||||
func getAuth() (string, string) {
|
||||
return os.Getenv(_registryUsernameEnv), os.Getenv(_registryPasswordEnv)
|
||||
}
|
||||
|
||||
@@ -20,10 +20,8 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
registryEnv = "MICRO_REGISTRY"
|
||||
registryAddressEnv = "MICRO_REGISTRY_ADDRESS"
|
||||
registryUsernameEnv = "MICRO_REGISTRY_AUTH_USERNAME"
|
||||
registryPasswordEnv = "MICRO_REGISTRY_AUTH_PASSWORD"
|
||||
_registryEnv = "MICRO_REGISTRY"
|
||||
_registryAddressEnv = "MICRO_REGISTRY_ADDRESS"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -64,7 +62,6 @@ func GetRegistry(opts ...Option) mRegistry.Registry {
|
||||
case "natsjs", "nats-js", "nats-js-kv": // for backwards compatibility - we will stick with one of those
|
||||
_reg = natsjsregistry.NewRegistry(
|
||||
mRegistry.Addrs(cfg.Addresses...),
|
||||
natsjsregistry.Authenticate(cfg.Username, cfg.Password),
|
||||
)
|
||||
case "memory":
|
||||
_reg = memr.NewRegistry()
|
||||
@@ -112,15 +109,13 @@ func getEnvs(opts ...Option) *Config {
|
||||
cfg := &Config{
|
||||
Type: "nats-js-kv",
|
||||
Addresses: []string{"127.0.0.1:9233"},
|
||||
Username: os.Getenv(registryUsernameEnv),
|
||||
Password: os.Getenv(registryPasswordEnv),
|
||||
}
|
||||
|
||||
if s := os.Getenv(registryEnv); s != "" {
|
||||
if s := os.Getenv(_registryEnv); s != "" {
|
||||
cfg.Type = s
|
||||
}
|
||||
|
||||
if s := strings.Split(os.Getenv(registryAddressEnv), ","); len(s) > 0 && s[0] != "" {
|
||||
if s := strings.Split(os.Getenv(_registryAddressEnv), ","); len(s) > 0 && s[0] != "" {
|
||||
cfg.Addresses = s
|
||||
}
|
||||
|
||||
|
||||
@@ -63,6 +63,8 @@ type Cache struct {
|
||||
TTL time.Duration `yaml:"ttl" env:"OCIS_CACHE_TTL" desc:"Time to live for events in the store. The duration can be set as number followed by a unit identifier like s, m or h."`
|
||||
Size int `yaml:"size" env:"OCIS_CACHE_SIZE" desc:"The maximum quantity of items in the store. Only applies when store type 'ocmem' is configured."`
|
||||
DisablePersistence bool `yaml:"disable_persistence" env:"OCIS_CACHE_DISABLE_PERSISTENCE" desc:"Disables persistence of the cache. Only applies when store type 'nats-js-kv' is configured. Defaults to false."`
|
||||
AuthUsername string `yaml:"auth_username" env:"OCIS_CACHE_AUTH_USERNAME" desc:"The username to use for authentication. Only applies when store type 'nats-js-kv' is configured."`
|
||||
AuthPassword string `yaml:"auth_password" env:"OCIS_CACHE_AUTH_PASSWORD" desc:"The password to use for authentication. Only applies when store type 'nats-js-kv' is configured."`
|
||||
}
|
||||
|
||||
// Commons holds configuration that are common to all extensions. Each extension can then decide whether
|
||||
|
||||
@@ -33,7 +33,7 @@ func Server(cfg *config.Config) *cli.Command {
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
logger := logging.Configure(cfg.Service.Name, cfg.Log)
|
||||
tracingProvider, err := tracing.GetServiceTraceProvider(cfg.Tracing, cfg.Service.Name)
|
||||
traceProvider, err := tracing.GetServiceTraceProvider(cfg.Tracing, cfg.Service.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -50,7 +50,7 @@ func Server(cfg *config.Config) *cli.Command {
|
||||
runtime.RunWithOptions(rCfg, pidFile,
|
||||
runtime.WithLogger(&logger.Logger),
|
||||
runtime.WithRegistry(reg),
|
||||
runtime.WithTraceProvider(tracingProvider),
|
||||
runtime.WithTraceProvider(traceProvider),
|
||||
)
|
||||
|
||||
return nil
|
||||
|
||||
@@ -32,7 +32,7 @@ func Server(cfg *config.Config) *cli.Command {
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
logger := logging.Configure(cfg.Service.Name, cfg.Log)
|
||||
tracingProvider, err := tracing.GetServiceTraceProvider(cfg.Tracing, cfg.Service.Name)
|
||||
traceProvider, err := tracing.GetServiceTraceProvider(cfg.Tracing, cfg.Service.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -49,7 +49,7 @@ func Server(cfg *config.Config) *cli.Command {
|
||||
runtime.RunWithOptions(rCfg, pidFile,
|
||||
runtime.WithLogger(&logger.Logger),
|
||||
runtime.WithRegistry(reg),
|
||||
runtime.WithTraceProvider(tracingProvider),
|
||||
runtime.WithTraceProvider(traceProvider),
|
||||
)
|
||||
|
||||
return nil
|
||||
|
||||
@@ -12,13 +12,13 @@ import (
|
||||
"github.com/owncloud/ocis/v2/ocis-pkg/config/configlog"
|
||||
"github.com/owncloud/ocis/v2/ocis-pkg/registry"
|
||||
"github.com/owncloud/ocis/v2/ocis-pkg/sync"
|
||||
"github.com/owncloud/ocis/v2/ocis-pkg/tracing"
|
||||
"github.com/owncloud/ocis/v2/ocis-pkg/version"
|
||||
"github.com/owncloud/ocis/v2/services/auth-service/pkg/config"
|
||||
"github.com/owncloud/ocis/v2/services/auth-service/pkg/config/parser"
|
||||
"github.com/owncloud/ocis/v2/services/auth-service/pkg/logging"
|
||||
"github.com/owncloud/ocis/v2/services/auth-service/pkg/revaconfig"
|
||||
"github.com/owncloud/ocis/v2/services/auth-service/pkg/server/debug"
|
||||
"github.com/owncloud/ocis/v2/services/auth-service/pkg/tracing"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
@@ -33,7 +33,7 @@ func Server(cfg *config.Config) *cli.Command {
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
logger := logging.Configure(cfg.Service.Name, cfg.Log)
|
||||
err := tracing.Configure(cfg, logger)
|
||||
traceProvider, err := tracing.GetServiceTraceProvider(cfg.Tracing, cfg.Service.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -43,11 +43,16 @@ func Server(cfg *config.Config) *cli.Command {
|
||||
defer cancel()
|
||||
|
||||
pidFile := path.Join(os.TempDir(), "revad-"+cfg.Service.Name+"-"+uuid.Must(uuid.NewV4()).String()+".pid")
|
||||
reg := registry.GetRegistry()
|
||||
|
||||
rcfg := revaconfig.AuthMachineConfigFromStruct(cfg)
|
||||
|
||||
gr.Add(func() error {
|
||||
runtime.RunWithOptions(rcfg, pidFile, runtime.WithLogger(&logger.Logger))
|
||||
runtime.RunWithOptions(rcfg, pidFile,
|
||||
runtime.WithLogger(&logger.Logger),
|
||||
runtime.WithRegistry(reg),
|
||||
runtime.WithTraceProvider(traceProvider),
|
||||
)
|
||||
return nil
|
||||
}, func(err error) {
|
||||
logger.Error().
|
||||
|
||||
@@ -24,12 +24,6 @@ type Config struct {
|
||||
Supervised bool `yaml:"-"`
|
||||
Context context.Context `yaml:"-"`
|
||||
}
|
||||
type Tracing struct {
|
||||
Enabled bool `yaml:"enabled" env:"OCIS_TRACING_ENABLED;AUTH_SERVICE_TRACING_ENABLED" desc:"Activates tracing."`
|
||||
Type string `yaml:"type" env:"OCIS_TRACING_TYPE;AUTH_SERVICE_TRACING_TYPE" desc:"The type of tracing. Defaults to '', which is the same as 'jaeger'. Allowed tracing types are 'jaeger' and '' as of now."`
|
||||
Endpoint string `yaml:"endpoint" env:"OCIS_TRACING_ENDPOINT;AUTH_SERVICE_TRACING_ENDPOINT" desc:"The endpoint of the tracing agent."`
|
||||
Collector string `yaml:"collector" env:"OCIS_TRACING_COLLECTOR;AUTH_SERVICE_TRACING_COLLECTOR" desc:"The HTTP endpoint for sending spans directly to a collector, i.e. http://jaeger-collector:14268/api/traces. Only used if the tracing endpoint is unset."`
|
||||
}
|
||||
|
||||
type Log struct {
|
||||
Level string `yaml:"level" env:"OCIS_LOG_LEVEL;AUTH_SERVICE_LOG_LEVEL" desc:"The log level. Valid values are: 'panic', 'fatal', 'error', 'warn', 'info', 'debug', 'trace'."`
|
||||
|
||||
21
services/auth-service/pkg/config/tracing.go
Normal file
21
services/auth-service/pkg/config/tracing.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package config
|
||||
|
||||
import "github.com/owncloud/ocis/v2/ocis-pkg/tracing"
|
||||
|
||||
// Tracing is the config for tracing parameters
|
||||
type Tracing struct {
|
||||
Enabled bool `yaml:"enabled" env:"OCIS_TRACING_ENABLED;AUTH_SERVICE_TRACING_ENABLED" desc:"Activates tracing."`
|
||||
Type string `yaml:"type" env:"OCIS_TRACING_TYPE;AUTH_SERVICE_TRACING_TYPE" desc:"The type of tracing. Defaults to '', which is the same as 'jaeger'. Allowed tracing types are 'jaeger' and '' as of now."`
|
||||
Endpoint string `yaml:"endpoint" env:"OCIS_TRACING_ENDPOINT;AUTH_SERVICE_TRACING_ENDPOINT" desc:"The endpoint of the tracing agent."`
|
||||
Collector string `yaml:"collector" env:"OCIS_TRACING_COLLECTOR;AUTH_SERVICE_TRACING_COLLECTOR" desc:"The HTTP endpoint for sending spans directly to a collector, i.e. http://jaeger-collector:14268/api/traces. Only used if the tracing endpoint is unset."`
|
||||
}
|
||||
|
||||
// Convert Tracing to the tracing package's Config struct.
|
||||
func (t Tracing) Convert() tracing.Config {
|
||||
return tracing.Config{
|
||||
Enabled: t.Enabled,
|
||||
Type: t.Type,
|
||||
Endpoint: t.Endpoint,
|
||||
Collector: t.Collector,
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
package tracing
|
||||
|
||||
import (
|
||||
"github.com/owncloud/ocis/v2/ocis-pkg/log"
|
||||
"github.com/owncloud/ocis/v2/services/auth-service/pkg/config"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
|
||||
pkgtrace "github.com/owncloud/ocis/v2/ocis-pkg/tracing"
|
||||
)
|
||||
|
||||
var (
|
||||
// TraceProvider is the global trace provider for the service.
|
||||
TraceProvider = trace.NewNoopTracerProvider()
|
||||
)
|
||||
|
||||
func Configure(cfg *config.Config, logger log.Logger) error {
|
||||
var err error
|
||||
if cfg.Tracing.Enabled {
|
||||
if TraceProvider, err = pkgtrace.GetTraceProvider(cfg.Tracing.Endpoint, cfg.Tracing.Collector, cfg.Service.Name, cfg.Tracing.Type); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -75,6 +75,8 @@ When setting the `FRONTEND_AUTO_ACCEPT_SHARES` to `true`, all incoming shares wi
|
||||
|
||||
Note that the password policy currently impacts only **public link password validation**.
|
||||
|
||||
In Infinite Scale, the password policy is always enabled because the max-length restriction is always applying and should be taken into account by the clients.
|
||||
|
||||
With the password policy, mandatory criteria for the password can be defined via the environment variables listed below.
|
||||
|
||||
Generally, a password can contain any UTF-8 characters, however some characters are regarded as special since they are not used in ordinary texts. Which characters should be treated as special is defined by "The OWASP® Foundation" [password-special-characters](https://owasp.org/www-community/password-special-characters) (between double quotes): " !"#$%&'()*+,-./:;<=>?@[\]^_`{|}~"
|
||||
|
||||
@@ -33,7 +33,7 @@ func Server(cfg *config.Config) *cli.Command {
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
logger := logging.Configure(cfg.Service.Name, cfg.Log)
|
||||
tracingProvider, err := tracing.GetServiceTraceProvider(cfg.Tracing, cfg.Service.Name)
|
||||
traceProvider, err := tracing.GetServiceTraceProvider(cfg.Tracing, cfg.Service.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -54,7 +54,7 @@ func Server(cfg *config.Config) *cli.Command {
|
||||
runtime.RunWithOptions(rCfg, pidFile,
|
||||
runtime.WithLogger(&logger.Logger),
|
||||
runtime.WithRegistry(reg),
|
||||
runtime.WithTraceProvider(tracingProvider),
|
||||
runtime.WithTraceProvider(traceProvider),
|
||||
)
|
||||
|
||||
return nil
|
||||
|
||||
@@ -137,6 +137,8 @@ type OCS struct {
|
||||
StatCacheTTL time.Duration `yaml:"stat_cache_ttl" env:"OCIS_CACHE_TTL;FRONTEND_OCS_STAT_CACHE_TTL" desc:"Default time to live for user info in the cache. Only applied when access tokens has no expiration. See the Environment Variable Types description for more details."`
|
||||
StatCacheSize int `yaml:"stat_cache_size" env:"OCIS_CACHE_SIZE;FRONTEND_OCS_STAT_CACHE_SIZE" desc:"Max number of entries to hold in the cache."`
|
||||
StatCacheDisablePersistence bool `yaml:"stat_cache_disable_persistence" env:"OCIS_CACHE_DISABLE_PERSISTENCE;FRONTEND_OCS_STAT_CACHE_DISABLE_PERSISTENCE" desc:"Disable persistence of the cache. Only applies when using the 'nats-js-kv' store type. Defaults to false."`
|
||||
StatCacheAuthUsername string `yaml:"stat_cache_auth_username" env:"OCIS_CACHE_AUTH_USERNAME;FRONTEND_OCS_STAT_CACHE_AUTH_USERNAME" desc:"The username to use for authentication. Only applies when using the 'nats-js-kv' store type."`
|
||||
StatCacheAuthPassword string `yaml:"stat_cache_auth_password" env:"OCIS_CACHE_AUTH_PASSWORD;FRONTEND_OCS_STAT_CACHE_AUTH_PASSWORD" desc:"The password to use for authentication. Only applies when using the 'nats-js-kv' store type."`
|
||||
|
||||
CacheWarmupDriver string `yaml:"cache_warmup_driver,omitempty"` // not supported by the oCIS product, therefore not part of docs
|
||||
CacheWarmupDrivers CacheWarmupDrivers `yaml:"cache_warmup_drivers,omitempty"` // not supported by the oCIS product, therefore not part of docs
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/owncloud/ocis/v2/ocis-pkg/config/defaults"
|
||||
"github.com/owncloud/ocis/v2/ocis-pkg/log"
|
||||
@@ -25,14 +24,10 @@ func FrontendConfigFromStruct(cfg *config.Config, logger log.Logger) (map[string
|
||||
webURL.Path = path.Join(webURL.Path, "external")
|
||||
webOpenInAppURL := webURL.String()
|
||||
|
||||
var bannedPasswordsList map[string]struct{}
|
||||
if cfg.PasswordPolicy.BannedPasswordsList != "" {
|
||||
bannedPasswordsList, err = readMultilineFile(cfg.PasswordPolicy.BannedPasswordsList)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("failed to load the banned passwords from a file %s: %w", cfg.PasswordPolicy.BannedPasswordsList, err)
|
||||
logger.Err(err).Send()
|
||||
return nil, err
|
||||
}
|
||||
passwordPolicyCfg, err := passwordPolicyConfig(cfg)
|
||||
if err != nil {
|
||||
logger.Err(err).Send()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
archivers := []map[string]interface{}{
|
||||
@@ -164,9 +159,11 @@ func FrontendConfigFromStruct(cfg *config.Config, logger log.Logger) (map[string
|
||||
"cache_nodes": cfg.OCS.StatCacheNodes,
|
||||
"cache_database": cfg.OCS.StatCacheDatabase,
|
||||
"cache_table": cfg.OCS.StatCacheTable,
|
||||
"cache_ttl": cfg.OCS.StatCacheTTL / time.Second,
|
||||
"cache_ttl": cfg.OCS.StatCacheTTL,
|
||||
"cache_size": cfg.OCS.StatCacheSize,
|
||||
"cache_disable_persistence": cfg.OCS.StatCacheDisablePersistence,
|
||||
"cache_auth_username": cfg.OCS.StatCacheAuthUsername,
|
||||
"cache_auth_password": cfg.OCS.StatCacheAuthPassword,
|
||||
},
|
||||
"prefix": cfg.OCS.Prefix,
|
||||
"additional_info_attribute": cfg.OCS.AdditionalInfoAttribute,
|
||||
@@ -327,16 +324,7 @@ func FrontendConfigFromStruct(cfg *config.Config, logger log.Logger) (map[string
|
||||
},
|
||||
},
|
||||
},
|
||||
"password_policy": map[string]interface{}{
|
||||
"max_characters": 72,
|
||||
"disabled": cfg.PasswordPolicy.Disabled,
|
||||
"min_characters": cfg.PasswordPolicy.MinCharacters,
|
||||
"min_lowercase_characters": cfg.PasswordPolicy.MinLowerCaseCharacters,
|
||||
"min_uppercase_characters": cfg.PasswordPolicy.MinUpperCaseCharacters,
|
||||
"min_digits": cfg.PasswordPolicy.MinDigits,
|
||||
"min_special_characters": cfg.PasswordPolicy.MinSpecialCharacters,
|
||||
"banned_passwords_list": bannedPasswordsList,
|
||||
},
|
||||
"password_policy": passwordPolicyCfg,
|
||||
"notifications": map[string]interface{}{
|
||||
"endpoints": []string{"list", "get", "delete"},
|
||||
},
|
||||
@@ -385,3 +373,30 @@ func fileExists(path string) bool {
|
||||
}
|
||||
return !info.IsDir()
|
||||
}
|
||||
|
||||
func passwordPolicyConfig(cfg *config.Config) (map[string]interface{}, error) {
|
||||
_maxCharacters := 72
|
||||
if cfg.PasswordPolicy.Disabled {
|
||||
return map[string]interface{}{
|
||||
"max_characters": _maxCharacters,
|
||||
"banned_passwords_list": nil,
|
||||
}, nil
|
||||
}
|
||||
var bannedPasswordsList map[string]struct{}
|
||||
var err error
|
||||
if cfg.PasswordPolicy.BannedPasswordsList != "" {
|
||||
bannedPasswordsList, err = readMultilineFile(cfg.PasswordPolicy.BannedPasswordsList)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to load the banned passwords from a file %s: %w", cfg.PasswordPolicy.BannedPasswordsList, err)
|
||||
}
|
||||
}
|
||||
return map[string]interface{}{
|
||||
"max_characters": _maxCharacters,
|
||||
"min_digits": cfg.PasswordPolicy.MinDigits,
|
||||
"min_characters": cfg.PasswordPolicy.MinCharacters,
|
||||
"min_lowercase_characters": cfg.PasswordPolicy.MinLowerCaseCharacters,
|
||||
"min_uppercase_characters": cfg.PasswordPolicy.MinUpperCaseCharacters,
|
||||
"min_special_characters": cfg.PasswordPolicy.MinSpecialCharacters,
|
||||
"banned_passwords_list": bannedPasswordsList,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -85,22 +85,20 @@ type StorageRegistry struct {
|
||||
|
||||
// Cache holds cache config
|
||||
type Cache struct {
|
||||
StatCacheStore string // NOTE: The stat cache is not working atm. Hence we block configuring it
|
||||
StatCacheNodes []string `yaml:"stat_cache_nodes" env:"OCIS_CACHE_STORE_NODES;GATEWAY_STAT_CACHE_STORE_NODES" desc:"A list of nodes to access the configured store. This has no effect when 'memory' or 'ocmem' stores are configured. Note that the behaviour how nodes are used is dependent on the library of the configured store. See the Environment Variable Types description for more details."`
|
||||
StatCacheDatabase string `yaml:"stat_cache_database" env:"OCIS_CACHE_DATABASE" desc:"The database name the configured store should use."`
|
||||
StatCacheTTL time.Duration `yaml:"stat_cache_ttl" env:"OCIS_CACHE_TTL;GATEWAY_STAT_CACHE_TTL" desc:"Default time to live for user info in the cache. Only applied when access tokens has no expiration. See the Environment Variable Types description for more details."`
|
||||
StatCacheSize int `yaml:"stat_cache_size" env:"OCIS_CACHE_SIZE;GATEWAY_STAT_CACHE_SIZE" desc:"The maximum quantity of items in the cache. Only applies when store type 'ocmem' is configured. Defaults to 512 which is derived from the ocmem package though not exclicitely set as default."`
|
||||
StatCacheDisablePersistence bool `yaml:"stat_cache_disable_persistence" env:"OCIS_CACHE_DISABLE_PERSISTENCE;GATEWAY_STAT_CACHE_DISABLE_PERSISTENCE" desc:"Disables persistence of the stat cache. Only applies when store type 'nats-js-kv' is configured. Defaults to false."`
|
||||
ProviderCacheStore string `yaml:"provider_cache_store" env:"OCIS_CACHE_STORE;GATEWAY_PROVIDER_CACHE_STORE" desc:"The type of the cache store. Supported values are: 'memory', 'redis-sentinel', 'nats-js-kv', 'noop'. See the text description for details."`
|
||||
ProviderCacheNodes []string `yaml:"provider_cache_nodes" env:"OCIS_CACHE_STORE_NODES;GATEWAY_PROVIDER_CACHE_STORE_NODES" desc:"A list of nodes to access the configured store. This has no effect when 'memory' or 'ocmem' stores are configured. Note that the behaviour how nodes are used is dependent on the library of the configured store. See the Environment Variable Types description for more details."`
|
||||
ProviderCacheDatabase string `yaml:"provider_cache_database" env:"OCIS_CACHE_DATABASE" desc:"The database name the configured store should use."`
|
||||
ProviderCacheTTL time.Duration `yaml:"provider_cache_ttl" env:"OCIS_CACHE_TTL;GATEWAY_PROVIDER_CACHE_TTL" desc:"Default time to live for user info in the cache. Only applied when access tokens has no expiration. See the Environment Variable Types description for more details."`
|
||||
ProviderCacheSize int `yaml:"provider_cache_size" env:"OCIS_CACHE_SIZE;GATEWAY_PROVIDER_CACHE_SIZE" desc:"The maximum quantity of items in the cache. Only applies when store type 'ocmem' is configured. Defaults to 512 which is derived from the ocmem package though not exclicitely set as default."`
|
||||
ProviderCacheDisablePersistence bool `yaml:"provider_cache_disable_persistence" env:"OCIS_CACHE_DISABLE_PERSISTENCE;GATEWAY_PROVIDER_CACHE_DISABLE_PERSISTENCE" desc:"Disables persistence of the provider cache. Only applies when store type 'nats-js-kv' is configured. Defaults to false."`
|
||||
ProviderCacheAuthUsername string `yaml:"provider_cache_auth_username" env:"OCIS_CACHE_AUTH_USERNAME;GATEWAY_PROVIDER_CACHE_AUTH_USERNAME" desc:"The username to use for authentication. Only applies when store type 'nats-js-kv' is configured."`
|
||||
ProviderCacheAuthPassword string `yaml:"provider_cache_auth_password" env:"OCIS_CACHE_AUTH_PASSWORD;GATEWAY_PROVIDER_CACHE_AUTH_PASSWORD" desc:"The password to use for authentication. Only applies when store type 'nats-js-kv' is configured."`
|
||||
CreateHomeCacheStore string `yaml:"create_home_cache_store" env:"OCIS_CACHE_STORE;GATEWAY_CREATE_HOME_CACHE_STORE" desc:"The type of the cache store. Supported values are: 'memory', 'redis-sentinel', 'nats-js-kv', 'noop'. See the text description for details."`
|
||||
CreateHomeCacheNodes []string `yaml:"create_home_cache_nodes" env:"OCIS_CACHE_STORE_NODES;GATEWAY_CREATE_HOME_CACHE_STORE_NODES" desc:"A list of nodes to access the configured store. This has no effect when 'memory' or 'ocmem' stores are configured. Note that the behaviour how nodes are used is dependent on the library of the configured store. See the Environment Variable Types description for more details."`
|
||||
CreateHomeCacheDatabase string `yaml:"create_home_cache_database" env:"OCIS_CACHE_DATABASE" desc:"The database name the configured store should use."`
|
||||
CreateHomeCacheTTL time.Duration `yaml:"create_home_cache_ttl" env:"OCIS_CACHE_TTL;GATEWAY_CREATE_HOME_CACHE_TTL" desc:"Default time to live for user info in the cache. Only applied when access tokens has no expiration. See the Environment Variable Types description for more details."`
|
||||
CreateHomeCacheSize int `yaml:"create_home_cache_size" env:"OCIS_CACHE_SIZE;GATEWAY_CREATE_HOME_CACHE_SIZE" desc:"The maximum quantity of items in the cache. Only applies when store type 'ocmem' is configured. Defaults to 512 which is derived from the ocmem package though not exclicitely set as default."`
|
||||
CreateHomeCacheDisablePersistence bool `yaml:"create_home_cache_disable_persistence" env:"OCIS_CACHE_DISABLE_PERSISTENCE;GATEWAY_CREATE_HOME_CACHE_DISABLE_PERSISTENCE" desc:"Disables persistence of the create home cache. Only applies when store type 'nats-js-kv' is configured. Defaults to false."`
|
||||
CreateHomeCacheAuthUsername string `yaml:"create_home_cache_auth_username" env:"OCIS_CACHE_AUTH_USERNAME;GATEWAY_CREATE_HOME_CACHE_AUTH_USERNAME" desc:"The username to use for authentication. Only applies when store type 'nats-js-kv' is configured."`
|
||||
CreateHomeCacheAuthPassword string `yaml:"create_home_cache_auth_password" env:"OCIS_CACHE_AUTH_PASSWORD;GATEWAY_CREATE_HOME_CACHE_AUTH_PASSWORD" desc:"The password to use for authentication. Only applies when store type 'nats-js-kv' is configured."`
|
||||
}
|
||||
|
||||
@@ -39,10 +39,6 @@ func DefaultConfig() *config.Config {
|
||||
DisableHomeCreationOnLogin: true,
|
||||
TransferExpires: 24 * 60 * 60,
|
||||
Cache: config.Cache{
|
||||
StatCacheStore: "noop", // NOTE: stat cache not working
|
||||
StatCacheDatabase: "ocis",
|
||||
StatCacheNodes: []string{"127.0.0.1:9233"},
|
||||
StatCacheTTL: 300 * time.Second,
|
||||
ProviderCacheStore: "noop",
|
||||
ProviderCacheNodes: []string{"127.0.0.1:9233"},
|
||||
ProviderCacheDatabase: "cache-providers",
|
||||
|
||||
@@ -61,15 +61,6 @@ func GatewayConfigFromStruct(cfg *config.Config, logger log.Logger) map[string]i
|
||||
"transfer_shared_secret": cfg.TransferSecret,
|
||||
"transfer_expires": cfg.TransferExpires,
|
||||
// cache and TTLs
|
||||
"stat_cache_config": map[string]interface{}{
|
||||
"cache_store": cfg.Cache.StatCacheStore,
|
||||
"cache_nodes": cfg.Cache.StatCacheNodes,
|
||||
"cache_database": cfg.Cache.StatCacheDatabase,
|
||||
"cache_table": "stat",
|
||||
"cache_ttl": cfg.Cache.StatCacheTTL,
|
||||
"cache_size": cfg.Cache.StatCacheSize,
|
||||
"cache_disable_persistenc": cfg.Cache.StatCacheDisablePersistence,
|
||||
},
|
||||
"provider_cache_config": map[string]interface{}{
|
||||
"cache_store": cfg.Cache.ProviderCacheStore,
|
||||
"cache_nodes": cfg.Cache.ProviderCacheNodes,
|
||||
@@ -78,6 +69,8 @@ func GatewayConfigFromStruct(cfg *config.Config, logger log.Logger) map[string]i
|
||||
"cache_ttl": cfg.Cache.ProviderCacheTTL,
|
||||
"cache_size": cfg.Cache.ProviderCacheSize,
|
||||
"disable_persistence": cfg.Cache.ProviderCacheDisablePersistence,
|
||||
"cache_auth_username": cfg.Cache.ProviderCacheAuthUsername,
|
||||
"cache_auth_password": cfg.Cache.ProviderCacheAuthPassword,
|
||||
},
|
||||
"create_home_cache_config": map[string]interface{}{
|
||||
"cache_store": cfg.Cache.CreateHomeCacheStore,
|
||||
@@ -87,6 +80,8 @@ func GatewayConfigFromStruct(cfg *config.Config, logger log.Logger) map[string]i
|
||||
"cache_ttl": cfg.Cache.CreateHomeCacheTTL,
|
||||
"cache_size": cfg.Cache.CreateHomeCacheSize,
|
||||
"cache_disable_persistence": cfg.Cache.CreateHomeCacheDisablePersistence,
|
||||
"cache_auth_username": cfg.Cache.CreateHomeCacheAuthUsername,
|
||||
"cache_auth_password": cfg.Cache.CreateHomeCacheAuthPassword,
|
||||
},
|
||||
},
|
||||
"authregistry": map[string]interface{}{
|
||||
|
||||
@@ -2,6 +2,7 @@ package svc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
@@ -41,7 +42,6 @@ import (
|
||||
"github.com/owncloud/ocis/v2/services/graph/pkg/validate"
|
||||
)
|
||||
|
||||
// From https://learn.microsoft.com/en-us/graph/api/driveitem-createuploadsession?view=graph-rest-1.0
|
||||
// CreateUploadSession create an upload session to allow your app to upload files up to the maximum file size.
|
||||
// An upload session allows your app to upload ranges of the file in sequential API requests, which allows the
|
||||
// transfer to be resumed if a connection is dropped while the upload is in progress.
|
||||
@@ -55,6 +55,7 @@ import (
|
||||
// }
|
||||
//
|
||||
// ```
|
||||
// From https://learn.microsoft.com/en-us/graph/api/driveitem-createuploadsession?view=graph-rest-1.0
|
||||
func (g Graph) CreateUploadSession(w http.ResponseWriter, r *http.Request) {
|
||||
g.logger.Info().Msg("Calling CreateUploadSession")
|
||||
|
||||
@@ -68,7 +69,7 @@ func (g Graph) CreateUploadSession(w http.ResponseWriter, r *http.Request) {
|
||||
errorcode.RenderError(w, r, err)
|
||||
return
|
||||
}
|
||||
if driveID.StorageId != driveItemID.StorageId || driveID.SpaceId != driveItemID.SpaceId {
|
||||
if driveID.GetStorageId() != driveItemID.GetStorageId() || driveID.GetSpaceId() != driveItemID.GetSpaceId() {
|
||||
errorcode.ItemNotFound.Render(w, r, http.StatusNotFound, "Item does not exist")
|
||||
return
|
||||
}
|
||||
@@ -101,27 +102,27 @@ func (g Graph) CreateUploadSession(w http.ResponseWriter, r *http.Request) {
|
||||
case err != nil:
|
||||
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
case res.Status.Code == cs3rpc.Code_CODE_OK:
|
||||
case res.GetStatus().GetCode() == cs3rpc.Code_CODE_OK:
|
||||
// ok
|
||||
case res.Status.Code == cs3rpc.Code_CODE_NOT_FOUND:
|
||||
errorcode.ItemNotFound.Render(w, r, http.StatusNotFound, res.Status.Message)
|
||||
case res.GetStatus().GetCode() == cs3rpc.Code_CODE_NOT_FOUND:
|
||||
errorcode.ItemNotFound.Render(w, r, http.StatusNotFound, res.GetStatus().GetMessage())
|
||||
return
|
||||
case res.Status.Code == cs3rpc.Code_CODE_PERMISSION_DENIED:
|
||||
errorcode.ItemNotFound.Render(w, r, http.StatusNotFound, res.Status.Message) // do not leak existence? check what graph does
|
||||
case res.GetStatus().GetCode() == cs3rpc.Code_CODE_PERMISSION_DENIED:
|
||||
errorcode.ItemNotFound.Render(w, r, http.StatusNotFound, res.GetStatus().GetMessage()) // do not leak existence? check what graph does
|
||||
return
|
||||
case res.Status.Code == cs3rpc.Code_CODE_UNAUTHENTICATED:
|
||||
errorcode.Unauthenticated.Render(w, r, http.StatusUnauthorized, res.Status.Message) // do not leak existence? check what graph does
|
||||
case res.GetStatus().GetCode() == cs3rpc.Code_CODE_UNAUTHENTICATED:
|
||||
errorcode.Unauthenticated.Render(w, r, http.StatusUnauthorized, res.GetStatus().GetMessage()) // do not leak existence? check what graph does
|
||||
return
|
||||
default:
|
||||
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, res.Status.Message)
|
||||
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, res.GetStatus().GetMessage())
|
||||
return
|
||||
}
|
||||
uploadSession := uploadSession{
|
||||
CS3Protocols: res.Protocols,
|
||||
CS3Protocols: res.GetProtocols(),
|
||||
}
|
||||
for _, p := range res.Protocols {
|
||||
if p.Protocol == "simple" {
|
||||
uploadSession.UploadUrl = p.UploadEndpoint + "/" + p.Token
|
||||
for _, p := range res.GetProtocols() {
|
||||
if p.GetProtocol() == "simple" {
|
||||
uploadSession.UploadURL = p.GetUploadEndpoint() + "/" + p.GetToken()
|
||||
}
|
||||
}
|
||||
render.Status(r, http.StatusOK)
|
||||
@@ -129,18 +130,18 @@ func (g Graph) CreateUploadSession(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
type createUploadSessionRequest struct {
|
||||
DeferCommit bool
|
||||
Item driveItemUploadableProperties
|
||||
DeferCommit bool `json:"deferCommit"`
|
||||
Item driveItemUploadableProperties `json:"item"`
|
||||
}
|
||||
type driveItemUploadableProperties struct {
|
||||
// ConflictBehavior "@microsoft.graph.conflictBehavior"
|
||||
//Description string
|
||||
FileSize int64
|
||||
FileSize int64 `json:"fileSize"`
|
||||
// fileSystemInfo
|
||||
Name string
|
||||
Name string `json:"name"`
|
||||
}
|
||||
type uploadSession struct {
|
||||
UploadUrl string
|
||||
UploadURL string
|
||||
//"expirationDateTime": "2015-01-29T09:21:55.523Z",
|
||||
//"nextExpectedRanges": ["0-"]
|
||||
CS3Protocols []*gateway.FileUploadProtocol
|
||||
@@ -161,7 +162,7 @@ func (g Graph) GetRootDriveChildren(w http.ResponseWriter, r *http.Request) {
|
||||
currentUser := revactx.ContextMustGetUser(r.Context())
|
||||
// do we need to list all or only the personal drive
|
||||
filters := []*storageprovider.ListStorageSpacesRequest_Filter{}
|
||||
filters = append(filters, listStorageSpacesUserFilter(currentUser.GetId().OpaqueId))
|
||||
filters = append(filters, listStorageSpacesUserFilter(currentUser.GetId().GetOpaqueId()))
|
||||
filters = append(filters, listStorageSpacesTypeFilter("personal"))
|
||||
|
||||
res, err := gatewayClient.ListStorageSpaces(ctx, &storageprovider.ListStorageSpacesRequest{
|
||||
@@ -172,47 +173,47 @@ func (g Graph) GetRootDriveChildren(w http.ResponseWriter, r *http.Request) {
|
||||
g.logger.Error().Err(err).Msg("error making ListStorageSpaces grpc call")
|
||||
errorcode.ServiceNotAvailable.Render(w, r, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
case res.Status.Code != cs3rpc.Code_CODE_OK:
|
||||
if res.Status.Code == cs3rpc.Code_CODE_NOT_FOUND {
|
||||
errorcode.ItemNotFound.Render(w, r, http.StatusNotFound, res.Status.Message)
|
||||
case res.GetStatus().GetCode() != cs3rpc.Code_CODE_OK:
|
||||
if res.GetStatus().GetCode() == cs3rpc.Code_CODE_NOT_FOUND {
|
||||
errorcode.ItemNotFound.Render(w, r, http.StatusNotFound, res.GetStatus().GetMessage())
|
||||
return
|
||||
}
|
||||
g.logger.Error().Err(err).Msg("error sending ListStorageSpaces grpc request")
|
||||
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, res.Status.Message)
|
||||
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, res.GetStatus().GetMessage())
|
||||
return
|
||||
}
|
||||
|
||||
var space *storageprovider.StorageSpace
|
||||
for _, s := range res.StorageSpaces {
|
||||
for _, s := range res.GetStorageSpaces() {
|
||||
if utils.UserIDEqual(currentUser.GetId(), s.GetOwner().GetId()) {
|
||||
space = s
|
||||
}
|
||||
}
|
||||
|
||||
lRes, err := gatewayClient.ListContainer(ctx, &storageprovider.ListContainerRequest{
|
||||
Ref: &storageprovider.Reference{ResourceId: space.Root},
|
||||
Ref: &storageprovider.Reference{ResourceId: space.GetRoot()},
|
||||
})
|
||||
switch {
|
||||
case err != nil:
|
||||
g.logger.Error().Err(err).Msg("error making ListContainer grpc call")
|
||||
errorcode.ServiceNotAvailable.Render(w, r, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
case lRes.Status.Code != cs3rpc.Code_CODE_OK:
|
||||
if lRes.Status.Code == cs3rpc.Code_CODE_NOT_FOUND {
|
||||
errorcode.ItemNotFound.Render(w, r, http.StatusNotFound, lRes.Status.Message)
|
||||
case lRes.GetStatus().GetCode() != cs3rpc.Code_CODE_OK:
|
||||
if lRes.GetStatus().GetCode() == cs3rpc.Code_CODE_NOT_FOUND {
|
||||
errorcode.ItemNotFound.Render(w, r, http.StatusNotFound, lRes.GetStatus().GetMessage())
|
||||
return
|
||||
}
|
||||
if lRes.Status.Code == cs3rpc.Code_CODE_PERMISSION_DENIED {
|
||||
if lRes.GetStatus().GetCode() == cs3rpc.Code_CODE_PERMISSION_DENIED {
|
||||
// TODO check if we should return 404 to not disclose existing items
|
||||
errorcode.AccessDenied.Render(w, r, http.StatusForbidden, lRes.Status.Message)
|
||||
errorcode.AccessDenied.Render(w, r, http.StatusForbidden, lRes.GetStatus().GetMessage())
|
||||
return
|
||||
}
|
||||
g.logger.Error().Err(err).Msg("error sending list container grpc request")
|
||||
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, res.Status.Message)
|
||||
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, res.GetStatus().GetMessage())
|
||||
return
|
||||
}
|
||||
|
||||
files, err := formatDriveItems(g.logger, lRes.Infos)
|
||||
files, err := formatDriveItems(g.logger, lRes.GetInfos())
|
||||
if err != nil {
|
||||
g.logger.Error().Err(err).Msg("error encoding response as json")
|
||||
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, err.Error())
|
||||
@@ -238,7 +239,7 @@ func (g Graph) GetDriveItem(w http.ResponseWriter, r *http.Request) {
|
||||
errorcode.RenderError(w, r, err)
|
||||
return
|
||||
}
|
||||
if driveID.StorageId != driveItemID.StorageId || driveID.SpaceId != driveItemID.SpaceId {
|
||||
if driveID.GetStorageId() != driveItemID.GetStorageId() || driveID.GetSpaceId() != driveItemID.GetSpaceId() {
|
||||
errorcode.ItemNotFound.Render(w, r, http.StatusNotFound, "Item does not exist")
|
||||
return
|
||||
}
|
||||
@@ -262,22 +263,22 @@ func (g Graph) GetDriveItem(w http.ResponseWriter, r *http.Request) {
|
||||
case err != nil:
|
||||
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
case res.Status.Code == cs3rpc.Code_CODE_OK:
|
||||
case res.GetStatus().GetCode() == cs3rpc.Code_CODE_OK:
|
||||
// ok
|
||||
case res.Status.Code == cs3rpc.Code_CODE_NOT_FOUND:
|
||||
errorcode.ItemNotFound.Render(w, r, http.StatusNotFound, res.Status.Message)
|
||||
case res.GetStatus().GetCode() == cs3rpc.Code_CODE_NOT_FOUND:
|
||||
errorcode.ItemNotFound.Render(w, r, http.StatusNotFound, res.GetStatus().GetMessage())
|
||||
return
|
||||
case res.Status.Code == cs3rpc.Code_CODE_PERMISSION_DENIED:
|
||||
errorcode.ItemNotFound.Render(w, r, http.StatusNotFound, res.Status.Message) // do not leak existence? check what graph does
|
||||
case res.GetStatus().GetCode() == cs3rpc.Code_CODE_PERMISSION_DENIED:
|
||||
errorcode.ItemNotFound.Render(w, r, http.StatusNotFound, res.GetStatus().GetMessage()) // do not leak existence? check what graph does
|
||||
return
|
||||
case res.Status.Code == cs3rpc.Code_CODE_UNAUTHENTICATED:
|
||||
errorcode.Unauthenticated.Render(w, r, http.StatusUnauthorized, res.Status.Message) // do not leak existence? check what graph does
|
||||
case res.GetStatus().GetCode() == cs3rpc.Code_CODE_UNAUTHENTICATED:
|
||||
errorcode.Unauthenticated.Render(w, r, http.StatusUnauthorized, res.GetStatus().GetMessage()) // do not leak existence? check what graph does
|
||||
return
|
||||
default:
|
||||
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, res.Status.Message)
|
||||
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, res.GetStatus().GetMessage())
|
||||
return
|
||||
}
|
||||
driveItem, err := cs3ResourceToDriveItem(g.logger, res.Info)
|
||||
driveItem, err := cs3ResourceToDriveItem(g.logger, res.GetInfo())
|
||||
if err != nil {
|
||||
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
@@ -302,7 +303,7 @@ func (g Graph) GetDriveItemChildren(w http.ResponseWriter, r *http.Request) {
|
||||
errorcode.RenderError(w, r, err)
|
||||
return
|
||||
}
|
||||
if driveID.StorageId != driveItemID.StorageId || driveID.SpaceId != driveItemID.SpaceId {
|
||||
if driveID.GetStorageId() != driveItemID.GetStorageId() || driveID.GetSpaceId() != driveItemID.GetSpaceId() {
|
||||
errorcode.ItemNotFound.Render(w, r, http.StatusNotFound, "Item does not exist")
|
||||
return
|
||||
}
|
||||
@@ -329,23 +330,23 @@ func (g Graph) GetDriveItemChildren(w http.ResponseWriter, r *http.Request) {
|
||||
case err != nil:
|
||||
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
case res.Status.Code == cs3rpc.Code_CODE_OK:
|
||||
case res.GetStatus().GetCode() == cs3rpc.Code_CODE_OK:
|
||||
// ok
|
||||
case res.Status.Code == cs3rpc.Code_CODE_NOT_FOUND:
|
||||
errorcode.ItemNotFound.Render(w, r, http.StatusNotFound, res.Status.Message)
|
||||
case res.GetStatus().GetCode() == cs3rpc.Code_CODE_NOT_FOUND:
|
||||
errorcode.ItemNotFound.Render(w, r, http.StatusNotFound, res.GetStatus().GetMessage())
|
||||
return
|
||||
case res.Status.Code == cs3rpc.Code_CODE_PERMISSION_DENIED:
|
||||
errorcode.ItemNotFound.Render(w, r, http.StatusNotFound, res.Status.Message) // do not leak existence? check what graph does
|
||||
case res.GetStatus().GetCode() == cs3rpc.Code_CODE_PERMISSION_DENIED:
|
||||
errorcode.ItemNotFound.Render(w, r, http.StatusNotFound, res.GetStatus().GetMessage()) // do not leak existence? check what graph does
|
||||
return
|
||||
case res.Status.Code == cs3rpc.Code_CODE_UNAUTHENTICATED:
|
||||
errorcode.Unauthenticated.Render(w, r, http.StatusUnauthorized, res.Status.Message) // do not leak existence? check what graph does
|
||||
case res.GetStatus().GetCode() == cs3rpc.Code_CODE_UNAUTHENTICATED:
|
||||
errorcode.Unauthenticated.Render(w, r, http.StatusUnauthorized, res.GetStatus().GetMessage()) // do not leak existence? check what graph does
|
||||
return
|
||||
default:
|
||||
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, res.Status.Message)
|
||||
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, res.GetStatus().GetMessage())
|
||||
return
|
||||
}
|
||||
|
||||
files, err := formatDriveItems(g.logger, res.Infos)
|
||||
files, err := formatDriveItems(g.logger, res.GetInfos())
|
||||
if err != nil {
|
||||
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
@@ -372,11 +373,16 @@ func (g Graph) ListPermissions(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
statResponse, err := gatewayClient.Stat(ctx, &storageprovider.StatRequest{Ref: &storageprovider.Reference{ResourceId: &itemID}})
|
||||
if errCode := errorcode.FromStat(statResponse, err); errCode != nil {
|
||||
g.logger.Warn().Err(errCode).Interface("stat.res", statResponse)
|
||||
g.logger.Warn().Err(errCode).Interface("stat.res", statResponse).Msg("stat failed")
|
||||
errCode.Render(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
condition := unifiedrole.UnifiedRoleConditionGrantee
|
||||
if IsSpaceRoot(statResponse.GetInfo().GetId()) {
|
||||
condition = unifiedrole.UnifiedRoleConditionOwner
|
||||
}
|
||||
|
||||
permissionSet := *statResponse.GetInfo().GetPermissionSet()
|
||||
allowedActions := unifiedrole.CS3ResourcePermissionsToLibregraphActions(permissionSet)
|
||||
|
||||
@@ -385,7 +391,7 @@ func (g Graph) ListPermissions(w http.ResponseWriter, r *http.Request) {
|
||||
LibreGraphPermissionsRolesAllowedValues: conversions.ToValueSlice(
|
||||
unifiedrole.GetApplicableRoleDefinitionsForActions(
|
||||
allowedActions,
|
||||
unifiedrole.UnifiedRoleConditionGrantee,
|
||||
condition,
|
||||
g.config.FilesSharing.EnableResharing,
|
||||
false,
|
||||
),
|
||||
@@ -451,28 +457,39 @@ func (g Graph) Invite(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
statResponse, err := gatewayClient.Stat(ctx, &storageprovider.StatRequest{Ref: &storageprovider.Reference{ResourceId: &itemID}})
|
||||
if errCode := errorcode.FromStat(statResponse, err); errCode != nil {
|
||||
g.logger.Warn().Err(errCode).Interface("stat.res", statResponse).Msg("stat failed")
|
||||
errCode.Render(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
condition := unifiedrole.UnifiedRoleConditionGrantee
|
||||
if IsSpaceRoot(statResponse.GetInfo().GetId()) {
|
||||
condition = unifiedrole.UnifiedRoleConditionOwner
|
||||
}
|
||||
|
||||
unifiedRolePermissions := []*libregraph.UnifiedRolePermission{{AllowedResourceActions: driveItemInvite.LibreGraphPermissionsActions}}
|
||||
for _, roleId := range driveItemInvite.GetRoles() {
|
||||
role, err := unifiedrole.NewUnifiedRoleFromID(roleId, g.config.FilesSharing.EnableResharing)
|
||||
for _, roleID := range driveItemInvite.GetRoles() {
|
||||
role, err := unifiedrole.NewUnifiedRoleFromID(roleID, g.config.FilesSharing.EnableResharing)
|
||||
if err != nil {
|
||||
g.logger.Debug().Err(err).Interface("role", driveItemInvite.GetRoles()[0]).Msg("unable to convert requested role")
|
||||
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError))
|
||||
return
|
||||
}
|
||||
|
||||
unifiedRolePermissions = append(unifiedRolePermissions, conversions.ToPointerSlice(role.GetRolePermissions())...)
|
||||
}
|
||||
allowedResourceActions := unifiedrole.GetAllowedResourceActions(role, condition)
|
||||
if len(allowedResourceActions) == 0 {
|
||||
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "role not applicable to this resource")
|
||||
return
|
||||
}
|
||||
|
||||
statResponse, err := gatewayClient.Stat(ctx, &storageprovider.StatRequest{Ref: &storageprovider.Reference{ResourceId: &itemID}})
|
||||
if errCode := errorcode.FromStat(statResponse, err); errCode != nil {
|
||||
g.logger.Warn().Err(errCode).Interface("stat.res", statResponse)
|
||||
errCode.Render(w, r)
|
||||
return
|
||||
unifiedRolePermissions = append(unifiedRolePermissions, conversions.ToPointerSlice(role.GetRolePermissions())...)
|
||||
}
|
||||
|
||||
driveRecipient := driveItemInvite.GetRecipients()[0]
|
||||
|
||||
objectId := driveRecipient.GetObjectId()
|
||||
objectID := driveRecipient.GetObjectId()
|
||||
cs3ResourcePermissions := unifiedrole.PermissionsToCS3ResourcePermissions(unifiedRolePermissions)
|
||||
|
||||
createShareRequest := &collaboration.CreateShareRequest{
|
||||
@@ -485,7 +502,7 @@ func (g Graph) Invite(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
permission := &libregraph.Permission{}
|
||||
if role := unifiedrole.CS3ResourcePermissionsToUnifiedRole(*cs3ResourcePermissions, unifiedrole.UnifiedRoleConditionGrantee, g.config.FilesSharing.EnableResharing); role != nil {
|
||||
if role := unifiedrole.CS3ResourcePermissionsToUnifiedRole(*cs3ResourcePermissions, condition, g.config.FilesSharing.EnableResharing); role != nil {
|
||||
permission.Roles = []string{role.GetId()}
|
||||
}
|
||||
|
||||
@@ -495,9 +512,9 @@ func (g Graph) Invite(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
switch driveRecipient.GetLibreGraphRecipientType() {
|
||||
case "group":
|
||||
group, err := g.identityCache.GetGroup(ctx, objectId)
|
||||
group, err := g.identityCache.GetGroup(ctx, objectID)
|
||||
if err != nil {
|
||||
g.logger.Debug().Err(err).Interface("groupId", objectId).Msg("failed group lookup")
|
||||
g.logger.Debug().Err(err).Interface("groupId", objectID).Msg("failed group lookup")
|
||||
errorcode.GeneralException.Render(w, r, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
@@ -514,9 +531,9 @@ func (g Graph) Invite(w http.ResponseWriter, r *http.Request) {
|
||||
},
|
||||
}
|
||||
default:
|
||||
user, err := g.identityCache.GetUser(ctx, objectId)
|
||||
user, err := g.identityCache.GetUser(ctx, objectID)
|
||||
if err != nil {
|
||||
g.logger.Debug().Err(err).Interface("userId", objectId).Msg("failed user lookup")
|
||||
g.logger.Debug().Err(err).Interface("userId", objectID).Msg("failed user lookup")
|
||||
errorcode.GeneralException.Render(w, r, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
@@ -588,7 +605,7 @@ func (g Graph) UpdatePermission(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
oldPermission, sharedResourceId, err := g.getPermissionByID(ctx, permissionID)
|
||||
oldPermission, sharedResourceID, err := g.getPermissionByID(ctx, permissionID)
|
||||
if err != nil {
|
||||
errorcode.RenderError(w, r, err)
|
||||
return
|
||||
@@ -596,7 +613,7 @@ func (g Graph) UpdatePermission(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// The resourceID of the shared resource need to match the item ID from the Request Path
|
||||
// otherwise this is an invalid Request.
|
||||
if !utils.ResourceIDEqual(sharedResourceId, &itemID) {
|
||||
if !utils.ResourceIDEqual(sharedResourceID, &itemID) {
|
||||
g.logger.Debug().Msg("resourceID of shared does not match itemID")
|
||||
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "permissionID and itemID do not match")
|
||||
return
|
||||
@@ -615,14 +632,13 @@ func (g Graph) UpdatePermission(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// This is a user share
|
||||
updatedPermission, err := g.updateUserShare(ctx, permissionID, oldPermission, permission)
|
||||
updatedPermission, err := g.updateUserShare(ctx, permissionID, permission)
|
||||
if err != nil {
|
||||
errorcode.RenderError(w, r, err)
|
||||
return
|
||||
}
|
||||
render.Status(r, http.StatusOK)
|
||||
render.JSON(w, r, &updatedPermission)
|
||||
return
|
||||
}
|
||||
|
||||
// DeletePermission removes a Permission from a Drive item
|
||||
@@ -643,13 +659,13 @@ func (g Graph) DeletePermission(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
isUserPermission := true
|
||||
|
||||
// Check if the id is refering to a User Share
|
||||
sharedResourceId, err := g.getUserPermissionResourceID(ctx, permissionID)
|
||||
// Check if the id is referring to a User Share
|
||||
sharedResourceID, err := g.getUserPermissionResourceID(ctx, permissionID)
|
||||
var errcode errorcode.Error
|
||||
if err != nil && errors.As(err, &errcode) && errcode.GetCode() == errorcode.ItemNotFound {
|
||||
// there is no user share with that ID, so lets check if it is referring to a public link
|
||||
isUserPermission = false
|
||||
sharedResourceId, err = g.getLinkPermissionResourceID(ctx, permissionID)
|
||||
sharedResourceID, err = g.getLinkPermissionResourceID(ctx, permissionID)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
@@ -659,7 +675,7 @@ func (g Graph) DeletePermission(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// The resourceID of the shared resource need to match the item ID from the Request Path
|
||||
// otherwise this is an invalid Request.
|
||||
if !utils.ResourceIDEqual(sharedResourceId, &itemID) {
|
||||
if !utils.ResourceIDEqual(sharedResourceID, &itemID) {
|
||||
g.logger.Debug().Msg("resourceID of shared does not match itemID")
|
||||
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "permissionID and itemID do not match")
|
||||
return
|
||||
@@ -679,7 +695,6 @@ func (g Graph) DeletePermission(w http.ResponseWriter, r *http.Request) {
|
||||
render.Status(r, http.StatusNoContent)
|
||||
render.NoContent(w, r)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (g Graph) getPermissionByID(ctx context.Context, permissionID string) (*libregraph.Permission, *storageprovider.ResourceId, error) {
|
||||
@@ -741,7 +756,7 @@ func (g Graph) getCS3UserShareByID(ctx context.Context, permissionID string) (*c
|
||||
return getShareResp.GetShare(), nil
|
||||
}
|
||||
|
||||
func (g Graph) updateUserShare(ctx context.Context, permissionID string, oldPermission, newPermission *libregraph.Permission) (*libregraph.Permission, error) {
|
||||
func (g Graph) updateUserShare(ctx context.Context, permissionID string, newPermission *libregraph.Permission) (*libregraph.Permission, error) {
|
||||
gatewayClient, err := g.gatewaySelector.Next()
|
||||
if err != nil {
|
||||
g.logger.Debug().Err(err).Msg("selecting gatewaySelector failed")
|
||||
@@ -765,8 +780,8 @@ func (g Graph) updateUserShare(ctx context.Context, permissionID string, oldPerm
|
||||
var roles, allowedResourceActions []string
|
||||
var permissionsUpdated, ok bool
|
||||
if roles, ok = newPermission.GetRolesOk(); ok && len(roles) > 0 {
|
||||
for _, roleId := range roles {
|
||||
role, err := unifiedrole.NewUnifiedRoleFromID(roleId, g.config.FilesSharing.EnableResharing)
|
||||
for _, roleID := range roles {
|
||||
role, err := unifiedrole.NewUnifiedRoleFromID(roleID, g.config.FilesSharing.EnableResharing)
|
||||
if err != nil {
|
||||
g.logger.Debug().Err(err).Interface("role", role).Msg("unable to convert requested role")
|
||||
return nil, err
|
||||
@@ -906,11 +921,11 @@ func (g Graph) getDriveItem(ctx context.Context, ref storageprovider.Reference)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if res.Status.Code != cs3rpc.Code_CODE_OK {
|
||||
if res.GetStatus().GetCode() != cs3rpc.Code_CODE_OK {
|
||||
refStr, _ := storagespace.FormatReference(&ref)
|
||||
return nil, fmt.Errorf("could not stat %s: %s", refStr, res.Status.Message)
|
||||
return nil, fmt.Errorf("could not stat %s: %s", refStr, res.GetStatus().GetMessage())
|
||||
}
|
||||
return cs3ResourceToDriveItem(g.logger, res.Info)
|
||||
return cs3ResourceToDriveItem(g.logger, res.GetInfo())
|
||||
}
|
||||
|
||||
func (g Graph) getRemoteItem(ctx context.Context, root *storageprovider.ResourceId, baseURL *url.URL) (*libregraph.RemoteItem, error) {
|
||||
@@ -926,12 +941,12 @@ func (g Graph) getRemoteItem(ctx context.Context, root *storageprovider.Resource
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if res.Status.Code != cs3rpc.Code_CODE_OK {
|
||||
if res.GetStatus().GetCode() != cs3rpc.Code_CODE_OK {
|
||||
// Only log this, there could be mountpoints which have no grant
|
||||
g.logger.Debug().Msg(res.Status.Message)
|
||||
g.logger.Debug().Msg(res.GetStatus().GetMessage())
|
||||
return nil, errors.New("could not fetch grant resource for the mountpoint")
|
||||
}
|
||||
item, err := cs3ResourceToRemoteItem(res.Info)
|
||||
item, err := cs3ResourceToRemoteItem(res.GetInfo())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -965,46 +980,46 @@ func formatDriveItems(logger *log.Logger, mds []*storageprovider.ResourceInfo) (
|
||||
}
|
||||
|
||||
func cs3TimestampToTime(t *types.Timestamp) time.Time {
|
||||
return time.Unix(int64(t.Seconds), int64(t.Nanos))
|
||||
return time.Unix(int64(t.GetSeconds()), int64(t.GetNanos()))
|
||||
}
|
||||
|
||||
func cs3ResourceToDriveItem(logger *log.Logger, res *storageprovider.ResourceInfo) (*libregraph.DriveItem, error) {
|
||||
size := new(int64)
|
||||
*size = int64(res.Size) // TODO lurking overflow: make size of libregraph drive item use uint64
|
||||
*size = int64(res.GetSize()) // TODO lurking overflow: make size of libregraph drive item use uint64
|
||||
|
||||
driveItem := &libregraph.DriveItem{
|
||||
Id: libregraph.PtrString(storagespace.FormatResourceID(*res.Id)),
|
||||
Id: libregraph.PtrString(storagespace.FormatResourceID(*res.GetId())),
|
||||
Size: size,
|
||||
}
|
||||
|
||||
if name := path.Base(res.Path); name != "" {
|
||||
if name := path.Base(res.GetPath()); name != "" {
|
||||
driveItem.Name = &name
|
||||
}
|
||||
if res.Etag != "" {
|
||||
if res.GetEtag() != "" {
|
||||
driveItem.ETag = &res.Etag
|
||||
}
|
||||
if res.Mtime != nil {
|
||||
lastModified := cs3TimestampToTime(res.Mtime)
|
||||
if res.GetMtime() != nil {
|
||||
lastModified := cs3TimestampToTime(res.GetMtime())
|
||||
driveItem.LastModifiedDateTime = &lastModified
|
||||
}
|
||||
if res.ParentId != nil {
|
||||
if res.GetParentId() != nil {
|
||||
parentRef := libregraph.NewItemReference()
|
||||
parentRef.SetDriveType(res.Space.SpaceType)
|
||||
parentRef.SetDriveId(storagespace.FormatStorageID(res.ParentId.StorageId, res.ParentId.SpaceId))
|
||||
parentRef.SetId(storagespace.FormatResourceID(*res.ParentId))
|
||||
parentRef.SetDriveType(res.GetSpace().GetSpaceType())
|
||||
parentRef.SetDriveId(storagespace.FormatStorageID(res.GetParentId().GetStorageId(), res.GetParentId().GetSpaceId()))
|
||||
parentRef.SetId(storagespace.FormatResourceID(*res.GetParentId()))
|
||||
driveItem.ParentReference = parentRef
|
||||
}
|
||||
if res.Type == storageprovider.ResourceType_RESOURCE_TYPE_FILE && res.MimeType != "" {
|
||||
if res.GetType() == storageprovider.ResourceType_RESOURCE_TYPE_FILE && res.GetMimeType() != "" {
|
||||
// We cannot use a libregraph.File here because the openapi codegenerator autodetects 'File' as a go type ...
|
||||
driveItem.File = &libregraph.OpenGraphFile{
|
||||
MimeType: &res.MimeType,
|
||||
}
|
||||
}
|
||||
if res.Type == storageprovider.ResourceType_RESOURCE_TYPE_CONTAINER {
|
||||
if res.GetType() == storageprovider.ResourceType_RESOURCE_TYPE_CONTAINER {
|
||||
driveItem.Folder = &libregraph.Folder{}
|
||||
}
|
||||
|
||||
if res.ArbitraryMetadata != nil {
|
||||
if res.GetArbitraryMetadata() != nil {
|
||||
driveItem.Audio = cs3ResourceToDriveItemAudioFacet(logger, res)
|
||||
driveItem.Location = cs3ResourceToDriveItemLocationFacet(logger, res)
|
||||
}
|
||||
@@ -1013,11 +1028,11 @@ func cs3ResourceToDriveItem(logger *log.Logger, res *storageprovider.ResourceInf
|
||||
}
|
||||
|
||||
func cs3ResourceToDriveItemAudioFacet(logger *log.Logger, res *storageprovider.ResourceInfo) *libregraph.Audio {
|
||||
if !strings.HasPrefix(res.MimeType, "audio/") {
|
||||
if !strings.HasPrefix(res.GetMimeType(), "audio/") {
|
||||
return nil
|
||||
}
|
||||
|
||||
k := res.ArbitraryMetadata.Metadata
|
||||
k := res.GetArbitraryMetadata().GetMetadata()
|
||||
if k == nil {
|
||||
return nil
|
||||
}
|
||||
@@ -1031,7 +1046,7 @@ func cs3ResourceToDriveItemAudioFacet(logger *log.Logger, res *storageprovider.R
|
||||
}
|
||||
|
||||
func cs3ResourceToDriveItemLocationFacet(logger *log.Logger, res *storageprovider.ResourceInfo) *libregraph.GeoCoordinates {
|
||||
k := res.ArbitraryMetadata.Metadata
|
||||
k := res.GetArbitraryMetadata().GetMetadata()
|
||||
if k == nil {
|
||||
return nil
|
||||
}
|
||||
@@ -1099,30 +1114,30 @@ func unmarshalStringMap(logger *log.Logger, out any, flatMap map[string]string,
|
||||
|
||||
func cs3ResourceToRemoteItem(res *storageprovider.ResourceInfo) (*libregraph.RemoteItem, error) {
|
||||
size := new(int64)
|
||||
*size = int64(res.Size) // TODO lurking overflow: make size of libregraph drive item use uint64
|
||||
*size = int64(res.GetSize()) // TODO lurking overflow: make size of libregraph drive item use uint64
|
||||
|
||||
remoteItem := &libregraph.RemoteItem{
|
||||
Id: libregraph.PtrString(storagespace.FormatResourceID(*res.Id)),
|
||||
Id: libregraph.PtrString(storagespace.FormatResourceID(*res.GetId())),
|
||||
Size: size,
|
||||
}
|
||||
|
||||
if res.GetPath() != "" {
|
||||
remoteItem.Path = libregraph.PtrString(path.Clean(res.GetPath()))
|
||||
}
|
||||
if res.Etag != "" {
|
||||
if res.GetEtag() != "" {
|
||||
remoteItem.ETag = &res.Etag
|
||||
}
|
||||
if res.Mtime != nil {
|
||||
lastModified := cs3TimestampToTime(res.Mtime)
|
||||
if res.GetMtime() != nil {
|
||||
lastModified := cs3TimestampToTime(res.GetMtime())
|
||||
remoteItem.LastModifiedDateTime = &lastModified
|
||||
}
|
||||
if res.Type == storageprovider.ResourceType_RESOURCE_TYPE_FILE && res.MimeType != "" {
|
||||
if res.GetType() == storageprovider.ResourceType_RESOURCE_TYPE_FILE && res.GetMimeType() != "" {
|
||||
// We cannot use a libregraph.File here because the openapi codegenerator autodetects 'File' as a go type ...
|
||||
remoteItem.File = &libregraph.OpenGraphFile{
|
||||
MimeType: &res.MimeType,
|
||||
}
|
||||
}
|
||||
if res.Type == storageprovider.ResourceType_RESOURCE_TYPE_CONTAINER {
|
||||
if res.GetType() == storageprovider.ResourceType_RESOURCE_TYPE_CONTAINER {
|
||||
remoteItem.Folder = &libregraph.Folder{}
|
||||
}
|
||||
if res.GetSpace() != nil && res.GetSpace().GetRoot() != nil {
|
||||
@@ -1145,10 +1160,10 @@ func (g Graph) getPathForResource(ctx context.Context, id storageprovider.Resour
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if res.Status.Code != cs3rpc.Code_CODE_OK {
|
||||
return "", fmt.Errorf("could not stat %v: %s", id, res.Status.Message)
|
||||
if res.GetStatus().GetCode() != cs3rpc.Code_CODE_OK {
|
||||
return "", fmt.Errorf("could not stat %v: %s", id, res.GetStatus().GetMessage())
|
||||
}
|
||||
return res.Path, err
|
||||
return res.GetPath(), err
|
||||
}
|
||||
|
||||
// getSpecialDriveItems reads properties from the opaque and transforms them into driveItems
|
||||
@@ -1156,20 +1171,20 @@ func (g Graph) getSpecialDriveItems(ctx context.Context, baseURL *url.URL, space
|
||||
if space.GetRoot().GetStorageId() == utils.ShareStorageProviderID {
|
||||
return nil // no point in stating the ShareStorageProvider
|
||||
}
|
||||
if space.Opaque == nil {
|
||||
if space.GetOpaque() == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
imageNode := utils.ReadPlainFromOpaque(space.Opaque, SpaceImageSpecialFolderName)
|
||||
readmeNode := utils.ReadPlainFromOpaque(space.Opaque, ReadmeSpecialFolderName)
|
||||
imageNode := utils.ReadPlainFromOpaque(space.GetOpaque(), SpaceImageSpecialFolderName)
|
||||
readmeNode := utils.ReadPlainFromOpaque(space.GetOpaque(), ReadmeSpecialFolderName)
|
||||
|
||||
cachekey := spaceRootStatKey(space.Root, imageNode, readmeNode)
|
||||
cachekey := spaceRootStatKey(space.GetRoot(), imageNode, readmeNode)
|
||||
// if the root is older or equal to our cache we can reuse the cached extended spaces properties
|
||||
if entry := g.specialDriveItemsCache.Get(cachekey); entry != nil {
|
||||
if cached, ok := entry.Value().(specialDriveItemEntry); ok {
|
||||
if cached.rootMtime != nil && space.Mtime != nil {
|
||||
if cached.rootMtime != nil && space.GetMtime() != nil {
|
||||
// beware, LaterTS does not handle equalness. it returns t1 if t1 > t2, else t2, so a >= check looks like this
|
||||
if utils.LaterTS(space.Mtime, cached.rootMtime) == cached.rootMtime {
|
||||
if utils.LaterTS(space.GetMtime(), cached.rootMtime) == cached.rootMtime {
|
||||
return cached.specialDriveItems
|
||||
}
|
||||
}
|
||||
@@ -1184,7 +1199,7 @@ func (g Graph) getSpecialDriveItems(ctx context.Context, baseURL *url.URL, space
|
||||
// cache properties
|
||||
spacePropertiesEntry := specialDriveItemEntry{
|
||||
specialDriveItems: spaceItems,
|
||||
rootMtime: space.Mtime,
|
||||
rootMtime: space.GetMtime(),
|
||||
}
|
||||
g.specialDriveItemsCache.Set(cachekey, spacePropertiesEntry, time.Duration(g.config.Spaces.ExtendedSpacePropertiesCacheTTL))
|
||||
|
||||
@@ -1222,7 +1237,7 @@ func spaceRootStatKey(id *storageprovider.ResourceId, imagenode, readmeNode stri
|
||||
_, _ = shakeHash.Write([]byte(readmeNode))
|
||||
h := make([]byte, 64)
|
||||
_, _ = shakeHash.Read(h)
|
||||
return fmt.Sprintf("%x", h)
|
||||
return hex.EncodeToString(h)
|
||||
}
|
||||
|
||||
type specialDriveItemEntry struct {
|
||||
@@ -1244,10 +1259,10 @@ func (g Graph) getSpecialDriveItem(ctx context.Context, ref storageprovider.Refe
|
||||
g.logger.Debug().Err(err).Str("ID", ref.GetResourceId().GetOpaqueId()).Str("name", itemName).Msg("Could not get item info")
|
||||
return nil
|
||||
}
|
||||
itemPath := ref.Path
|
||||
itemPath := ref.GetPath()
|
||||
if itemPath == "" {
|
||||
// lookup by id
|
||||
itemPath, err = g.getPathForResource(ctx, *ref.ResourceId)
|
||||
itemPath, err = g.getPathForResource(ctx, *ref.GetResourceId())
|
||||
if err != nil {
|
||||
g.logger.Debug().Err(err).Str("ID", ref.GetResourceId().GetOpaqueId()).Str("name", itemName).Msg("Could not get item path")
|
||||
return nil
|
||||
@@ -1255,7 +1270,7 @@ func (g Graph) getSpecialDriveItem(ctx context.Context, ref storageprovider.Refe
|
||||
}
|
||||
spaceItem.SpecialFolder = &libregraph.SpecialFolder{Name: libregraph.PtrString(itemName)}
|
||||
webdavURL := *baseURL
|
||||
webdavURL.Path = path.Join(webdavURL.Path, space.Id.OpaqueId, itemPath)
|
||||
webdavURL.Path = path.Join(webdavURL.Path, space.GetId().GetOpaqueId(), itemPath)
|
||||
spaceItem.WebDavUrl = libregraph.PtrString(webdavURL.String())
|
||||
|
||||
return spaceItem
|
||||
|
||||
@@ -685,6 +685,25 @@ var _ = Describe("Driveitems", func() {
|
||||
_, ok := res.GetRolesOk()
|
||||
Expect(ok).To(BeTrue())
|
||||
})
|
||||
It("fails to update the share permissions for a file share when setting a space specific role", func() {
|
||||
updateShareMock := gatewayClient.On("UpdateShare",
|
||||
mock.Anything,
|
||||
mock.MatchedBy(func(req *collaboration.UpdateShareRequest) bool {
|
||||
return req.GetShare().GetId().GetOpaqueId() == "permissionid"
|
||||
}),
|
||||
)
|
||||
updateShareMock.Return(updateShareMockResponse, nil)
|
||||
|
||||
driveItemPermission.SetRoles([]string{unifiedrole.NewSpaceViewerUnifiedRole().GetId()})
|
||||
body, err := driveItemPermission.MarshalJSON()
|
||||
Expect(err).To(BeNil())
|
||||
svc.UpdatePermission(
|
||||
rr,
|
||||
httptest.NewRequest(http.MethodPatch, "/", strings.NewReader(string(body))).
|
||||
WithContext(ctx),
|
||||
)
|
||||
Expect(rr.Code).To(Equal(http.StatusBadRequest))
|
||||
})
|
||||
It("updates the share permissions when changing the resource permission actions", func() {
|
||||
updateShareMock := gatewayClient.On("UpdateShare",
|
||||
mock.Anything,
|
||||
@@ -1007,6 +1026,17 @@ var _ = Describe("Driveitems", func() {
|
||||
Expect(jsonData.Get("0.roles.0").String()).To(Equal(unifiedrole.NewViewerUnifiedRole(true).GetId()))
|
||||
})
|
||||
|
||||
It("fails with wrong role", func() {
|
||||
driveItemInvite.Roles = []string{unifiedrole.NewCoownerUnifiedRole().GetId()}
|
||||
svc.Invite(
|
||||
rr,
|
||||
httptest.NewRequest(http.MethodPost, "/", toJSONReader(driveItemInvite)).
|
||||
WithContext(ctx),
|
||||
)
|
||||
|
||||
Expect(rr.Code).To(Equal(http.StatusBadRequest))
|
||||
})
|
||||
|
||||
It("with actions (happy path)", func() {
|
||||
driveItemInvite.Roles = nil
|
||||
driveItemInvite.LibreGraphPermissionsActions = []string{unifiedrole.DriveItemContentRead}
|
||||
|
||||
@@ -622,6 +622,10 @@ func (g Graph) UpdateDrive(w http.ResponseWriter, r *http.Request) {
|
||||
logger.Debug().Interface("id", rid).Msg("could not update drive, invalid argument")
|
||||
errorcode.NotAllowed.Render(w, r, http.StatusBadRequest, resp.GetStatus().GetMessage())
|
||||
return
|
||||
case cs3rpc.Code_CODE_UNIMPLEMENTED:
|
||||
logger.Debug().Interface("id", rid).Msg("could not delete drive: delete not implemented for this type of drive")
|
||||
errorcode.NotAllowed.Render(w, r, http.StatusMethodNotAllowed, "drive cannot be updated")
|
||||
return
|
||||
default:
|
||||
logger.Debug().Interface("id", rid).Str("grpc", resp.GetStatus().GetMessage()).Msg("could not update drive: grpc error")
|
||||
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, "grpc error")
|
||||
@@ -1193,6 +1197,10 @@ func (g Graph) DeleteDrive(w http.ResponseWriter, r *http.Request) {
|
||||
logger.Debug().Interface("id", rid).Msg("could not delete drive: drive not found")
|
||||
errorcode.ItemNotFound.Render(w, r, http.StatusNotFound, "drive not found")
|
||||
return
|
||||
case cs3rpc.Code_CODE_UNIMPLEMENTED:
|
||||
logger.Debug().Interface("id", rid).Msg("could not delete drive: delete not implemented for this type of drive")
|
||||
errorcode.NotAllowed.Render(w, r, http.StatusMethodNotAllowed, "drive cannot be deleted")
|
||||
return
|
||||
// don't expose internal error codes to the outside world
|
||||
default:
|
||||
logger.Debug().Str("grpc", dRes.GetStatus().GetMessage()).Interface("id", rid).Msg("could not delete drive: grpc error")
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"reflect"
|
||||
"slices"
|
||||
|
||||
cs3User "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
|
||||
collaboration "github.com/cs3org/go-cs3apis/cs3/sharing/collaboration/v1beta1"
|
||||
storageprovider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
|
||||
"github.com/cs3org/reva/v2/pkg/utils"
|
||||
@@ -183,55 +184,36 @@ func (g Graph) listSharedWithMe(ctx context.Context) ([]libregraph.DriveItem, er
|
||||
}
|
||||
|
||||
if userID := shareStat.GetInfo().GetOwner(); userID != nil {
|
||||
user, err := g.identityCache.GetUser(ctx, userID.GetOpaqueId())
|
||||
identity, err := g.cs3UserIdToIdentity(ctx, userID)
|
||||
if err != nil {
|
||||
g.logger.Error().Err(err).Msg("could not get user")
|
||||
return err
|
||||
// TODO: define a proper error behavior here. We don't
|
||||
// want the whole request to fail just because a single
|
||||
// resource owner couldn't be resolved. But, should be
|
||||
// really return the affect share in the response?
|
||||
// For now we just log a warning. The returned
|
||||
// identitySet will just contain the userid.
|
||||
g.logger.Warn().Err(err).Str("userid", userID.String()).Msg("could not get owner of shared resource")
|
||||
}
|
||||
|
||||
identitySet := libregraph.IdentitySet{
|
||||
User: &libregraph.Identity{
|
||||
DisplayName: user.GetDisplayName(),
|
||||
Id: libregraph.PtrString(user.GetId()),
|
||||
},
|
||||
}
|
||||
|
||||
remoteItem.SetCreatedBy(identitySet)
|
||||
driveItem.SetCreatedBy(identitySet)
|
||||
remoteItem.SetCreatedBy(libregraph.IdentitySet{User: &identity})
|
||||
driveItem.SetCreatedBy(libregraph.IdentitySet{User: &identity})
|
||||
}
|
||||
|
||||
if userID := receivedShare.GetShare().GetOwner(); userID != nil {
|
||||
user, err := g.identityCache.GetUser(ctx, userID.GetOpaqueId())
|
||||
identity, err := g.cs3UserIdToIdentity(ctx, userID)
|
||||
if err != nil {
|
||||
g.logger.Error().Err(err).Msg("could not get user")
|
||||
return err
|
||||
g.logger.Warn().Err(err).Str("userid", userID.String()).Msg("could not get owner of the share")
|
||||
}
|
||||
|
||||
identitySet := libregraph.IdentitySet{
|
||||
User: &libregraph.Identity{
|
||||
DisplayName: user.GetDisplayName(),
|
||||
Id: libregraph.PtrString(user.GetId()),
|
||||
},
|
||||
}
|
||||
|
||||
shared.SetOwner(identitySet)
|
||||
shared.SetOwner(libregraph.IdentitySet{User: &identity})
|
||||
}
|
||||
|
||||
if userID := receivedShare.GetShare().GetCreator(); userID != nil {
|
||||
user, err := g.identityCache.GetUser(ctx, userID.GetOpaqueId())
|
||||
identity, err := g.cs3UserIdToIdentity(ctx, userID)
|
||||
if err != nil {
|
||||
g.logger.Error().Err(err).Msg("could not get user")
|
||||
return err
|
||||
g.logger.Warn().Err(err).Str("userid", userID.String()).Msg("could not get creator of the share")
|
||||
}
|
||||
|
||||
identitySet := libregraph.IdentitySet{
|
||||
User: &libregraph.Identity{
|
||||
DisplayName: user.GetDisplayName(),
|
||||
Id: libregraph.PtrString(user.GetId()),
|
||||
},
|
||||
}
|
||||
|
||||
shared.SetSharedBy(identitySet)
|
||||
shared.SetSharedBy(libregraph.IdentitySet{User: &identity})
|
||||
|
||||
}
|
||||
|
||||
@@ -344,3 +326,18 @@ func (g Graph) cs3ReceivedShareToLibreGraphPermissions(ctx context.Context, rece
|
||||
|
||||
return permission, nil
|
||||
}
|
||||
|
||||
func (g Graph) cs3UserIdToIdentity(ctx context.Context, cs3UserID *cs3User.UserId) (libregraph.Identity, error) {
|
||||
identity := libregraph.Identity{
|
||||
Id: libregraph.PtrString(cs3UserID.GetOpaqueId()),
|
||||
}
|
||||
var err error
|
||||
if cs3UserID.GetType() != cs3User.UserType_USER_TYPE_SPACE_OWNER {
|
||||
var user libregraph.User
|
||||
user, err = g.identityCache.GetUser(ctx, cs3UserID.GetOpaqueId())
|
||||
if err == nil {
|
||||
identity.SetDisplayName(user.GetDisplayName())
|
||||
}
|
||||
}
|
||||
return identity, err
|
||||
}
|
||||
|
||||
@@ -393,5 +393,35 @@ var _ = Describe("SharedWithMe", func() {
|
||||
Expect(jsonData.Get("user.displayName").String()).To(Equal(shareCreator.DisplayName))
|
||||
Expect(jsonData.Get("user.id").String()).To(Equal(shareCreator.Id.OpaqueId))
|
||||
})
|
||||
|
||||
It("returns shares created on project space", func() {
|
||||
shareCreator := getUserResponseDefault.User
|
||||
|
||||
ownerID := &userv1beta1.UserId{
|
||||
OpaqueId: "project-space-id",
|
||||
Type: userv1beta1.UserType_USER_TYPE_SPACE_OWNER,
|
||||
}
|
||||
share := listReceivedSharesResponse.Shares[0].Share
|
||||
share.Creator = shareCreator.Id
|
||||
share.Owner = ownerID
|
||||
resourceInfo := statResponse.Info
|
||||
resourceInfo.Owner = ownerID
|
||||
|
||||
svc.ListSharedWithMe(
|
||||
tape,
|
||||
httptest.NewRequest(http.MethodGet, "/graph/v1beta1/me/drive/sharedWithMe", nil),
|
||||
)
|
||||
|
||||
jsonData := gjson.Get(tape.Body.String(), "value.0.createdBy")
|
||||
|
||||
Expect(jsonData.Get("user.displayName").String()).To(Equal(""))
|
||||
Expect(jsonData.Get("user.id").String()).To(Equal(ownerID.OpaqueId))
|
||||
|
||||
jsonData = gjson.Get(tape.Body.String(), "value.0.remoteItem.shared")
|
||||
Expect(jsonData.Get("sharedBy.user.displayName").String()).To(Equal(shareCreator.DisplayName))
|
||||
Expect(jsonData.Get("sharedBy.user.id").String()).To(Equal(shareCreator.Id.OpaqueId))
|
||||
Expect(jsonData.Get("owner.user.displayName").String()).To(Equal(""))
|
||||
Expect(jsonData.Get("owner.user.id").String()).To(Equal(ownerID.OpaqueId))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -153,7 +153,7 @@ func NewCoownerUnifiedRole() *libregraph.UnifiedRoleDefinition {
|
||||
RolePermissions: []libregraph.UnifiedRolePermission{
|
||||
{
|
||||
AllowedResourceActions: convert(r),
|
||||
Condition: proto.String(UnifiedRoleConditionGrantee),
|
||||
Condition: proto.String(UnifiedRoleConditionOwner),
|
||||
},
|
||||
},
|
||||
LibreGraphWeight: proto.Int32(0),
|
||||
@@ -185,10 +185,6 @@ func NewManagerUnifiedRole() *libregraph.UnifiedRoleDefinition {
|
||||
Description: proto.String("Grants manager permissions on a resource. Semantically equivalent to co-owner"),
|
||||
DisplayName: displayName(r),
|
||||
RolePermissions: []libregraph.UnifiedRolePermission{
|
||||
{
|
||||
AllowedResourceActions: convert(r),
|
||||
Condition: proto.String(UnifiedRoleConditionGrantee),
|
||||
},
|
||||
{
|
||||
AllowedResourceActions: convert(r),
|
||||
Condition: proto.String(UnifiedRoleConditionOwner),
|
||||
|
||||
@@ -27,8 +27,7 @@ var _ = Describe("unifiedroles", func() {
|
||||
Entry(rConversions.RoleViewer, rConversions.NewViewerRole(true), unifiedrole.NewViewerUnifiedRole(true), unifiedrole.UnifiedRoleConditionGrantee),
|
||||
Entry(rConversions.RoleEditor, rConversions.NewEditorRole(true), unifiedrole.NewEditorUnifiedRole(true), unifiedrole.UnifiedRoleConditionGrantee),
|
||||
Entry(rConversions.RoleFileEditor, rConversions.NewFileEditorRole(true), unifiedrole.NewFileEditorUnifiedRole(true), unifiedrole.UnifiedRoleConditionGrantee),
|
||||
Entry(rConversions.RoleCoowner, rConversions.NewCoownerRole(), unifiedrole.NewCoownerUnifiedRole(), unifiedrole.UnifiedRoleConditionGrantee),
|
||||
Entry(rConversions.RoleManager, rConversions.NewManagerRole(), unifiedrole.NewManagerUnifiedRole(), unifiedrole.UnifiedRoleConditionGrantee),
|
||||
Entry(rConversions.RoleCoowner, rConversions.NewCoownerRole(), unifiedrole.NewCoownerUnifiedRole(), unifiedrole.UnifiedRoleConditionOwner),
|
||||
Entry(rConversions.RoleManager, rConversions.NewManagerRole(), unifiedrole.NewManagerUnifiedRole(), unifiedrole.UnifiedRoleConditionOwner),
|
||||
Entry(rConversions.RoleSpaceViewer, rConversions.NewSpaceViewerRole(), unifiedrole.NewSpaceViewerUnifiedRole(), unifiedrole.UnifiedRoleConditionOwner),
|
||||
Entry(rConversions.RoleSpaceEditor, rConversions.NewSpaceEditorRole(), unifiedrole.NewSpaceEditorUnifiedRole(), unifiedrole.UnifiedRoleConditionOwner),
|
||||
@@ -208,6 +207,17 @@ var _ = Describe("unifiedroles", func() {
|
||||
unifiedrole.NewViewerUnifiedRole(false),
|
||||
unifiedrole.NewFileEditorUnifiedRole(false),
|
||||
unifiedrole.NewEditorUnifiedRole(false),
|
||||
},
|
||||
),
|
||||
|
||||
Entry(
|
||||
"GetBuiltinRoleDefinitionList",
|
||||
rolesToAction(unifiedrole.GetBuiltinRoleDefinitionList(false)...),
|
||||
unifiedrole.UnifiedRoleConditionOwner,
|
||||
false,
|
||||
[]*libregraph.UnifiedRoleDefinition{
|
||||
unifiedrole.NewSpaceViewerUnifiedRole(),
|
||||
unifiedrole.NewSpaceEditorUnifiedRole(),
|
||||
unifiedrole.NewCoownerUnifiedRole(),
|
||||
unifiedrole.NewManagerUnifiedRole(),
|
||||
},
|
||||
@@ -223,8 +233,6 @@ var _ = Describe("unifiedroles", func() {
|
||||
unifiedrole.NewViewerUnifiedRole(true),
|
||||
unifiedrole.NewFileEditorUnifiedRole(true),
|
||||
unifiedrole.NewEditorUnifiedRole(true),
|
||||
unifiedrole.NewCoownerUnifiedRole(),
|
||||
unifiedrole.NewManagerUnifiedRole(),
|
||||
},
|
||||
),
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ func Server(cfg *config.Config) *cli.Command {
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
logger := logging.Configure(cfg.Service.Name, cfg.Log)
|
||||
tracingProvider, err := tracing.GetServiceTraceProvider(cfg.Tracing, cfg.Service.Name)
|
||||
traceProvider, err := tracing.GetServiceTraceProvider(cfg.Tracing, cfg.Service.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -63,7 +63,7 @@ func Server(cfg *config.Config) *cli.Command {
|
||||
runtime.RunWithOptions(rCfg, pidFile,
|
||||
runtime.WithLogger(&logger.Logger),
|
||||
runtime.WithRegistry(reg),
|
||||
runtime.WithTraceProvider(tracingProvider),
|
||||
runtime.WithTraceProvider(traceProvider),
|
||||
)
|
||||
|
||||
return nil
|
||||
|
||||
@@ -13,7 +13,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2023-12-30 00:38+0000\n"
|
||||
"POT-Creation-Date: 2024-01-19 00:48+0000\n"
|
||||
"PO-Revision-Date: 2023-04-19 11:11+0000\n"
|
||||
"Last-Translator: Michael Barz <mbarz@owncloud.com>, 2023\n"
|
||||
"Language-Team: German (https://app.transifex.com/owncloud-org/teams/6149/de/)\n"
|
||||
|
||||
@@ -12,7 +12,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2023-12-30 00:38+0000\n"
|
||||
"POT-Creation-Date: 2024-01-19 00:48+0000\n"
|
||||
"PO-Revision-Date: 2023-04-19 11:11+0000\n"
|
||||
"Last-Translator: Michael Barz <mbarz@owncloud.com>, 2023\n"
|
||||
"Language-Team: German (Germany) (https://app.transifex.com/owncloud-org/teams/6149/de_DE/)\n"
|
||||
|
||||
@@ -11,7 +11,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2023-12-30 00:38+0000\n"
|
||||
"POT-Creation-Date: 2024-01-19 00:48+0000\n"
|
||||
"PO-Revision-Date: 2023-04-19 11:11+0000\n"
|
||||
"Last-Translator: Shouyuan, 2023\n"
|
||||
"Language-Team: Chinese Simplified (https://app.transifex.com/owncloud-org/teams/6149/zh-Hans/)\n"
|
||||
|
||||
@@ -11,7 +11,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2023-12-30 00:38+0000\n"
|
||||
"POT-Creation-Date: 2024-01-19 00:48+0000\n"
|
||||
"PO-Revision-Date: 2023-04-19 11:11+0000\n"
|
||||
"Last-Translator: Shouyuan, 2023\n"
|
||||
"Language-Team: Chinese (China) (https://app.transifex.com/owncloud-org/teams/6149/zh_CN/)\n"
|
||||
|
||||
@@ -30,7 +30,7 @@ func Server(cfg *config.Config) *cli.Command {
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
logger := logging.Configure(cfg.Service.Name, cfg.Log)
|
||||
tracingProvider, err := tracing.GetServiceTraceProvider(cfg.Tracing, cfg.Service.Name)
|
||||
traceProvider, err := tracing.GetServiceTraceProvider(cfg.Tracing, cfg.Service.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -86,7 +86,7 @@ func Server(cfg *config.Config) *cli.Command {
|
||||
ocdav.MetricsEnabled(true),
|
||||
ocdav.MetricsNamespace("ocis"),
|
||||
ocdav.Tracing("Adding these strings is a workaround for ->", "https://github.com/cs3org/reva/issues/4131"),
|
||||
ocdav.WithTraceProvider(tracingProvider),
|
||||
ocdav.WithTraceProvider(traceProvider),
|
||||
}
|
||||
|
||||
s, err := ocdav.Service(opts...)
|
||||
|
||||
@@ -34,11 +34,6 @@ type GRPC struct {
|
||||
TLS *shared.GRPCServiceTLS `yaml:"tls"`
|
||||
}
|
||||
|
||||
// TokenManager is the config for using the reva token manager
|
||||
type TokenManager struct {
|
||||
JWTSecret string `yaml:"jwt_secret" env:"OCIS_JWT_SECRET;POLICIES_JWT_SECRET" desc:"The secret to mint and validate jwt tokens."`
|
||||
}
|
||||
|
||||
// Engine configures the policy engine.
|
||||
type Engine struct {
|
||||
Timeout time.Duration `yaml:"timeout" env:"POLICIES_ENGINE_TIMEOUT" desc:"Sets the timeout the rego expression evaluation can take. Rules default to deny if the timeout was reached. See the Environment Variable Types description for more details."`
|
||||
|
||||
@@ -182,7 +182,7 @@ The following metrics are exposed by the proxy service:
|
||||
| `ocis_proxy_build_info{version}` | A metric with a constant `1` value labeled by version, exposing the version of the ocis proxy service. | `version`: Build version of the proxy |
|
||||
|
||||
### Prometheus Configuration
|
||||
The following is an example prometheus configuration for the single process mode. It assumes that the proxy service is configured to bind on all interfaces `PROXY_HTTP_ADDR=0.0.0.0:9205` and that the proxy is available via the `ocis` service name (typically in docker-compose). The prometheus service detects the `/metrics` endpoint automatically and scrapes it every 15 seconds.
|
||||
The following is an example prometheus configuration for the single process mode. It assumes that the proxy debug address is configured to bind on all interfaces `PROXY_DEBUG_ADDR=0.0.0.0:9205` and that the proxy is available via the `ocis` service name (typically in docker-compose). The prometheus service detects the `/metrics` endpoint automatically and scrapes it every 15 seconds.
|
||||
|
||||
```yaml
|
||||
global:
|
||||
|
||||
@@ -32,9 +32,9 @@ func ResolveReference(ctx context.Context, ref *provider.Reference, ri *provider
|
||||
}
|
||||
|
||||
gpRes, err := gatewayClient.GetPath(ctx, &provider.GetPathRequest{
|
||||
ResourceId: ri.Id,
|
||||
ResourceId: ri.GetId(),
|
||||
})
|
||||
if err != nil || gpRes.Status.Code != rpc.Code_CODE_OK {
|
||||
if err != nil || gpRes.GetStatus().GetCode() != rpc.Code_CODE_OK {
|
||||
return nil, err
|
||||
}
|
||||
return &provider.Reference{
|
||||
@@ -43,7 +43,7 @@ func ResolveReference(ctx context.Context, ref *provider.Reference, ri *provider
|
||||
SpaceId: ref.GetResourceId().GetSpaceId(),
|
||||
OpaqueId: ref.GetResourceId().GetSpaceId(),
|
||||
},
|
||||
Path: utils.MakeRelativePath(gpRes.Path),
|
||||
Path: utils.MakeRelativePath(gpRes.GetPath()),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ func (ma matchArray) Swap(i, j int) {
|
||||
ma[i], ma[j] = ma[j], ma[i]
|
||||
}
|
||||
func (ma matchArray) Less(i, j int) bool {
|
||||
return ma[i].Score > ma[j].Score
|
||||
return ma[i].GetScore() > ma[j].GetScore()
|
||||
}
|
||||
|
||||
func logDocCount(engine engine.Engine, logger log.Logger) {
|
||||
@@ -88,7 +88,7 @@ func statResource(ctx context.Context, ref *provider.Reference, gatewaySelector
|
||||
logger.Error().Err(err).Msg("failed to stat the moved resource")
|
||||
return nil, err
|
||||
}
|
||||
switch res.Status.Code {
|
||||
switch res.GetStatus().GetCode() {
|
||||
case rpc.Code_CODE_OK:
|
||||
return res, nil
|
||||
case rpc.Code_CODE_NOT_FOUND:
|
||||
@@ -111,34 +111,34 @@ func convertToWebDAVPermissions(isShared, isMountpoint, isDir bool, p *provider.
|
||||
if isShared {
|
||||
fmt.Fprintf(&b, "S")
|
||||
}
|
||||
if p.ListContainer &&
|
||||
p.ListFileVersions &&
|
||||
p.ListRecycle &&
|
||||
p.Stat &&
|
||||
p.GetPath &&
|
||||
p.GetQuota &&
|
||||
p.InitiateFileDownload {
|
||||
if p.GetListContainer() &&
|
||||
p.GetListFileVersions() &&
|
||||
p.GetListRecycle() &&
|
||||
p.GetStat() &&
|
||||
p.GetGetPath() &&
|
||||
p.GetGetQuota() &&
|
||||
p.GetInitiateFileDownload() {
|
||||
fmt.Fprintf(&b, "R")
|
||||
}
|
||||
if isMountpoint {
|
||||
fmt.Fprintf(&b, "M")
|
||||
}
|
||||
if p.Delete {
|
||||
if p.GetDelete() {
|
||||
fmt.Fprintf(&b, "D")
|
||||
}
|
||||
if p.InitiateFileUpload &&
|
||||
p.RestoreFileVersion &&
|
||||
p.RestoreRecycleItem {
|
||||
if p.GetInitiateFileUpload() &&
|
||||
p.GetRestoreFileVersion() &&
|
||||
p.GetRestoreRecycleItem() {
|
||||
fmt.Fprintf(&b, "NV")
|
||||
if !isDir {
|
||||
fmt.Fprintf(&b, "W")
|
||||
}
|
||||
}
|
||||
if isDir &&
|
||||
p.ListContainer &&
|
||||
p.Stat &&
|
||||
p.CreateContainer &&
|
||||
p.InitiateFileUpload {
|
||||
p.GetListContainer() &&
|
||||
p.GetStat() &&
|
||||
p.GetCreateContainer() &&
|
||||
p.GetInitiateFileUpload() {
|
||||
fmt.Fprintf(&b, "CK")
|
||||
}
|
||||
return b.String()
|
||||
|
||||
@@ -32,12 +32,12 @@ func Server(cfg *config.Config) *cli.Command {
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
logger := logging.Configure(cfg.Service.Name, cfg.Log)
|
||||
tracingProvider, err := tracing.GetServiceTraceProvider(cfg.Tracing, cfg.Service.Name)
|
||||
traceProvider, err := tracing.GetServiceTraceProvider(cfg.Tracing, cfg.Service.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cfg.GrpcClient, err = ogrpc.NewClient(
|
||||
append(ogrpc.GetClientOptions(cfg.GRPCClientTLS), ogrpc.WithTraceProvider(tracingProvider))...,
|
||||
append(ogrpc.GetClientOptions(cfg.GRPCClientTLS), ogrpc.WithTraceProvider(traceProvider))...,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -65,7 +65,7 @@ func Server(cfg *config.Config) *cli.Command {
|
||||
http.Config(cfg),
|
||||
http.Metrics(mtrcs),
|
||||
http.ServiceHandler(handle),
|
||||
http.TraceProvider(tracingProvider),
|
||||
http.TraceProvider(traceProvider),
|
||||
)
|
||||
if err != nil {
|
||||
logger.Error().
|
||||
@@ -87,7 +87,7 @@ func Server(cfg *config.Config) *cli.Command {
|
||||
grpc.Config(cfg),
|
||||
grpc.Metrics(mtrcs),
|
||||
grpc.ServiceHandler(handle),
|
||||
grpc.TraceProvider(tracingProvider),
|
||||
grpc.TraceProvider(traceProvider),
|
||||
)
|
||||
servers.Add(grpcServer.Run, func(_ error) {
|
||||
logger.Info().Str("server", "grpc").Msg("Shutting down server")
|
||||
|
||||
@@ -34,7 +34,7 @@ func Server(cfg *config.Config) *cli.Command {
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
logger := logging.Configure(cfg.Service.Name, cfg.Log)
|
||||
tracingProvider, err := tracing.GetServiceTraceProvider(cfg.Tracing, cfg.Service.Name)
|
||||
traceProvider, err := tracing.GetServiceTraceProvider(cfg.Tracing, cfg.Service.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -66,7 +66,7 @@ func Server(cfg *config.Config) *cli.Command {
|
||||
runtime.RunWithOptions(rCfg, pidFile,
|
||||
runtime.WithLogger(&logger.Logger),
|
||||
runtime.WithRegistry(reg),
|
||||
runtime.WithTraceProvider(tracingProvider),
|
||||
runtime.WithTraceProvider(traceProvider),
|
||||
)
|
||||
|
||||
return nil
|
||||
|
||||
@@ -153,6 +153,8 @@ type Events struct {
|
||||
TLSInsecure bool `yaml:"tls_insecure" env:"OCIS_INSECURE;SHARING_EVENTS_TLS_INSECURE" desc:"Whether to verify the server TLS certificates."`
|
||||
TLSRootCaCertPath string `yaml:"tls_root_ca_cert_path" env:"OCIS_EVENTS_TLS_ROOT_CA_CERTIFICATE;SHARING_EVENTS_TLS_ROOT_CA_CERTIFICATE" desc:"The root CA certificate used to validate the server's TLS certificate. If provided SHARING_EVENTS_TLS_INSECURE will be seen as false."`
|
||||
EnableTLS bool `yaml:"enable_tls" env:"OCIS_EVENTS_ENABLE_TLS;SHARING_EVENTS_ENABLE_TLS" desc:"Enable TLS for the connection to the events broker. The events broker is the ocis service which receives and delivers events between the services.."`
|
||||
AuthUsername string `yaml:"auth_username" env:"OCIS_EVENTS_AUTH_USERNAME;SHARING_EVENTS_AUTH_USERNAME" desc:"Username for the events broker."`
|
||||
AuthPassword string `yaml:"auth_password" env:"OCIS_EVENTS_AUTH_PASSWORD;SHARING_EVENTS_AUTH_PASSWORD" desc:"Password for the events broker."`
|
||||
}
|
||||
|
||||
// PasswordPolicy configures reva password policy
|
||||
|
||||
@@ -14,15 +14,10 @@ import (
|
||||
|
||||
// SharingConfigFromStruct will adapt an oCIS config struct into a reva mapstructure to start a reva service.
|
||||
func SharingConfigFromStruct(cfg *config.Config, logger log.Logger) (map[string]interface{}, error) {
|
||||
var bannedPasswordsList map[string]struct{}
|
||||
var err error
|
||||
if cfg.PasswordPolicy.BannedPasswordsList != "" {
|
||||
bannedPasswordsList, err = readMultilineFile(cfg.PasswordPolicy.BannedPasswordsList)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("failed to load the banned passwords from a file %s: %w", cfg.PasswordPolicy.BannedPasswordsList, err)
|
||||
logger.Err(err).Send()
|
||||
return nil, err
|
||||
}
|
||||
passwordPolicyCfg, err := passwordPolicyConfig(cfg)
|
||||
if err != nil {
|
||||
logger.Err(err).Send()
|
||||
return nil, err
|
||||
}
|
||||
rcfg := map[string]interface{}{
|
||||
"shared": map[string]interface{}{
|
||||
@@ -86,6 +81,8 @@ func SharingConfigFromStruct(cfg *config.Config, logger log.Logger) (map[string]
|
||||
"natsclusterid": cfg.Events.ClusterID,
|
||||
"tlsinsecure": cfg.Events.TLSInsecure,
|
||||
"tlsrootcacertificate": cfg.Events.TLSRootCaCertPath,
|
||||
"authusername": cfg.Events.AuthUsername,
|
||||
"authpassword": cfg.Events.AuthPassword,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -94,16 +91,8 @@ func SharingConfigFromStruct(cfg *config.Config, logger log.Logger) (map[string]
|
||||
"gateway_addr": cfg.Reva.Address,
|
||||
"writeable_share_must_have_password": cfg.WriteableShareMustHavePassword,
|
||||
"public_share_must_have_password": cfg.PublicShareMustHavePassword,
|
||||
"password_policy": map[string]interface{}{
|
||||
"disabled": cfg.PasswordPolicy.Disabled,
|
||||
"min_digits": cfg.PasswordPolicy.MinDigits,
|
||||
"min_characters": cfg.PasswordPolicy.MinCharacters,
|
||||
"min_lowercase_characters": cfg.PasswordPolicy.MinLowerCaseCharacters,
|
||||
"min_uppercase_characters": cfg.PasswordPolicy.MinUpperCaseCharacters,
|
||||
"min_special_characters": cfg.PasswordPolicy.MinSpecialCharacters,
|
||||
"banned_passwords_list": bannedPasswordsList,
|
||||
},
|
||||
"driver": cfg.PublicSharingDriver,
|
||||
"password_policy": passwordPolicyCfg,
|
||||
"driver": cfg.PublicSharingDriver,
|
||||
"drivers": map[string]interface{}{
|
||||
"json": map[string]interface{}{
|
||||
"file": cfg.PublicSharingDrivers.JSON.File,
|
||||
@@ -147,6 +136,8 @@ func SharingConfigFromStruct(cfg *config.Config, logger log.Logger) (map[string]
|
||||
"tls-root-ca-cert": cfg.Events.TLSRootCaCertPath,
|
||||
"enable-tls": cfg.Events.EnableTLS,
|
||||
"name": "sharing-eventsmiddleware",
|
||||
"username": cfg.Events.AuthUsername,
|
||||
"password": cfg.Events.AuthPassword,
|
||||
},
|
||||
"prometheus": map[string]interface{}{
|
||||
"namespace": "ocis",
|
||||
@@ -185,3 +176,30 @@ func fileExists(path string) bool {
|
||||
}
|
||||
return !info.IsDir()
|
||||
}
|
||||
|
||||
func passwordPolicyConfig(cfg *config.Config) (map[string]interface{}, error) {
|
||||
_maxCharacters := 72
|
||||
if cfg.PasswordPolicy.Disabled {
|
||||
return map[string]interface{}{
|
||||
"max_characters": _maxCharacters,
|
||||
"banned_passwords_list": nil,
|
||||
}, nil
|
||||
}
|
||||
var bannedPasswordsList map[string]struct{}
|
||||
var err error
|
||||
if cfg.PasswordPolicy.BannedPasswordsList != "" {
|
||||
bannedPasswordsList, err = readMultilineFile(cfg.PasswordPolicy.BannedPasswordsList)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to load the banned passwords from a file %s: %w", cfg.PasswordPolicy.BannedPasswordsList, err)
|
||||
}
|
||||
}
|
||||
return map[string]interface{}{
|
||||
"max_characters": _maxCharacters,
|
||||
"min_digits": cfg.PasswordPolicy.MinDigits,
|
||||
"min_characters": cfg.PasswordPolicy.MinCharacters,
|
||||
"min_lowercase_characters": cfg.PasswordPolicy.MinLowerCaseCharacters,
|
||||
"min_uppercase_characters": cfg.PasswordPolicy.MinUpperCaseCharacters,
|
||||
"min_special_characters": cfg.PasswordPolicy.MinSpecialCharacters,
|
||||
"banned_passwords_list": bannedPasswordsList,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ func Server(cfg *config.Config) *cli.Command {
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
logger := logging.Configure(cfg.Service.Name, cfg.Log)
|
||||
tracingProvider, err := tracing.GetServiceTraceProvider(cfg.Tracing, cfg.Service.Name)
|
||||
traceProvider, err := tracing.GetServiceTraceProvider(cfg.Tracing, cfg.Service.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -50,7 +50,7 @@ func Server(cfg *config.Config) *cli.Command {
|
||||
runtime.RunWithOptions(rCfg, pidFile,
|
||||
runtime.WithLogger(&logger.Logger),
|
||||
runtime.WithRegistry(reg),
|
||||
runtime.WithTraceProvider(tracingProvider),
|
||||
runtime.WithTraceProvider(traceProvider),
|
||||
)
|
||||
|
||||
return nil
|
||||
|
||||
@@ -93,4 +93,6 @@ type Cache struct {
|
||||
TTL time.Duration `yaml:"ttl" env:"OCIS_CACHE_TTL;STORAGE_SYSTEM_CACHE_TTL" desc:"Default time to live for user info in the user info cache. Only applied when access tokens has no expiration. See the Environment Variable Types description for more details."`
|
||||
Size int `yaml:"size" env:"OCIS_CACHE_SIZE;STORAGE_SYSTEM_CACHE_SIZE" desc:"The maximum quantity of items in the user info cache. Only applies when store type 'ocmem' is configured. Defaults to 512 which is derived from the ocmem package though not exclicitely set as default."`
|
||||
DisablePersistence bool `yaml:"disable_persistence" env:"OCIS_CACHE_DISABLE_PERSISTENCE;STORAGE_SYSTEM_CACHE_DISABLE_PERSISTENCE" desc:"Disables persistence of the cache. Only applies when store type 'nats-js-kv' is configured. Defaults to false."`
|
||||
AuthUsername string `yaml:"auth_username" env:"OCIS_CACHE_AUTH_USERNAME;STORAGE_SYSTEM_CACHE_AUTH_USERNAME" desc:"Username for the configured store. Only applies when store type 'nats-js-kv' is configured."`
|
||||
AuthPassword string `yaml:"auth_password" env:"OCIS_CACHE_AUTH_PASSWORD;STORAGE_SYSTEM_CACHE_AUTH_PASSWORD" desc:"Password for the configured store. Only applies when store type 'nats-js-kv' is configured."`
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
package revaconfig
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
|
||||
"github.com/owncloud/ocis/v2/services/storage-system/pkg/config"
|
||||
)
|
||||
@@ -165,9 +163,11 @@ func metadataDrivers(cfg *config.Config) map[string]interface{} {
|
||||
"cache_store": cfg.FileMetadataCache.Store,
|
||||
"cache_nodes": cfg.FileMetadataCache.Nodes,
|
||||
"cache_database": cfg.FileMetadataCache.Database,
|
||||
"cache_ttl": cfg.FileMetadataCache.TTL / time.Second,
|
||||
"cache_ttl": cfg.FileMetadataCache.TTL,
|
||||
"cache_size": cfg.FileMetadataCache.Size,
|
||||
"cache_disable_persistence": cfg.FileMetadataCache.DisablePersistence,
|
||||
"cache_auth_username": cfg.FileMetadataCache.AuthUsername,
|
||||
"cache_auth_password": cfg.FileMetadataCache.AuthPassword,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -31,7 +31,6 @@ type Config struct {
|
||||
|
||||
TransferExpires int64 `yaml:"transfer_expires" env:"STORAGE_USERS_TRANSFER_EXPIRES" desc:"the time after which the token for upload postprocessing expires"`
|
||||
Events Events `yaml:"events"`
|
||||
StatCache StatCache `yaml:"stat_cache"`
|
||||
FilemetadataCache FilemetadataCache `yaml:"filemetadata_cache"`
|
||||
IDCache IDCache `yaml:"id_cache"`
|
||||
MountID string `yaml:"mount_id" env:"STORAGE_USERS_MOUNT_ID" desc:"Mount ID of this storage."`
|
||||
@@ -182,16 +181,6 @@ type Events struct {
|
||||
AuthPassword string `yaml:"password" env:"OCIS_EVENTS_AUTH_PASSWORD;STORAGE_USERS_EVENTS_AUTH_PASSWORD" desc:"The password to authenticate with the events broker. The events broker is the ocis service which receives and delivers events between the services.."`
|
||||
}
|
||||
|
||||
// StatCache holds cache config
|
||||
type StatCache struct {
|
||||
Store string `yaml:"store" env:"OCIS_CACHE_STORE;STORAGE_USERS_STAT_CACHE_STORE" desc:"The type of the cache store. Supported values are: 'memory', 'redis-sentinel', 'nats-js-kv', 'noop'. See the text description for details."`
|
||||
Nodes []string `yaml:"nodes" env:"OCIS_CACHE_STORE_NODES;STORAGE_USERS_STAT_CACHE_STORE_NODES" desc:"A list of nodes to access the configured store. This has no effect when 'memory' or 'ocmem' stores are configured. Note that the behaviour how nodes are used is dependent on the library of the configured store. See the Environment Variable Types description for more details."`
|
||||
Database string `yaml:"database" env:"OCIS_CACHE_DATABASE" desc:"The database name the configured store should use."`
|
||||
TTL time.Duration `yaml:"ttl" env:"OCIS_CACHE_TTL;STORAGE_USERS_STAT_CACHE_TTL" desc:"Default time to live for user info in the user info cache. Only applied when access tokens has no expiration. See the Environment Variable Types description for more details."`
|
||||
Size int `yaml:"size" env:"OCIS_CACHE_SIZE;STORAGE_USERS_STAT_CACHE_SIZE" desc:"The maximum quantity of items in the user info cache. Only applies when store type 'ocmem' is configured. Defaults to 512 which is derived from the ocmem package though not exclicitely set as default."`
|
||||
DisablePersistence bool `yaml:"disable_persistence" env:"OCIS_CACHE_DISABLE_PERSISTENCE;STORAGE_USERS_STAT_CACHE_DISABLE_PERSISTENCE" desc:"Disables persistence of the cache. Only applies when store type 'nats-js-kv' is configured. Defaults to false."`
|
||||
}
|
||||
|
||||
// FilemetadataCache holds cache config
|
||||
type FilemetadataCache struct {
|
||||
Store string `yaml:"store" env:"OCIS_CACHE_STORE;STORAGE_USERS_FILEMETADATA_CACHE_STORE" desc:"The type of the cache store. Supported values are: 'memory', 'redis-sentinel', 'nats-js-kv', 'noop'. See the text description for details."`
|
||||
@@ -200,6 +189,8 @@ type FilemetadataCache struct {
|
||||
TTL time.Duration `yaml:"ttl" env:"OCIS_CACHE_TTL;STORAGE_USERS_FILEMETADATA_CACHE_TTL" desc:"Default time to live for user info in the user info cache. Only applied when access tokens has no expiration. See the Environment Variable Types description for more details."`
|
||||
Size int `yaml:"size" env:"OCIS_CACHE_SIZE;STORAGE_USERS_FILEMETADATA_CACHE_SIZE" desc:"The maximum quantity of items in the user info cache. Only applies when store type 'ocmem' is configured. Defaults to 512 which is derived from the ocmem package though not exclicitely set as default."`
|
||||
DisablePersistence bool `yaml:"disable_persistence" env:"OCIS_CACHE_DISABLE_PERSISTENCE;STORAGE_USERS_FILEMETADATA_CACHE_DISABLE_PERSISTENCE" desc:"Disables persistence of the cache. Only applies when store type 'nats-js-kv' is configured. Defaults to false."`
|
||||
AuthUsername string `yaml:"username" env:"OCIS_CACHE_AUTH_USERNAME;STORAGE_USERS_FILEMETADATA_CACHE_AUTH_USERNAME" desc:"The username to authenticate with the cache store. Only applies when store type 'nats-js-kv' is configured."`
|
||||
AuthPassword string `yaml:"password" env:"OCIS_CACHE_AUTH_PASSWORD;STORAGE_USERS_FILEMETADATA_CACHE_AUTH_PASSWORD" desc:"The password to authenticate with the cache store. Only applies when store type 'nats-js-kv' is configured."`
|
||||
}
|
||||
|
||||
// IDCache holds cache config
|
||||
@@ -210,6 +201,8 @@ type IDCache struct {
|
||||
TTL time.Duration `yaml:"ttl" env:"OCIS_CACHE_TTL;STORAGE_USERS_ID_CACHE_TTL" desc:"Default time to live for user info in the user info cache. Only applied when access tokens have no expiration. Defaults to 300s which is derived from the underlaying package though not explicitly set as default. See the Environment Variable Types description for more details."`
|
||||
Size int `yaml:"size" env:"OCIS_CACHE_SIZE;STORAGE_USERS_ID_CACHE_SIZE" desc:"The maximum quantity of items in the user info cache. Only applies when store type 'ocmem' is configured. Defaults to 512 which is derived from the ocmem package though not exclicitely set as default."`
|
||||
DisablePersistence bool `yaml:"disable_persistence" env:"OCIS_CACHE_DISABLE_PERSISTENCE;STORAGE_USERS_ID_CACHE_DISABLE_PERSISTENCE" desc:"Disables persistence of the cache. Only applies when store type 'nats-js-kv' is configured. Defaults to false."`
|
||||
AuthUsername string `yaml:"username" env:"OCIS_CACHE_AUTH_USERNAME;STORAGE_USERS_ID_CACHE_AUTH_USERNAME" desc:"The username to authenticate with the cache store. Only applies when store type 'nats-js-kv' is configured."`
|
||||
AuthPassword string `yaml:"password" env:"OCIS_CACHE_AUTH_PASSWORD;STORAGE_USERS_ID_CACHE_AUTH_PASSWORD" desc:"The password to authenticate with the cache store. Only applies when store type 'nats-js-kv' is configured."`
|
||||
}
|
||||
|
||||
// S3Driver is the storage driver configuration when using 's3' storage driver
|
||||
|
||||
@@ -94,12 +94,6 @@ func DefaultConfig() *config.Config {
|
||||
ClusterID: "ocis-cluster",
|
||||
EnableTLS: false,
|
||||
},
|
||||
StatCache: config.StatCache{
|
||||
Store: "memory",
|
||||
Nodes: []string{"127.0.0.1:9233"},
|
||||
Database: "ocis",
|
||||
TTL: 300 * time.Second,
|
||||
},
|
||||
FilemetadataCache: config.FilemetadataCache{
|
||||
Store: "memory",
|
||||
Nodes: []string{"127.0.0.1:9233"},
|
||||
|
||||
@@ -46,6 +46,8 @@ func StorageUsersConfigFromStruct(cfg *config.Config) map[string]interface{} {
|
||||
"tls-root-ca-cert": cfg.Events.TLSRootCaCertPath,
|
||||
"enable-tls": cfg.Events.EnableTLS,
|
||||
"name": "storage-users-eventsmiddleware",
|
||||
"username": cfg.Events.AuthUsername,
|
||||
"password": cfg.Events.AuthPassword,
|
||||
},
|
||||
"prometheus": map[string]interface{}{
|
||||
"namespace": "ocis",
|
||||
@@ -70,35 +72,8 @@ func StorageUsersConfigFromStruct(cfg *config.Config) map[string]interface{} {
|
||||
"nats_tls_insecure": cfg.Events.TLSInsecure,
|
||||
"nats_root_ca_cert_path": cfg.Events.TLSRootCaCertPath,
|
||||
"nats_enable_tls": cfg.Events.EnableTLS,
|
||||
"data_txs": map[string]interface{}{
|
||||
"simple": map[string]interface{}{
|
||||
"cache_store": cfg.StatCache.Store,
|
||||
"cache_nodes": cfg.StatCache.Nodes,
|
||||
"cache_database": cfg.StatCache.Database,
|
||||
"cache_ttl": cfg.StatCache.TTL,
|
||||
"cache_size": cfg.StatCache.Size,
|
||||
"cache_table": "stat",
|
||||
"cache_disable_persistence": cfg.StatCache.DisablePersistence,
|
||||
},
|
||||
"spaces": map[string]interface{}{
|
||||
"cache_store": cfg.StatCache.Store,
|
||||
"cache_nodes": cfg.StatCache.Nodes,
|
||||
"cache_database": cfg.StatCache.Database,
|
||||
"cache_ttl": cfg.StatCache.TTL,
|
||||
"cache_size": cfg.StatCache.Size,
|
||||
"cache_table": "stat",
|
||||
"cache_disable_persistence": cfg.StatCache.DisablePersistence,
|
||||
},
|
||||
"tus": map[string]interface{}{
|
||||
"cache_store": cfg.StatCache.Store,
|
||||
"cache_nodes": cfg.StatCache.Nodes,
|
||||
"cache_database": cfg.StatCache.Database,
|
||||
"cache_ttl": cfg.StatCache.TTL,
|
||||
"cache_size": cfg.StatCache.Size,
|
||||
"cache_table": "stat",
|
||||
"cache_disable_persistence": cfg.StatCache.DisablePersistence,
|
||||
},
|
||||
},
|
||||
"nats_username": cfg.Events.AuthUsername,
|
||||
"nats_password": cfg.Events.AuthPassword,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -132,14 +132,6 @@ func Ocis(cfg *config.Config) map[string]interface{} {
|
||||
"max_concurrency": cfg.Drivers.OCIS.MaxConcurrency,
|
||||
"asyncfileuploads": cfg.Drivers.OCIS.AsyncUploads,
|
||||
"max_quota": cfg.Drivers.OCIS.MaxQuota,
|
||||
"statcache": map[string]interface{}{
|
||||
"cache_store": cfg.StatCache.Store,
|
||||
"cache_nodes": cfg.StatCache.Nodes,
|
||||
"cache_database": cfg.StatCache.Database,
|
||||
"cache_ttl": cfg.StatCache.TTL,
|
||||
"cache_size": cfg.StatCache.Size,
|
||||
"cache_disable_persistence": cfg.StatCache.DisablePersistence,
|
||||
},
|
||||
"filemetadatacache": map[string]interface{}{
|
||||
"cache_store": cfg.FilemetadataCache.Store,
|
||||
"cache_nodes": cfg.FilemetadataCache.Nodes,
|
||||
@@ -147,6 +139,8 @@ func Ocis(cfg *config.Config) map[string]interface{} {
|
||||
"cache_ttl": cfg.FilemetadataCache.TTL,
|
||||
"cache_size": cfg.FilemetadataCache.Size,
|
||||
"cache_disable_persistence": cfg.FilemetadataCache.DisablePersistence,
|
||||
"cache_auth_username": cfg.FilemetadataCache.AuthUsername,
|
||||
"cache_auth_password": cfg.FilemetadataCache.AuthPassword,
|
||||
},
|
||||
"idcache": map[string]interface{}{
|
||||
"cache_store": cfg.IDCache.Store,
|
||||
@@ -155,13 +149,11 @@ func Ocis(cfg *config.Config) map[string]interface{} {
|
||||
"cache_ttl": cfg.IDCache.TTL,
|
||||
"cache_size": cfg.IDCache.Size,
|
||||
"cache_disable_persistence": cfg.IDCache.DisablePersistence,
|
||||
"cache_auth_username": cfg.IDCache.AuthUsername,
|
||||
"cache_auth_password": cfg.IDCache.AuthPassword,
|
||||
},
|
||||
"events": map[string]interface{}{
|
||||
"natsaddress": cfg.Events.Addr,
|
||||
"natsclusterid": cfg.Events.ClusterID,
|
||||
"tlsinsecure": cfg.Events.TLSInsecure,
|
||||
"tlsrootcacertificate": cfg.Events.TLSRootCaCertPath,
|
||||
"numconsumers": cfg.Events.NumConsumers,
|
||||
"numconsumers": cfg.Events.NumConsumers,
|
||||
},
|
||||
"tokens": map[string]interface{}{
|
||||
"transfer_shared_secret": cfg.Commons.TransferSecret,
|
||||
@@ -193,14 +185,6 @@ func OcisNoEvents(cfg *config.Config) map[string]interface{} {
|
||||
"lock_cycle_duration_factor": cfg.Drivers.OCIS.LockCycleDurationFactor,
|
||||
"max_concurrency": cfg.Drivers.OCIS.MaxConcurrency,
|
||||
"max_quota": cfg.Drivers.OCIS.MaxQuota,
|
||||
"statcache": map[string]interface{}{
|
||||
"cache_store": cfg.StatCache.Store,
|
||||
"cache_nodes": cfg.StatCache.Nodes,
|
||||
"cache_database": cfg.StatCache.Database,
|
||||
"cache_ttl": cfg.StatCache.TTL,
|
||||
"cache_size": cfg.StatCache.Size,
|
||||
"cache_disable_persistence": cfg.StatCache.DisablePersistence,
|
||||
},
|
||||
"filemetadatacache": map[string]interface{}{
|
||||
"cache_store": cfg.FilemetadataCache.Store,
|
||||
"cache_nodes": cfg.FilemetadataCache.Nodes,
|
||||
@@ -208,6 +192,8 @@ func OcisNoEvents(cfg *config.Config) map[string]interface{} {
|
||||
"cache_ttl": cfg.FilemetadataCache.TTL,
|
||||
"cache_size": cfg.FilemetadataCache.Size,
|
||||
"cache_disable_persistence": cfg.FilemetadataCache.DisablePersistence,
|
||||
"cache_auth_username": cfg.FilemetadataCache.AuthUsername,
|
||||
"cache_auth_password": cfg.FilemetadataCache.AuthPassword,
|
||||
},
|
||||
"idcache": map[string]interface{}{
|
||||
"cache_store": cfg.IDCache.Store,
|
||||
@@ -216,6 +202,8 @@ func OcisNoEvents(cfg *config.Config) map[string]interface{} {
|
||||
"cache_ttl": cfg.IDCache.TTL,
|
||||
"cache_size": cfg.IDCache.Size,
|
||||
"cache_disable_persistence": cfg.IDCache.DisablePersistence,
|
||||
"cache_auth_username": cfg.IDCache.AuthUsername,
|
||||
"cache_auth_password": cfg.IDCache.AuthPassword,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -259,14 +247,6 @@ func S3NG(cfg *config.Config) map[string]interface{} {
|
||||
"lock_cycle_duration_factor": cfg.Drivers.S3NG.LockCycleDurationFactor,
|
||||
"max_concurrency": cfg.Drivers.S3NG.MaxConcurrency,
|
||||
"asyncfileuploads": cfg.Drivers.OCIS.AsyncUploads,
|
||||
"statcache": map[string]interface{}{
|
||||
"cache_store": cfg.StatCache.Store,
|
||||
"cache_nodes": cfg.StatCache.Nodes,
|
||||
"cache_database": cfg.StatCache.Database,
|
||||
"cache_ttl": cfg.StatCache.TTL,
|
||||
"cache_size": cfg.StatCache.Size,
|
||||
"cache_disable_persistence": cfg.StatCache.DisablePersistence,
|
||||
},
|
||||
"filemetadatacache": map[string]interface{}{
|
||||
"cache_store": cfg.FilemetadataCache.Store,
|
||||
"cache_nodes": cfg.FilemetadataCache.Nodes,
|
||||
@@ -274,6 +254,8 @@ func S3NG(cfg *config.Config) map[string]interface{} {
|
||||
"cache_ttl": cfg.FilemetadataCache.TTL,
|
||||
"cache_size": cfg.FilemetadataCache.Size,
|
||||
"cache_disable_persistence": cfg.FilemetadataCache.DisablePersistence,
|
||||
"cache_auth_username": cfg.FilemetadataCache.AuthUsername,
|
||||
"cache_auth_password": cfg.FilemetadataCache.AuthPassword,
|
||||
},
|
||||
"idcache": map[string]interface{}{
|
||||
"cache_store": cfg.IDCache.Store,
|
||||
@@ -282,13 +264,11 @@ func S3NG(cfg *config.Config) map[string]interface{} {
|
||||
"cache_ttl": cfg.IDCache.TTL,
|
||||
"cache_size": cfg.IDCache.Size,
|
||||
"cache_disable_persistence": cfg.IDCache.DisablePersistence,
|
||||
"cache_auth_username": cfg.IDCache.AuthUsername,
|
||||
"cache_auth_password": cfg.IDCache.AuthPassword,
|
||||
},
|
||||
"events": map[string]interface{}{
|
||||
"natsaddress": cfg.Events.Addr,
|
||||
"natsclusterid": cfg.Events.ClusterID,
|
||||
"tlsinsecure": cfg.Events.TLSInsecure,
|
||||
"tlsrootcacertificate": cfg.Events.TLSRootCaCertPath,
|
||||
"numconsumers": cfg.Events.NumConsumers,
|
||||
"numconsumers": cfg.Events.NumConsumers,
|
||||
},
|
||||
"tokens": map[string]interface{}{
|
||||
"transfer_shared_secret": cfg.Commons.TransferSecret,
|
||||
@@ -324,14 +304,6 @@ func S3NGNoEvents(cfg *config.Config) map[string]interface{} {
|
||||
"max_acquire_lock_cycles": cfg.Drivers.S3NG.MaxAcquireLockCycles,
|
||||
"max_concurrency": cfg.Drivers.S3NG.MaxConcurrency,
|
||||
"lock_cycle_duration_factor": cfg.Drivers.S3NG.LockCycleDurationFactor,
|
||||
"statcache": map[string]interface{}{
|
||||
"cache_store": cfg.StatCache.Store,
|
||||
"cache_nodes": cfg.StatCache.Nodes,
|
||||
"cache_database": cfg.StatCache.Database,
|
||||
"cache_ttl": cfg.StatCache.TTL,
|
||||
"cache_size": cfg.StatCache.Size,
|
||||
"cache_disable_persistence": cfg.StatCache.DisablePersistence,
|
||||
},
|
||||
"filemetadatacache": map[string]interface{}{
|
||||
"cache_store": cfg.FilemetadataCache.Store,
|
||||
"cache_nodes": cfg.FilemetadataCache.Nodes,
|
||||
@@ -339,6 +311,8 @@ func S3NGNoEvents(cfg *config.Config) map[string]interface{} {
|
||||
"cache_ttl": cfg.FilemetadataCache.TTL,
|
||||
"cache_size": cfg.FilemetadataCache.Size,
|
||||
"cache_disable_persistence": cfg.FilemetadataCache.DisablePersistence,
|
||||
"cache_auth_username": cfg.FilemetadataCache.AuthUsername,
|
||||
"cache_auth_password": cfg.FilemetadataCache.AuthPassword,
|
||||
},
|
||||
"idcache": map[string]interface{}{
|
||||
"cache_store": cfg.IDCache.Store,
|
||||
@@ -347,6 +321,8 @@ func S3NGNoEvents(cfg *config.Config) map[string]interface{} {
|
||||
"cache_ttl": cfg.IDCache.TTL,
|
||||
"cache_size": cfg.IDCache.Size,
|
||||
"cache_disable_persistence": cfg.IDCache.DisablePersistence,
|
||||
"cache_auth_username": cfg.IDCache.AuthUsername,
|
||||
"cache_auth_password": cfg.IDCache.AuthPassword,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2023-12-30 00:38+0000\n"
|
||||
"POT-Creation-Date: 2024-01-19 00:48+0000\n"
|
||||
"PO-Revision-Date: 2023-03-15 08:28+0000\n"
|
||||
"Last-Translator: Andi Chandler <andi@gowling.com>, 2023\n"
|
||||
"Language-Team: English (United Kingdom) (https://app.transifex.com/owncloud-org/teams/6149/en_GB/)\n"
|
||||
|
||||
@@ -11,7 +11,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2024-01-03 04:25+0000\n"
|
||||
"POT-Creation-Date: 2024-01-24 00:44+0000\n"
|
||||
"PO-Revision-Date: 2023-03-15 08:28+0000\n"
|
||||
"Last-Translator: Juan Carlos Garrote, 2023\n"
|
||||
"Language-Team: Spanish (https://app.transifex.com/owncloud-org/teams/6149/es/)\n"
|
||||
|
||||
@@ -13,7 +13,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2023-12-28 00:05+0000\n"
|
||||
"POT-Creation-Date: 2024-01-18 00:47+0000\n"
|
||||
"PO-Revision-Date: 2023-03-15 08:28+0000\n"
|
||||
"Last-Translator: Roman Perekhod, 2023\n"
|
||||
"Language-Team: Russian (https://app.transifex.com/owncloud-org/teams/6149/ru/)\n"
|
||||
|
||||
@@ -11,7 +11,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2023-12-30 00:38+0000\n"
|
||||
"POT-Creation-Date: 2024-01-19 00:48+0000\n"
|
||||
"PO-Revision-Date: 2023-03-15 08:28+0000\n"
|
||||
"Last-Translator: Begüm Topyıldız <bgmtpyldz@gmail.com>, 2023\n"
|
||||
"Language-Team: Turkish (https://app.transifex.com/owncloud-org/teams/6149/tr/)\n"
|
||||
|
||||
@@ -1822,4 +1822,29 @@ class GraphHelper {
|
||||
self::getRequestHeaders()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $baseUrl
|
||||
* @param string $xRequestId
|
||||
* @param string $user
|
||||
* @param string $password
|
||||
*
|
||||
* @return ResponseInterface
|
||||
* @throws GuzzleException
|
||||
*/
|
||||
public static function getSharesSharedWithMe(
|
||||
string $baseUrl,
|
||||
string $xRequestId,
|
||||
string $user,
|
||||
string $password
|
||||
): ResponseInterface {
|
||||
$url = self::getBetaFullUrl($baseUrl, "me/drive/sharedWithMe");
|
||||
return HttpRequestHelper::get(
|
||||
$url,
|
||||
$xRequestId,
|
||||
$user,
|
||||
$password,
|
||||
self::getRequestHeaders()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -179,6 +179,7 @@ class WebDavHelper {
|
||||
* @param string|null $type
|
||||
* @param int|null $davPathVersionToUse
|
||||
* @param string|null $doDavRequestAsUser
|
||||
* @param array|null $headers
|
||||
*
|
||||
* @return ResponseInterface
|
||||
* @throws Exception
|
||||
@@ -194,7 +195,8 @@ class WebDavHelper {
|
||||
?string $folderDepth = '1',
|
||||
?string $type = "files",
|
||||
?int $davPathVersionToUse = self::DAV_VERSION_NEW,
|
||||
?string $doDavRequestAsUser = null
|
||||
?string $doDavRequestAsUser = null,
|
||||
?array $headers = []
|
||||
):ResponseInterface {
|
||||
$body = self::getBodyForPropfind($properties);
|
||||
$folderDepth = (string) $folderDepth;
|
||||
|
||||
@@ -125,10 +125,14 @@ cannot share a folder with create permission
|
||||
- [coreApiSharePublicLink2/uploadToPublicLinkShare.feature:13](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/coreApiSharePublicLink2/uploadToPublicLinkShare.feature#L13)
|
||||
- [coreApiSharePublicLink2/uploadToPublicLinkShare.feature:121](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/coreApiSharePublicLink2/uploadToPublicLinkShare.feature#L121)
|
||||
|
||||
#### [Set quota over settings](https://github.com/owncloud/ocis/issues/1290)
|
||||
#### [d:quota-available-bytes in dprop of PROPFIND give wrong response value](https://github.com/owncloud/ocis/issues/8197)
|
||||
|
||||
- [coreApiSharePublicLink2/uploadToPublicLinkShare.feature:91](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/coreApiSharePublicLink2/uploadToPublicLinkShare.feature#L91)
|
||||
- [coreApiSharePublicLink2/uploadToPublicLinkShare.feature:101](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/coreApiSharePublicLink2/uploadToPublicLinkShare.feature#L101)
|
||||
- [coreApiWebdavProperties/getQuota.feature:55](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/coreApiWebdavProperties/getQuota.feature#L55)
|
||||
- [coreApiWebdavProperties/getQuota.feature:56](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/coreApiWebdavProperties/getQuota.feature#L56)
|
||||
- [coreApiWebdavProperties/getQuota.feature:57](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/coreApiWebdavProperties/getQuota.feature#L57)
|
||||
- [coreApiWebdavProperties/getQuota.feature:71](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/coreApiWebdavProperties/getQuota.feature#L71)
|
||||
- [coreApiWebdavProperties/getQuota.feature:72](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/coreApiWebdavProperties/getQuota.feature#L72)
|
||||
- [coreApiWebdavProperties/getQuota.feature:73](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/coreApiWebdavProperties/getQuota.feature#L73)
|
||||
|
||||
#### [deleting a file inside a received shared folder is moved to the trash-bin of the sharer not the receiver](https://github.com/owncloud/ocis/issues/1124)
|
||||
|
||||
@@ -144,11 +148,6 @@ cannot share a folder with create permission
|
||||
- [coreApiTrashbin/trashbinSharingToShares.feature:201](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/coreApiTrashbin/trashbinSharingToShares.feature#L201)
|
||||
- [coreApiTrashbin/trashbinSharingToShares.feature:224](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/coreApiTrashbin/trashbinSharingToShares.feature#L224)
|
||||
|
||||
#### [changing user quota gives ocs status 103 / Cannot set quota](https://github.com/owncloud/product/issues/247)
|
||||
|
||||
- [coreApiShareOperationsToShares2/uploadToShare.feature:202](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/coreApiShareOperationsToShares2/uploadToShare.feature#L202)
|
||||
- [coreApiShareOperationsToShares2/uploadToShare.feature:203](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/coreApiShareOperationsToShares2/uploadToShare.feature#L203)
|
||||
|
||||
#### [Expiration date for shares is not implemented](https://github.com/owncloud/ocis/issues/1250)
|
||||
|
||||
#### Expiration date of user shares
|
||||
|
||||
@@ -45,18 +45,6 @@ The expected failures in this file are from features in the owncloud/ocis repo.
|
||||
|
||||
- [apiGraphUserGroup/deleteGroup.feature:67](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/apiGraphUserGroup/deleteGroup.feature#L67)
|
||||
|
||||
#### [CORS headers are not identical with oC10 headers](https://github.com/owncloud/ocis/issues/5195)
|
||||
|
||||
- [apiCors/cors.feature:28](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/apiCors/cors.feature#L28)
|
||||
- [apiCors/cors.feature:29](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/apiCors/cors.feature#L29)
|
||||
- [apiCors/cors.feature:30](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/apiCors/cors.feature#L30)
|
||||
- [apiCors/cors.feature:31](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/apiCors/cors.feature#L31)
|
||||
|
||||
#### [Requests with invalid credentials do not return CORS headers](https://github.com/owncloud/ocis/issues/5194)
|
||||
|
||||
- [apiCors/cors.feature:70](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/apiCors/cors.feature#L70)
|
||||
- [apiCors/cors.feature:71](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/apiCors/cors.feature#L71)
|
||||
|
||||
#### [A User can get information of another user with Graph API](https://github.com/owncloud/ocis/issues/5125)
|
||||
|
||||
- [apiGraphUserGroup/getUser.feature:89](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/apiGraphUserGroup/getUser.feature#L89)
|
||||
@@ -270,10 +258,10 @@ The expected failures in this file are from features in the owncloud/ocis repo.
|
||||
- [apiSharingNg/linkShare.feature:453](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/apiSharingNg/linkShare.feature#L453)
|
||||
- [apiSharingNg/linkShare.feature:455](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/apiSharingNg/linkShare.feature#L455)
|
||||
- [apiSharingNg/linkShare.feature:456](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/apiSharingNg/linkShare.feature#L456)
|
||||
- [apiSharingNg/deletePermissions.feature:146](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/apiSharingNg/deletePermissions.feature#L146)
|
||||
- [apiSharingNg/deletePermissions.feature:163](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/apiSharingNg/deletePermissions.feature#L163)
|
||||
- [apiSharingNg/deletePermissions.feature:184](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/apiSharingNg/deletePermissions.feature#L184)
|
||||
- [apiSharingNg/deletePermissions.feature:203](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/apiSharingNg/deletePermissions.feature#L203)
|
||||
- [apiSharingNg/deletePermissions.feature:130](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/apiSharingNg/deletePermissions.feature#L130)
|
||||
- [apiSharingNg/deletePermissions.feature:147](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/apiSharingNg/deletePermissions.feature#L147)
|
||||
- [apiSharingNg/deletePermissions.feature:168](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/apiSharingNg/deletePermissions.feature#L168)
|
||||
- [apiSharingNg/deletePermissions.feature:187](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/apiSharingNg/deletePermissions.feature#L187)
|
||||
|
||||
### [sharee (editor role) MOVE a file by file-id into same shared folder returns 403](https://github.com/owncloud/ocis/issues/7617)
|
||||
|
||||
|
||||
@@ -16,7 +16,9 @@ Feature: Copy test
|
||||
Scenario: check the COPY response headers
|
||||
Given user "Alice" has uploaded a file inside space "new-space" with content "some content" to "testfile.txt"
|
||||
And user "Alice" has created a folder "new" in space "new-space"
|
||||
When user "Alice" copies file "testfile.txt" from space "new-space" to "/new/testfile.txt" inside space "new-space" using the WebDAV API
|
||||
When user "Alice" copies file "testfile.txt" from space "new-space" to "/new/testfile.txt" inside space "new-space" with following headers using the WebDAV API
|
||||
| header | value |
|
||||
| Origin | %base_url% |
|
||||
Then the HTTP status code should be "201"
|
||||
And the following headers should match these regular expressions
|
||||
| Oc-Fileid | /^[a-f0-9!\$\-]{110}$/ |
|
||||
|
||||
@@ -18,11 +18,10 @@ Feature: CORS headers
|
||||
Then the OCS status code should be "<ocs-code>"
|
||||
And the HTTP status code should be "<http-code>"
|
||||
And the following headers should be set
|
||||
| header | value |
|
||||
| Access-Control-Allow-Headers | OC-Checksum,OC-Total-Length,OCS-APIREQUEST,X-OC-Mtime,OC-RequestAppPassword,Accept,Authorization,Brief,Content-Length,Content-Range,Content-Type,Date,Depth,Destination,Host,If,If-Match,If-Modified-Since,If-None-Match,If-Range,If-Unmodified-Since,Location,Lock-Token,Overwrite,Prefer,Range,Schedule-Reply,Timeout,User-Agent,X-Expected-Entity-Length,Accept-Language,Access-Control-Request-Method,Access-Control-Allow-Origin,Cache-Control,ETag,OC-Autorename,OC-CalDav-Import,OC-Chunked,OC-Etag,OC-FileId,OC-LazyOps,OC-Total-File-Length,Origin,X-Request-ID,X-Requested-With |
|
||||
| Access-Control-Expose-Headers | Content-Location,DAV,ETag,Link,Lock-Token,OC-ETag,OC-Checksum,OC-FileId,OC-JobStatus-Location,OC-RequestAppPassword,Vary,Webdav-Location,X-Sabre-Status |
|
||||
| Access-Control-Allow-Origin | https://aphno.badal |
|
||||
| Access-Control-Allow-Methods | GET,OPTIONS,POST,PUT,DELETE,MKCOL,PROPFIND,PATCH,PROPPATCH,REPORT |
|
||||
| header | value |
|
||||
| Access-Control-Expose-Headers | Location |
|
||||
| Access-Control-Allow-Origin | https://aphno.badal |
|
||||
| Access-Control-Allow-Credentials | true |
|
||||
Examples:
|
||||
| ocs_api_version | endpoint | ocs-code | http-code |
|
||||
| 1 | /config | 100 | 200 |
|
||||
@@ -34,8 +33,8 @@ Feature: CORS headers
|
||||
Scenario Outline: CORS headers should not be returned when CORS domain does not match origin header
|
||||
Given using OCS API version "<ocs_api_version>"
|
||||
When user "Alice" sends HTTP method "GET" to OCS API endpoint "<endpoint>" with headers
|
||||
| header | value |
|
||||
| Origin | https://mero.badal |
|
||||
| header | value |
|
||||
| Origin | https://mero.badal |
|
||||
Then the OCS status code should be "<ocs-code>"
|
||||
And the HTTP status code should be "<http-code>"
|
||||
And the following headers should not be set
|
||||
@@ -52,20 +51,42 @@ Feature: CORS headers
|
||||
| 2 | /apps/files_sharing/api/v1/shares | 200 | 200 |
|
||||
|
||||
@issue-5194
|
||||
Scenario Outline: CORS headers should be returned when an invalid password is used
|
||||
Scenario Outline: CORS headers should be returned when an preflight request is sent
|
||||
Given using OCS API version "<ocs_api_version>"
|
||||
When user "Alice" sends HTTP method "GET" to OCS API endpoint "<endpoint>" with headers using password "invalid"
|
||||
When user "Alice" sends HTTP method "OPTIONS" to OCS API endpoint "<endpoint>" with headers
|
||||
| header | value |
|
||||
| Origin | https://aphno.badal |
|
||||
| Access-Control-Request-Headers | Origin, Accept, Content-Type, Depth, Authorization, Ocs-Apirequest, If-None-Match, If-Match, Destination, Overwrite, X-Request-Id, X-Requested-With, Tus-Resumable, Tus-Checksum-Algorithm, Upload-Concat, Upload-Length, Upload-Metadata, Upload-Defer-Length, Upload-Expires, Upload-Checksum, Upload-Offset, X-Http-Method-Override, Cache-Control |
|
||||
| Access-Control-Request-Method | <request_method> |
|
||||
And the HTTP status code should be "204"
|
||||
And the following headers should be set
|
||||
| header | value |
|
||||
| Access-Control-Allow-Headers | Origin, Accept, Content-Type, Depth, Authorization, Ocs-Apirequest, If-None-Match, If-Match, Destination, Overwrite, X-Request-Id, X-Requested-With, Tus-Resumable, Tus-Checksum-Algorithm, Upload-Concat, Upload-Length, Upload-Metadata, Upload-Defer-Length, Upload-Expires, Upload-Checksum, Upload-Offset, X-Http-Method-Override, Cache-Control |
|
||||
| Access-Control-Allow-Origin | https://aphno.badal |
|
||||
| Access-Control-Allow-Methods | <request_method> |
|
||||
Examples:
|
||||
| ocs_api_version | | endpoint | request_method |
|
||||
| 1 | | /apps/files_sharing/api/v1/shares | GET |
|
||||
| 2 | | /apps/files_sharing/api/v1/shares | PUT |
|
||||
| 1 | | /apps/files_sharing/api/v1/shares | DELETE |
|
||||
| 2 | | /apps/files_sharing/api/v1/shares | POST |
|
||||
|
||||
|
||||
Scenario: CORS headers should be returned when setting CORS domain sending origin header in the Graph api
|
||||
When user "Alice" lists all available spaces with headers using the Graph API
|
||||
| header | value |
|
||||
| Origin | https://aphno.badal |
|
||||
Then the OCS status code should be "997"
|
||||
And the HTTP status code should be "401"
|
||||
Then the HTTP status code should be "200"
|
||||
And the following headers should be set
|
||||
| header | value |
|
||||
| Access-Control-Allow-Headers | OC-Checksum,OC-Total-Length,OCS-APIREQUEST,X-OC-Mtime,OC-RequestAppPassword,Accept,Authorization,Brief,Content-Length,Content-Range,Content-Type,Date,Depth,Destination,Host,If,If-Match,If-Modified-Since,If-None-Match,If-Range,If-Unmodified-Since,Location,Lock-Token,Overwrite,Prefer,Range,Schedule-Reply,Timeout,User-Agent,X-Expected-Entity-Length,Accept-Language,Access-Control-Request-Method,Access-Control-Allow-Origin,Cache-Control,ETag,OC-Autorename,OC-CalDav-Import,OC-Chunked,OC-Etag,OC-FileId,OC-LazyOps,OC-Total-File-Length,Origin,X-Request-ID,X-Requested-With |
|
||||
| Access-Control-Expose-Headers | Content-Location,DAV,ETag,Link,Lock-Token,OC-ETag,OC-Checksum,OC-FileId,OC-JobStatus-Location,OC-RequestAppPassword,Vary,Webdav-Location,X-Sabre-Status |
|
||||
| Access-Control-Allow-Origin | https://aphno.badal |
|
||||
| Access-Control-Allow-Methods | GET,OPTIONS,POST,PUT,DELETE,MKCOL,PROPFIND,PATCH,PROPPATCH,REPORT |
|
||||
Examples:
|
||||
| ocs_api_version | endpoint |
|
||||
| 1 | /apps/files_sharing/api/v1/shares |
|
||||
| 2 | /apps/files_sharing/api/v1/shares |
|
||||
| header | value |
|
||||
| Access-Control-Allow-Origin | https://aphno.badal |
|
||||
|
||||
@issue-8231
|
||||
Scenario: CORS headers should be returned when setting CORS domain sending origin header in the Webdav api
|
||||
When user "Alice" sends PROPFIND request to space "Alice Hansen" with headers using the WebDAV API
|
||||
| header | value |
|
||||
| Origin | https://aphno.badal |
|
||||
Then the HTTP status code should be "207"
|
||||
And the following headers should be set
|
||||
| header | value |
|
||||
| Access-Control-Allow-Origin | https://aphno.badal |
|
||||
|
||||
@@ -1443,3 +1443,171 @@ Feature: get users
|
||||
| user | errorToken |
|
||||
| Alice-From-Wonderland | -From-Wonderland |
|
||||
| Alice@From@Wonderland | @From@Wonderland |
|
||||
|
||||
@issue-7990
|
||||
Scenario: non-admin user searches other users by e-mail
|
||||
When user "Brian" searches for user "%22alice@example.org%22" using Graph API
|
||||
Then the HTTP status code should be "200"
|
||||
And the JSON data of the response should match
|
||||
"""
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"value"
|
||||
],
|
||||
"properties": {
|
||||
"value": {
|
||||
"type": "array",
|
||||
"required": [
|
||||
"displayName",
|
||||
"id",
|
||||
"mail",
|
||||
"userType"
|
||||
],
|
||||
"properties": {
|
||||
"displayName": {
|
||||
"type": "string",
|
||||
"enum": ["Alice Hansen"]
|
||||
},
|
||||
"id": {
|
||||
"type": "string",
|
||||
"pattern": "^%user_id_pattern%$"
|
||||
},
|
||||
"mail": {
|
||||
"type": "string",
|
||||
"enum": ["alice@example.org"]
|
||||
},
|
||||
"userType": {
|
||||
"type": "string",
|
||||
"enum": ["Member"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
|
||||
Scenario: non-admin user searches for a disabled users
|
||||
Given the user "Admin" has disabled user "Alice"
|
||||
When user "Brian" searches for user "alice" using Graph API
|
||||
Then the HTTP status code should be "200"
|
||||
And the JSON data of the response should match
|
||||
"""
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"value"
|
||||
],
|
||||
"properties": {
|
||||
"value": {
|
||||
"type": "array",
|
||||
"required": [
|
||||
"displayName",
|
||||
"id",
|
||||
"mail",
|
||||
"userType"
|
||||
],
|
||||
"properties": {
|
||||
"displayName": {
|
||||
"type": "string",
|
||||
"enum": ["Alice Hansen"]
|
||||
},
|
||||
"id": {
|
||||
"type": "string",
|
||||
"pattern": "^%user_id_pattern%$"
|
||||
},
|
||||
"mail": {
|
||||
"type": "string",
|
||||
"enum": ["alice@example.org"]
|
||||
},
|
||||
"userType": {
|
||||
"type": "string",
|
||||
"enum": ["Member"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
|
||||
Scenario: non-admin user searches for multiple users having same displayname
|
||||
Given the user "Admin" has created a new user with the following attributes:
|
||||
| userName | another-alice |
|
||||
| displayName | Alice Hansen |
|
||||
| email | another-alice@example.org |
|
||||
| password | containsCharacters(*:!;_+-&) |
|
||||
|
||||
When user "Brian" searches for user "alice" using Graph API
|
||||
Then the HTTP status code should be "200"
|
||||
And the JSON data of the response should match
|
||||
"""
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"value"
|
||||
],
|
||||
"properties": {
|
||||
"value": {
|
||||
"type": "array",
|
||||
"items": [
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"displayName",
|
||||
"id",
|
||||
"mail",
|
||||
"userType"
|
||||
],
|
||||
"properties": {
|
||||
"displayName": {
|
||||
"type": "string",
|
||||
"enum": ["Alice Hansen"]
|
||||
},
|
||||
"id": {
|
||||
"type": "string",
|
||||
"pattern": "^%user_id_pattern%$"
|
||||
},
|
||||
"mail": {
|
||||
"type": "string",
|
||||
"enum": ["alice@example.org"]
|
||||
},
|
||||
"userType": {
|
||||
"type": "string",
|
||||
"enum": ["Member"]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"displayName",
|
||||
"id",
|
||||
"mail",
|
||||
"userType"
|
||||
],
|
||||
"properties": {
|
||||
"displayName": {
|
||||
"type": "string",
|
||||
"enum": ["Alice Hansen"]
|
||||
},
|
||||
"id": {
|
||||
"type": "string",
|
||||
"pattern": "^%user_id_pattern%$"
|
||||
},
|
||||
"mail": {
|
||||
"type": "string",
|
||||
"enum": ["another-alice@example.org"]
|
||||
},
|
||||
"userType": {
|
||||
"type": "string",
|
||||
"enum": ["Member"]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
@@ -27,13 +27,9 @@ Feature: Remove access to a drive item
|
||||
| permissionsRole | resource-type | path |
|
||||
| Viewer | file | textfile.txt |
|
||||
| File Editor | file | textfile.txt |
|
||||
| Co Owner | file | textfile.txt |
|
||||
| Manager | file | textfile.txt |
|
||||
| Viewer | folder | FolderToShare |
|
||||
| Editor | folder | FolderToShare |
|
||||
| Co Owner | folder | FolderToShare |
|
||||
| Uploader | folder | FolderToShare |
|
||||
| Manager | folder | FolderToShare |
|
||||
|
||||
|
||||
Scenario Outline: user removes access to resource inside of a project space in the user share
|
||||
@@ -56,13 +52,9 @@ Feature: Remove access to a drive item
|
||||
| permissionsRole | resource-type | path |
|
||||
| Viewer | file | textfile.txt |
|
||||
| File Editor | file | textfile.txt |
|
||||
| Co Owner | file | textfile.txt |
|
||||
| Manager | file | textfile.txt |
|
||||
| Viewer | folder | FolderToShare |
|
||||
| Editor | folder | FolderToShare |
|
||||
| Co Owner | folder | FolderToShare |
|
||||
| Uploader | folder | FolderToShare |
|
||||
| Manager | folder | FolderToShare |
|
||||
|
||||
|
||||
Scenario Outline: user removes access to a resource in a group share
|
||||
@@ -86,13 +78,9 @@ Feature: Remove access to a drive item
|
||||
| permissionsRole | resource-type | path |
|
||||
| Viewer | file | textfile.txt |
|
||||
| File Editor | file | textfile.txt |
|
||||
| Co Owner | file | textfile.txt |
|
||||
| Manager | file | textfile.txt |
|
||||
| Viewer | folder | FolderToShare |
|
||||
| Editor | folder | FolderToShare |
|
||||
| Co Owner | folder | FolderToShare |
|
||||
| Uploader | folder | FolderToShare |
|
||||
| Manager | folder | FolderToShare |
|
||||
|
||||
|
||||
Scenario Outline: user removes access to a resource inside of a project space in group share
|
||||
@@ -118,13 +106,9 @@ Feature: Remove access to a drive item
|
||||
| permissionsRole | resource-type | path |
|
||||
| Viewer | file | textfile.txt |
|
||||
| File Editor | file | textfile.txt |
|
||||
| Co Owner | file | textfile.txt |
|
||||
| Manager | file | textfile.txt |
|
||||
| Viewer | folder | FolderToShare |
|
||||
| Editor | folder | FolderToShare |
|
||||
| Co Owner | folder | FolderToShare |
|
||||
| Uploader | folder | FolderToShare |
|
||||
| Manager | folder | FolderToShare |
|
||||
|
||||
|
||||
Scenario Outline: user removes access to a folder in link share
|
||||
|
||||
125
tests/acceptance/features/apiSharingNg/reshare.feature
Normal file
125
tests/acceptance/features/apiSharingNg/reshare.feature
Normal file
@@ -0,0 +1,125 @@
|
||||
Feature: Reshare a share invitation
|
||||
As a user
|
||||
I want to be able to reshare the share invitations to other users
|
||||
So that they can have access to it
|
||||
|
||||
https://owncloud.dev/libre-graph-api/#/drives.permissions/Invite
|
||||
|
||||
Background:
|
||||
Given these users have been created with default attributes and without skeleton files:
|
||||
| username |
|
||||
| Alice |
|
||||
| Brian |
|
||||
| Carol |
|
||||
|
||||
|
||||
Scenario Outline: reshare a file to a user with different roles
|
||||
Given user "Alice" has uploaded file with content "to share" to "/textfile1.txt"
|
||||
And user "Alice" has sent the following share invitation:
|
||||
| resourceType | file |
|
||||
| resource | textfile1.txt |
|
||||
| space | Personal |
|
||||
| sharee | Brian |
|
||||
| shareType | user |
|
||||
| permissionsRole | <permissions-role> |
|
||||
When user "Brian" sends the following share invitation using the Graph API:
|
||||
| resourceType | file |
|
||||
| resource | textfile1.txt |
|
||||
| space | Shares |
|
||||
| sharee | Carol |
|
||||
| shareType | user |
|
||||
| permissionsRole | <reshare-permissions-role> |
|
||||
Then the HTTP status code should be "200"
|
||||
And for user "Carol" the space Shares should contain these entries:
|
||||
| textfile1.txt |
|
||||
Examples:
|
||||
| permissions-role | reshare-permissions-role |
|
||||
| Viewer | Viewer |
|
||||
| File Editor | Viewer |
|
||||
| File Editor | File Editor |
|
||||
|
||||
|
||||
Scenario Outline: reshare a folder to a user with different roles
|
||||
Given user "Alice" has created folder "FolderToShare"
|
||||
And user "Alice" has sent the following share invitation:
|
||||
| resourceType | folder |
|
||||
| resource | FolderToShare |
|
||||
| space | Personal |
|
||||
| sharee | Brian |
|
||||
| shareType | user |
|
||||
| permissionsRole | <permissions-role> |
|
||||
When user "Brian" sends the following share invitation using the Graph API:
|
||||
| resourceType | folder |
|
||||
| resource | FolderToShare |
|
||||
| space | Shares |
|
||||
| sharee | Carol |
|
||||
| shareType | user |
|
||||
| permissionsRole | <reshare-permissions-role> |
|
||||
Then the HTTP status code should be "200"
|
||||
And for user "Carol" the space Shares should contain these entries:
|
||||
| FolderToShare |
|
||||
Examples:
|
||||
| permissions-role | reshare-permissions-role |
|
||||
| Viewer | Viewer |
|
||||
| Editor | Viewer |
|
||||
| Editor | Editor |
|
||||
| Editor | Uploader |
|
||||
|
||||
|
||||
Scenario Outline: reshare a file inside project space to a user with different roles
|
||||
Given using spaces DAV path
|
||||
And the administrator has assigned the role "Space Admin" to user "Alice" using the Graph API
|
||||
And user "Alice" has created a space "NewSpace" with the default quota using the Graph API
|
||||
And user "Alice" has uploaded a file inside space "NewSpace" with content "to share" to "textfile1.txt"
|
||||
And user "Alice" has sent the following share invitation:
|
||||
| resourceType | file |
|
||||
| resource | textfile1.txt |
|
||||
| space | NewSpace |
|
||||
| sharee | Brian |
|
||||
| shareType | user |
|
||||
| permissionsRole | <permissions-role> |
|
||||
When user "Brian" sends the following share invitation using the Graph API:
|
||||
| resourceType | file |
|
||||
| resource | textfile1.txt |
|
||||
| space | Shares |
|
||||
| sharee | Carol |
|
||||
| shareType | user |
|
||||
| permissionsRole | <reshare-permissions-role> |
|
||||
Then the HTTP status code should be "200"
|
||||
And for user "Carol" the space Shares should contain these entries:
|
||||
| textfile1.txt |
|
||||
Examples:
|
||||
| permissions-role | reshare-permissions-role |
|
||||
| Viewer | Viewer |
|
||||
| File Editor | Viewer |
|
||||
| File Editor | File Editor |
|
||||
|
||||
|
||||
Scenario Outline: reshare a folder inside project space to a user with different roles
|
||||
Given using spaces DAV path
|
||||
And the administrator has assigned the role "Space Admin" to user "Alice" using the Graph API
|
||||
And user "Alice" has created a space "NewSpace" with the default quota using the Graph API
|
||||
And user "Alice" has created a folder "FolderToShare" in space "NewSpace"
|
||||
And user "Alice" has sent the following share invitation:
|
||||
| resourceType | folder |
|
||||
| resource | FolderToShare |
|
||||
| space | NewSpace |
|
||||
| sharee | Brian |
|
||||
| shareType | user |
|
||||
| permissionsRole | <permissions-role> |
|
||||
When user "Brian" sends the following share invitation using the Graph API:
|
||||
| resourceType | folder |
|
||||
| resource | FolderToShare |
|
||||
| space | Shares |
|
||||
| sharee | Carol |
|
||||
| shareType | user |
|
||||
| permissionsRole | <reshare-permissions-role> |
|
||||
Then the HTTP status code should be "200"
|
||||
And for user "Carol" the space Shares should contain these entries:
|
||||
| FolderToShare |
|
||||
Examples:
|
||||
| permissions-role | reshare-permissions-role |
|
||||
| Viewer | Viewer |
|
||||
| Editor | Viewer |
|
||||
| Editor | Editor |
|
||||
| Editor | Uploader |
|
||||
@@ -23,6 +23,8 @@ Feature: Send a sharing invitations
|
||||
| shareType | user |
|
||||
| permissionsRole | <permissions-role> |
|
||||
Then the HTTP status code should be "200"
|
||||
And for user "Brian" the space Shares should contain these entries:
|
||||
| <path> |
|
||||
And the JSON data of the response should match
|
||||
"""
|
||||
{
|
||||
@@ -89,13 +91,9 @@ Feature: Send a sharing invitations
|
||||
| permissions-role | resource-type | path |
|
||||
| Viewer | file | /textfile1.txt |
|
||||
| File Editor | file | /textfile1.txt |
|
||||
| Co Owner | file | /textfile1.txt |
|
||||
| Manager | file | /textfile1.txt |
|
||||
| Viewer | folder | FolderToShare |
|
||||
| Editor | folder | FolderToShare |
|
||||
| Co Owner | folder | FolderToShare |
|
||||
| Uploader | folder | FolderToShare |
|
||||
| Manager | folder | FolderToShare |
|
||||
|
||||
|
||||
Scenario Outline: send share invitation to group with different roles
|
||||
@@ -115,6 +113,10 @@ Feature: Send a sharing invitations
|
||||
| shareType | group |
|
||||
| permissionsRole | <permissions-role> |
|
||||
Then the HTTP status code should be "200"
|
||||
And for user "Brian" the space Shares should contain these entries:
|
||||
| <path> |
|
||||
And for user "Carol" the space Shares should contain these entries:
|
||||
| <path> |
|
||||
And the JSON data of the response should match
|
||||
"""
|
||||
{
|
||||
@@ -181,13 +183,9 @@ Feature: Send a sharing invitations
|
||||
| permissions-role | resource-type | path |
|
||||
| Viewer | file | /textfile1.txt |
|
||||
| File Editor | file | /textfile1.txt |
|
||||
| Co Owner | file | /textfile1.txt |
|
||||
| Manager | file | /textfile1.txt |
|
||||
| Viewer | folder | FolderToShare |
|
||||
| Editor | folder | FolderToShare |
|
||||
| Co Owner | folder | FolderToShare |
|
||||
| Uploader | folder | FolderToShare |
|
||||
| Manager | folder | FolderToShare |
|
||||
|
||||
|
||||
Scenario Outline: send share invitation for a file to user with different permissions
|
||||
@@ -265,7 +263,6 @@ Feature: Send a sharing invitations
|
||||
Examples:
|
||||
| permissionsAction |
|
||||
| permissions/create |
|
||||
| children/create |
|
||||
| upload/create |
|
||||
| path/read |
|
||||
| quota/read |
|
||||
@@ -275,11 +272,9 @@ Feature: Send a sharing invitations
|
||||
| versions/read |
|
||||
| deleted/read |
|
||||
| basic/read |
|
||||
| path/update |
|
||||
| versions/update |
|
||||
| deleted/update |
|
||||
| permissions/update |
|
||||
| standard/delete |
|
||||
| permissions/delete |
|
||||
| deleted/delete |
|
||||
| permissions/deny |
|
||||
@@ -461,7 +456,6 @@ Feature: Send a sharing invitations
|
||||
Examples:
|
||||
| permissionsAction |
|
||||
| permissions/create |
|
||||
| children/create |
|
||||
| upload/create |
|
||||
| path/read |
|
||||
| quota/read |
|
||||
@@ -471,11 +465,9 @@ Feature: Send a sharing invitations
|
||||
| versions/read |
|
||||
| deleted/read |
|
||||
| basic/read |
|
||||
| path/update |
|
||||
| versions/update |
|
||||
| deleted/update |
|
||||
| permissions/update |
|
||||
| standard/delete |
|
||||
| permissions/delete |
|
||||
| deleted/delete |
|
||||
| permissions/deny |
|
||||
@@ -667,13 +659,9 @@ Feature: Send a sharing invitations
|
||||
| permissions-role | resource-type | path |
|
||||
| Viewer | file | /textfile1.txt |
|
||||
| File Editor | file | /textfile1.txt |
|
||||
| Co Owner | file | /textfile1.txt |
|
||||
| Manager | file | /textfile1.txt |
|
||||
| Viewer | folder | FolderToShare |
|
||||
| Editor | folder | FolderToShare |
|
||||
| Co Owner | folder | FolderToShare |
|
||||
| Uploader | folder | FolderToShare |
|
||||
| Manager | folder | FolderToShare |
|
||||
|
||||
|
||||
Scenario Outline: send share invitation with expiration date to group with different roles
|
||||
@@ -767,13 +755,9 @@ Feature: Send a sharing invitations
|
||||
| permissions-role | resource-type | path |
|
||||
| Viewer | file | /textfile1.txt |
|
||||
| File Editor | file | /textfile1.txt |
|
||||
| Co Owner | file | /textfile1.txt |
|
||||
| Manager | file | /textfile1.txt |
|
||||
| Viewer | folder | FolderToShare |
|
||||
| Editor | folder | FolderToShare |
|
||||
| Co Owner | folder | FolderToShare |
|
||||
| Uploader | folder | FolderToShare |
|
||||
| Manager | folder | FolderToShare |
|
||||
|
||||
@issue-7962
|
||||
Scenario Outline: send share invitation to disabled user
|
||||
@@ -854,13 +838,9 @@ Feature: Send a sharing invitations
|
||||
| permissions-role | resource-type | path |
|
||||
| Viewer | file | /textfile1.txt |
|
||||
| File Editor | file | /textfile1.txt |
|
||||
| Co Owner | file | /textfile1.txt |
|
||||
| Manager | file | /textfile1.txt |
|
||||
| Viewer | folder | FolderToShare |
|
||||
| Editor | folder | FolderToShare |
|
||||
| Co Owner | folder | FolderToShare |
|
||||
| Uploader | folder | FolderToShare |
|
||||
| Manager | folder | FolderToShare |
|
||||
|
||||
|
||||
Scenario Outline: send sharing invitation to a deleted group with different roles
|
||||
@@ -917,13 +897,9 @@ Feature: Send a sharing invitations
|
||||
| permissions-role | resource-type | path |
|
||||
| Viewer | file | /textfile1.txt |
|
||||
| File Editor | file | /textfile1.txt |
|
||||
| Co Owner | file | /textfile1.txt |
|
||||
| Manager | file | /textfile1.txt |
|
||||
| Viewer | folder | FolderToShare |
|
||||
| Editor | folder | FolderToShare |
|
||||
| Co Owner | folder | FolderToShare |
|
||||
| Uploader | folder | FolderToShare |
|
||||
| Manager | folder | FolderToShare |
|
||||
|
||||
|
||||
Scenario Outline: send share invitation to deleted user
|
||||
@@ -972,13 +948,9 @@ Feature: Send a sharing invitations
|
||||
| permissions-role | resource-type | path |
|
||||
| Viewer | file | /textfile1.txt |
|
||||
| File Editor | file | /textfile1.txt |
|
||||
| Co Owner | file | /textfile1.txt |
|
||||
| Manager | file | /textfile1.txt |
|
||||
| Viewer | folder | FolderToShare |
|
||||
| Editor | folder | FolderToShare |
|
||||
| Co Owner | folder | FolderToShare |
|
||||
| Uploader | folder | FolderToShare |
|
||||
| Manager | folder | FolderToShare |
|
||||
|
||||
|
||||
Scenario Outline: try to send sharing invitation to multiple groups
|
||||
@@ -1039,8 +1011,6 @@ Feature: Send a sharing invitations
|
||||
| permissions-role | resource-type | path |
|
||||
| Viewer | file | /textfile1.txt |
|
||||
| File Editor | file | /textfile1.txt |
|
||||
| Co Owner | file | /textfile1.txt |
|
||||
| Manager | file | /textfile1.txt |
|
||||
| Viewer | folder | FolderToShare |
|
||||
| Editor | folder | FolderToShare |
|
||||
| Co Owner | folder | FolderToShare |
|
||||
@@ -1104,10 +1074,121 @@ Feature: Send a sharing invitations
|
||||
| permissions-role | resource-type | path |
|
||||
| Viewer | file | /textfile1.txt |
|
||||
| File Editor | file | /textfile1.txt |
|
||||
| Co Owner | file | /textfile1.txt |
|
||||
| Manager | file | /textfile1.txt |
|
||||
| Viewer | folder | FolderToShare |
|
||||
| Editor | folder | FolderToShare |
|
||||
| Co Owner | folder | FolderToShare |
|
||||
| Uploader | folder | FolderToShare |
|
||||
| Manager | folder | FolderToShare |
|
||||
|
||||
|
||||
Scenario Outline: send sharing invitation to non-existing group
|
||||
Given user "Alice" has uploaded file with content "to share" to "/textfile1.txt"
|
||||
And user "Alice" has created folder "FolderToShare"
|
||||
When user "Alice" sends the following share invitation using the Graph API:
|
||||
| resourceType | <resource-type> |
|
||||
| resource | <path> |
|
||||
| space | Personal |
|
||||
| sharee | nonExistentGroup |
|
||||
| shareType | group |
|
||||
| permissionsRole | <permissions-role> |
|
||||
Then the HTTP status code should be "400"
|
||||
And the JSON data of the response should match
|
||||
"""
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"error"
|
||||
],
|
||||
"properties": {
|
||||
"error": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"code",
|
||||
"message"
|
||||
],
|
||||
"properties": {
|
||||
"code": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"generalException"
|
||||
]
|
||||
},
|
||||
"message": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"itemNotFound: not found"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
Examples:
|
||||
| permissions-role | resource-type | path |
|
||||
| Viewer | file | /textfile1.txt |
|
||||
| File Editor | file | /textfile1.txt |
|
||||
| Viewer | folder | FolderToShare |
|
||||
| Editor | folder | FolderToShare |
|
||||
| Uploader | folder | FolderToShare |
|
||||
|
||||
|
||||
Scenario Outline: send sharing invitation to already shared group
|
||||
Given user "Carol" has been created with default attributes and without skeleton files
|
||||
And group "grp1" has been created
|
||||
And the following users have been added to the following groups
|
||||
| username | groupname |
|
||||
| Brian | grp1 |
|
||||
| Carol | grp1 |
|
||||
And user "Alice" has uploaded file with content "to share" to "/textfile1.txt"
|
||||
And user "Alice" has created folder "FolderToShare"
|
||||
And user "Alice" has sent the following share invitation:
|
||||
| resourceType | <resource-type> |
|
||||
| resource | <path> |
|
||||
| space | Personal |
|
||||
| sharee | grp1 |
|
||||
| shareType | group |
|
||||
| permissionsRole | <permissions-role> |
|
||||
When user "Alice" sends the following share invitation using the Graph API:
|
||||
| resourceType | <resource-type> |
|
||||
| resource | <path> |
|
||||
| space | Personal |
|
||||
| sharee | grp1 |
|
||||
| shareType | group |
|
||||
| permissionsRole | <permissions-role> |
|
||||
Then the HTTP status code should be "409"
|
||||
And the JSON data of the response should match
|
||||
"""
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"error"
|
||||
],
|
||||
"properties": {
|
||||
"error": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"code",
|
||||
"message"
|
||||
],
|
||||
"properties": {
|
||||
"code": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"nameAlreadyExists"
|
||||
]
|
||||
},
|
||||
"message": {
|
||||
"type": "string",
|
||||
"pattern": "^error creating share: error: already exists:.*$"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
Examples:
|
||||
| permissions-role | resource-type | path |
|
||||
| Viewer | file | /textfile1.txt |
|
||||
| File Editor | file | /textfile1.txt |
|
||||
| Viewer | folder | FolderToShare |
|
||||
| Editor | folder | FolderToShare |
|
||||
| Uploader | folder | FolderToShare |
|
||||
|
||||
@@ -205,8 +205,10 @@ Feature: Change data of space
|
||||
"mimeType"
|
||||
],
|
||||
"properties": {
|
||||
"type": "string",
|
||||
"enum": ["text/markdown"]
|
||||
"mimeType": {
|
||||
"type": "string",
|
||||
"enum": ["text/markdown"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"id": {
|
||||
@@ -298,8 +300,10 @@ Feature: Change data of space
|
||||
"mimeType"
|
||||
],
|
||||
"properties": {
|
||||
"type": "string",
|
||||
"enum": ["<mimeType>"]
|
||||
"mimeType": {
|
||||
"type": "string",
|
||||
"enum": ["<mimeType>"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"id": {
|
||||
@@ -393,8 +397,10 @@ Feature: Change data of space
|
||||
"mimeType"
|
||||
],
|
||||
"properties": {
|
||||
"type": "string",
|
||||
"enum": ["text/markdown"]
|
||||
"mimeType": {
|
||||
"type": "string",
|
||||
"enum": ["text/markdown"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"id": {
|
||||
@@ -479,8 +485,10 @@ Feature: Change data of space
|
||||
"mimeType"
|
||||
],
|
||||
"properties": {
|
||||
"type": "string",
|
||||
"enum": ["image/png"]
|
||||
"mimeType": {
|
||||
"type": "string",
|
||||
"enum": ["image/png"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"id": {
|
||||
|
||||
@@ -41,28 +41,28 @@ Feature: create space
|
||||
"enum": ["Project Mars"]
|
||||
},
|
||||
"driveType": {
|
||||
"type": "string",
|
||||
"type": "string",
|
||||
"enum": ["project"]
|
||||
},
|
||||
"driveAlias": {
|
||||
"type": "string",
|
||||
"type": "string",
|
||||
"enum": ["project/project-mars"]
|
||||
},
|
||||
"id": {
|
||||
"type": "string",
|
||||
"type": "string",
|
||||
"enum": ["%space_id%"]
|
||||
},
|
||||
"quota": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"type": "object",
|
||||
"required": [
|
||||
"total"
|
||||
],
|
||||
"properties": {
|
||||
"state": {
|
||||
"type": "number",
|
||||
"enum": [1000000000]
|
||||
}
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"total": {
|
||||
"type": "number",
|
||||
"enum": [1000000000]
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"type": "object",
|
||||
@@ -70,11 +70,11 @@ Feature: create space
|
||||
"webDavUrl"
|
||||
],
|
||||
"properties": {
|
||||
"webDavUrl": {
|
||||
"type": "string",
|
||||
"enum": ["%base_url%/dav/spaces/%space_id%"]
|
||||
}
|
||||
}
|
||||
"webDavUrl": {
|
||||
"type": "string",
|
||||
"enum": ["%base_url%/dav/spaces/%space_id%"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"webUrl": {
|
||||
"type": "string",
|
||||
@@ -111,24 +111,24 @@ Feature: create space
|
||||
"enum": ["Project Venus"]
|
||||
},
|
||||
"driveType": {
|
||||
"type": "string",
|
||||
"type": "string",
|
||||
"enum": ["project"]
|
||||
},
|
||||
"id": {
|
||||
"type": "string",
|
||||
"type": "string",
|
||||
"enum": ["%space_id%"]
|
||||
},
|
||||
"quota": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"type": "object",
|
||||
"required": [
|
||||
"total"
|
||||
],
|
||||
"properties": {
|
||||
"state": {
|
||||
"type": "number",
|
||||
"enum": [2000]
|
||||
}
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"total": {
|
||||
"type": "number",
|
||||
"enum": [2000]
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"type": "object",
|
||||
@@ -136,11 +136,11 @@ Feature: create space
|
||||
"webDavUrl"
|
||||
],
|
||||
"properties": {
|
||||
"webDavUrl": {
|
||||
"type": "string",
|
||||
"enum": ["%base_url%/dav/spaces/%space_id%"]
|
||||
}
|
||||
}
|
||||
"webDavUrl": {
|
||||
"type": "string",
|
||||
"enum": ["%base_url%/dav/spaces/%space_id%"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"webUrl": {
|
||||
"type": "string",
|
||||
|
||||
@@ -167,11 +167,11 @@ Feature: List and create spaces
|
||||
"enum": ["my project"]
|
||||
},
|
||||
"driveType": {
|
||||
"type": "string",
|
||||
"type": "string",
|
||||
"enum": ["project"]
|
||||
},
|
||||
"id": {
|
||||
"type": "string",
|
||||
"type": "string",
|
||||
"enum": ["%space_id%"]
|
||||
}
|
||||
}
|
||||
@@ -227,13 +227,17 @@ Feature: List and create spaces
|
||||
"user"
|
||||
],
|
||||
"properties": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"id"
|
||||
],
|
||||
"properties": {
|
||||
"type": "string",
|
||||
"enum": ["%user_id%"]
|
||||
"user": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"id"
|
||||
],
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"enum": ["%user_id%"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -343,6 +347,10 @@ Feature: List and create spaces
|
||||
"enum": ["%base_url%/dav/spaces/%space_id%"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"webUrl": {
|
||||
"type": "string",
|
||||
"enum": ["%base_url%/f/%space_id%"]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -454,28 +462,28 @@ Feature: List and create spaces
|
||||
"enum": ["Shares"]
|
||||
},
|
||||
"driveType": {
|
||||
"type": "string",
|
||||
"type": "string",
|
||||
"enum": ["virtual"]
|
||||
},
|
||||
"driveAlias": {
|
||||
"type": "string",
|
||||
"type": "string",
|
||||
"enum": ["virtual/shares"]
|
||||
},
|
||||
"id": {
|
||||
"type": "string",
|
||||
"type": "string",
|
||||
"enum": ["%space_id%"]
|
||||
},
|
||||
"quota": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"type": "object",
|
||||
"required": [
|
||||
"state"
|
||||
],
|
||||
"properties": {
|
||||
"state": {
|
||||
"type": "string",
|
||||
"enum": ["normal"]
|
||||
}
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"state": {
|
||||
"type": "string",
|
||||
"enum": ["normal"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"type": "object",
|
||||
@@ -484,15 +492,15 @@ Feature: List and create spaces
|
||||
"webDavUrl"
|
||||
],
|
||||
"properties": {
|
||||
"eTag": {
|
||||
"type": "string",
|
||||
"enum": ["%space_etag%"]
|
||||
},
|
||||
"webDavUrl": {
|
||||
"type": "string",
|
||||
"enum": ["%base_url%/dav/spaces/%space_id%"]
|
||||
}
|
||||
}
|
||||
"eTag": {
|
||||
"type": "string",
|
||||
"enum": ["%space_etag%"]
|
||||
},
|
||||
"webDavUrl": {
|
||||
"type": "string",
|
||||
"enum": ["%base_url%/dav/spaces/%space_id%"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"webUrl": {
|
||||
"type": "string",
|
||||
|
||||
@@ -39,11 +39,11 @@ Feature: Space management
|
||||
"enum": ["Project"]
|
||||
},
|
||||
"driveType": {
|
||||
"type": "string",
|
||||
"type": "string",
|
||||
"enum": ["project"]
|
||||
},
|
||||
"id": {
|
||||
"type": "string",
|
||||
"type": "string",
|
||||
"enum": ["%space_id%"]
|
||||
}
|
||||
}
|
||||
@@ -70,11 +70,11 @@ Feature: Space management
|
||||
"enum": ["Alice Hansen"]
|
||||
},
|
||||
"driveType": {
|
||||
"type": "string",
|
||||
"type": "string",
|
||||
"enum": ["personal"]
|
||||
},
|
||||
"id": {
|
||||
"type": "string",
|
||||
"type": "string",
|
||||
"enum": ["%space_id%"]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,3 +39,82 @@ Feature: upload resources using TUS protocol
|
||||
Then for user "Alice" the space "Alice Hansen" should contain these entries:
|
||||
| test.txt |
|
||||
| upload.txt |
|
||||
|
||||
|
||||
Scenario Outline: upload a zero-byte file inside a shared folder
|
||||
Given using <dav-path-version> DAV path
|
||||
And user "Brian" has been created with default attributes and without skeleton files
|
||||
And user "Alice" has created folder "testFolder"
|
||||
And user "Alice" has shared folder "testFolder" with user "Brian" with permissions "all"
|
||||
When user "Brian" uploads file "filesForUpload/zerobyte.txt" to "Shares/testFolder/textfile.txt" using the TUS protocol on the WebDAV API
|
||||
Then the content of file "Shares/testFolder/textfile.txt" for user "Brian" should be ""
|
||||
And the content of file "testFolder/textfile.txt" for user "Alice" should be ""
|
||||
Examples:
|
||||
| dav-path-version |
|
||||
| old |
|
||||
| new |
|
||||
|
||||
|
||||
Scenario: upload a zero-byte file inside a shared folder (spaces dav path)
|
||||
Given using spaces DAV path
|
||||
And user "Brian" has been created with default attributes and without skeleton files
|
||||
And user "Alice" has created folder "testFolder"
|
||||
And user "Alice" has shared folder "testFolder" with user "Brian" with permissions "all"
|
||||
When user "Brian" uploads a file from "filesForUpload/zerobyte.txt" to "testFolder/textfile.txt" via TUS inside of the space "Shares" using the WebDAV API
|
||||
Then for user "Brian" the content of the file "testFolder/textfile.txt" of the space "Shares" should be ""
|
||||
And for user "Alice" the content of the file "testFolder/textfile.txt" of the space "Personal" should be ""
|
||||
|
||||
|
||||
Scenario: upload a zero-byte file inside a project space
|
||||
Given using spaces DAV path
|
||||
And the administrator has assigned the role "Space Admin" to user "Alice" using the Graph API
|
||||
And user "Alice" has created a space "new-space" with the default quota using the Graph API
|
||||
When user "Alice" uploads a file from "filesForUpload/zerobyte.txt" to "textfile.txt" via TUS inside of the space "new-space" using the WebDAV API
|
||||
Then for user "Alice" the content of the file "textfile.txt" of the space "new-space" should be ""
|
||||
|
||||
@issue-8003
|
||||
Scenario Outline: replace a shared file with zero-byte file
|
||||
Given using <dav-path-version> DAV path
|
||||
And user "Brian" has been created with default attributes and without skeleton files
|
||||
And user "Alice" has uploaded file with content "This is TUS upload" to "textfile.txt"
|
||||
And user "Alice" has shared file "textfile.txt" with user "Brian" with permissions "read,update"
|
||||
When user "Brian" uploads file "filesForUpload/zerobyte.txt" to "Shares/textfile.txt" using the TUS protocol on the WebDAV API
|
||||
Then the content of file "Shares/textfile.txt" for user "Brian" should be ""
|
||||
And the content of file "textfile.txt" for user "Alice" should be ""
|
||||
Examples:
|
||||
| dav-path-version |
|
||||
| old |
|
||||
| new |
|
||||
|
||||
@issue-8003
|
||||
Scenario: replace a shared file with zero-byte file (spaces dav path)
|
||||
Given using spaces DAV path
|
||||
And user "Brian" has been created with default attributes and without skeleton files
|
||||
And user "Alice" has uploaded file with content "This is TUS upload" to "textfile.txt"
|
||||
And user "Alice" has shared file "textfile.txt" with user "Brian" with permissions "read,update"
|
||||
When user "Brian" uploads a file from "filesForUpload/zerobyte.txt" to "textfile.txt" via TUS inside of the space "Shares" using the WebDAV API
|
||||
Then for user "Brian" the content of the file "textfile.txt" of the space "Shares" should be ""
|
||||
And for user "Alice" the content of the file "textfile.txt" of the space "Personal" should be ""
|
||||
|
||||
@issue-8003
|
||||
Scenario: replace a file inside a project space with zero-byte file
|
||||
Given using spaces DAV path
|
||||
And the administrator has assigned the role "Space Admin" to user "Alice" using the Graph API
|
||||
And user "Alice" has created a space "new-space" with the default quota using the Graph API
|
||||
And user "Alice" has uploaded a file inside space "new-space" with content "This is TUS upload" to "textfile.txt"
|
||||
When user "Alice" uploads a file from "filesForUpload/zerobyte.txt" to "textfile.txt" via TUS inside of the space "new-space" using the WebDAV API
|
||||
Then for user "Alice" the content of the file "textfile.txt" of the space "new-space" should be ""
|
||||
|
||||
@issue-8003
|
||||
Scenario: replace a file inside a shared project space with zero-byte file
|
||||
Given using spaces DAV path
|
||||
And user "Brian" has been created with default attributes and without skeleton files
|
||||
And the administrator has assigned the role "Space Admin" to user "Alice" using the Graph API
|
||||
And user "Alice" has created a space "new-space" with the default quota using the Graph API
|
||||
And user "Alice" has uploaded a file inside space "new-space" with content "This is TUS upload" to "textfile.txt"
|
||||
And user "Alice" has shared a space "new-space" with settings:
|
||||
| shareWith | Brian |
|
||||
| role | editor |
|
||||
When user "Brian" uploads a file from "filesForUpload/zerobyte.txt" to "textfile.txt" via TUS inside of the space "new-space" using the WebDAV API
|
||||
Then for user "Brian" the content of the file "textfile.txt" of the space "new-space" should be ""
|
||||
And for user "Alice" the content of the file "textfile.txt" of the space "new-space" should be ""
|
||||
@@ -1937,122 +1937,6 @@ trait Provisioning {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @When /^the administrator changes the quota of user "([^"]*)" to "([^"]*)" using the provisioning API$/
|
||||
*
|
||||
* @param string $user
|
||||
* @param string $quota
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function adminChangesTheQuotaOfUserUsingTheProvisioningApi(
|
||||
string $user,
|
||||
string $quota
|
||||
):void {
|
||||
$result = UserHelper::editUser(
|
||||
$this->getBaseUrl(),
|
||||
$this->getActualUsername($user),
|
||||
'quota',
|
||||
$quota,
|
||||
$this->getAdminUsername(),
|
||||
$this->getAdminPassword(),
|
||||
$this->getStepLineRef(),
|
||||
$this->ocsApiVersion
|
||||
);
|
||||
$this->response = $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @Given /^the administrator has (?:changed|set) the quota of user "([^"]*)" to "([^"]*)"$/
|
||||
*
|
||||
* @param string $user
|
||||
* @param string $quota
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function adminHasChangedTheQuotaOfUserTo(
|
||||
string $user,
|
||||
string $quota
|
||||
):void {
|
||||
$user = $this->getActualUsername($user);
|
||||
$this->adminChangesTheQuotaOfUserUsingTheProvisioningApi(
|
||||
$user,
|
||||
$quota
|
||||
);
|
||||
$this->theHTTPStatusCodeShouldBe(
|
||||
200,
|
||||
"could not change quota of user $user"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $requestingUser
|
||||
* @param string $targetUser
|
||||
* @param string $quota
|
||||
*
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function userChangeQuotaOfUserUsingProvisioningApi(
|
||||
string $requestingUser,
|
||||
string $targetUser,
|
||||
string $quota
|
||||
):ResponseInterface {
|
||||
return UserHelper::editUser(
|
||||
$this->getBaseUrl(),
|
||||
$this->getActualUsername($targetUser),
|
||||
'quota',
|
||||
$quota,
|
||||
$this->getActualUsername($requestingUser),
|
||||
$this->getPasswordForUser($requestingUser),
|
||||
$this->getStepLineRef(),
|
||||
$this->ocsApiVersion
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @When /^user "([^"]*)" changes the quota of user "([^"]*)" to "([^"]*)" using the provisioning API$/
|
||||
*
|
||||
* @param string $requestingUser
|
||||
* @param string $targetUser
|
||||
* @param string $quota
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function userChangesTheQuotaOfUserUsingTheProvisioningApi(
|
||||
string $requestingUser,
|
||||
string $targetUser,
|
||||
string $quota
|
||||
):void {
|
||||
$this->response = $this->userChangeQuotaOfUserUsingProvisioningApi(
|
||||
$requestingUser,
|
||||
$targetUser,
|
||||
$quota
|
||||
);
|
||||
$this->pushToLastStatusCodesArrays();
|
||||
}
|
||||
|
||||
/**
|
||||
* @Given /^user "([^"]*)" has changed the quota of user "([^"]*)" to "([^"]*)"$/
|
||||
*
|
||||
* @param string $requestingUser
|
||||
* @param string $targetUser
|
||||
* @param string $quota
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function userHasChangedTheQuotaOfUserUsingTheProvisioningApi(
|
||||
string $requestingUser,
|
||||
string $targetUser,
|
||||
string $quota
|
||||
):void {
|
||||
$response = $this->userChangeQuotaOfUserUsingProvisioningApi(
|
||||
$requestingUser,
|
||||
$targetUser,
|
||||
$quota
|
||||
);
|
||||
$this->theHTTPStatusCodeShouldBeBetween(200, 299, $response);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $user
|
||||
*
|
||||
@@ -4450,68 +4334,6 @@ trait Provisioning {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @When the administrator sets the quota of user :user to :quota using the provisioning API
|
||||
*
|
||||
* @param string $user
|
||||
* @param string $quota
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function adminSetsUserQuotaToUsingTheProvisioningApi(string $user, string $quota):void {
|
||||
$user = $this->getActualUsername($user);
|
||||
$body
|
||||
= [
|
||||
'key' => 'quota',
|
||||
'value' => $quota,
|
||||
];
|
||||
|
||||
$this->response = OcsApiHelper::sendRequest(
|
||||
$this->getBaseUrl(),
|
||||
$this->getAdminUsername(),
|
||||
$this->getAdminPassword(),
|
||||
"PUT",
|
||||
"/cloud/users/$user",
|
||||
$this->getStepLineRef(),
|
||||
$body
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Given the quota of user :user has been set to :quota
|
||||
*
|
||||
* @param string $user
|
||||
* @param string $quota
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function theQuotaOfUserHasBeenSetTo(string $user, string $quota):void {
|
||||
$this->adminSetsUserQuotaToUsingTheProvisioningApi($user, $quota);
|
||||
$this->theHTTPStatusCodeShouldBe(200);
|
||||
}
|
||||
|
||||
/**
|
||||
* @When the administrator gives unlimited quota to user :user using the provisioning API
|
||||
*
|
||||
* @param string $user
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function adminGivesUnlimitedQuotaToUserUsingTheProvisioningApi(string $user):void {
|
||||
$this->adminSetsUserQuotaToUsingTheProvisioningApi($user, 'none');
|
||||
}
|
||||
|
||||
/**
|
||||
* @Given user :user has been given unlimited quota
|
||||
*
|
||||
* @param string $user
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function userHasBeenGivenUnlimitedQuota(string $user):void {
|
||||
$this->theQuotaOfUserHasBeenSetTo($user, 'none');
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then /^the user attributes returned by the API should include$/
|
||||
*
|
||||
|
||||
@@ -23,7 +23,9 @@ use Behat\Behat\Context\Context;
|
||||
use Behat\Behat\Hook\Scope\BeforeScenarioScope;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use TestHelpers\GraphHelper;
|
||||
use TestHelpers\WebDavHelper;
|
||||
use Behat\Gherkin\Node\TableNode;
|
||||
use PHPUnit\Framework\Assert;
|
||||
|
||||
require_once 'bootstrap.php';
|
||||
|
||||
@@ -139,9 +141,20 @@ class SharingNgContext implements Context {
|
||||
$rows = $table->getRowsHash();
|
||||
$spaceId = ($this->spacesContext->getSpaceByName($user, $rows['space']))["id"];
|
||||
|
||||
$itemId = ($rows['resourceType'] === 'folder')
|
||||
? $this->spacesContext->getResourceId($user, $rows['space'], $rows['resource'])
|
||||
: $this->spacesContext->getFileId($user, $rows['space'], $rows['resource']);
|
||||
// for resharing a resource, "item-id" in API endpoint takes shareMountId
|
||||
if ($rows['space'] === 'Shares') {
|
||||
$itemId = GraphHelper::getShareMountId(
|
||||
$this->featureContext->getBaseUrl(),
|
||||
$this->featureContext->getStepLineRef(),
|
||||
$user,
|
||||
$this->featureContext->getPasswordForUser($user),
|
||||
$rows['resource']
|
||||
);
|
||||
} else {
|
||||
$itemId = ($rows['resourceType'] === 'folder')
|
||||
? $this->spacesContext->getResourceId($user, $rows['space'], $rows['resource'])
|
||||
: $this->spacesContext->getFileId($user, $rows['space'], $rows['resource']);
|
||||
}
|
||||
|
||||
$sharees = array_map('trim', explode(',', $rows['sharee']));
|
||||
$shareTypes = array_map('trim', explode(',', $rows['shareType']));
|
||||
@@ -149,9 +162,10 @@ class SharingNgContext implements Context {
|
||||
$shareeIds = [];
|
||||
foreach ($sharees as $index => $sharee) {
|
||||
$shareType = $shareTypes[$index];
|
||||
$shareeIds[] = ($shareType === 'user')
|
||||
// for non-exiting group or user, generate random id
|
||||
$shareeIds[] = (($shareType === 'user')
|
||||
? $this->featureContext->getAttributeOfCreatedUser($sharee, 'id')
|
||||
: $this->featureContext->getAttributeOfCreatedGroup($sharee, 'id');
|
||||
: $this->featureContext->getAttributeOfCreatedGroup($sharee, 'id')) ?: WebDavHelper::generateUUIDv4();
|
||||
}
|
||||
|
||||
$permissionsRole = $rows['permissionsRole'] ?? null;
|
||||
@@ -424,4 +438,34 @@ class SharingNgContext implements Context {
|
||||
$this->removeSharePermission($sharer, 'link', $resourceType, $resource, $space)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then /^for user "([^"]*)" the space Shares should (not|)\s?contain these (files|entries):$/
|
||||
*
|
||||
* @param string $user
|
||||
* @param string $shouldOrNot
|
||||
* @param TableNode $table
|
||||
*
|
||||
* @return void
|
||||
* @throws Exception
|
||||
*/
|
||||
public function forUserTheSpaceSharesShouldContainTheseEntries(string $user, string $shouldOrNot, TableNode $table): void {
|
||||
$should = $shouldOrNot !== 'not';
|
||||
$rows = $table->getRows();
|
||||
$response = GraphHelper::getSharesSharedWithMe(
|
||||
$this->featureContext->getBaseUrl(),
|
||||
$this->featureContext->getStepLineRef(),
|
||||
$user,
|
||||
$this->featureContext->getPasswordForUser($user)
|
||||
);
|
||||
$contents = \json_decode($response->getBody()->getContents(), true);
|
||||
|
||||
$fileFound = empty(array_diff(array_map(fn ($row) => trim($row[0], '/'), $rows), array_column($contents['value'], 'name')));
|
||||
|
||||
$assertMessage = $should
|
||||
? "Response does not contain the entry."
|
||||
: "Response does contain the entry but should not.";
|
||||
|
||||
Assert::assertSame($should, $fileFound, $assertMessage);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -544,19 +544,22 @@ class SpacesContext implements Context {
|
||||
/**
|
||||
* @param string $user
|
||||
* @param string $query
|
||||
* @param array $headers
|
||||
*
|
||||
* @return ResponseInterface
|
||||
*
|
||||
* @throws GuzzleException
|
||||
* @throws Exception
|
||||
*/
|
||||
public function listAllAvailableSpacesOfUser(string $user, string $query = ''): ResponseInterface {
|
||||
public function listAllAvailableSpacesOfUser(string $user, string $query = '', array $headers = []): ResponseInterface {
|
||||
$response = GraphHelper::getMySpaces(
|
||||
$this->featureContext->getBaseUrl(),
|
||||
$user,
|
||||
$this->featureContext->getPasswordForUser($user),
|
||||
"?" . $query,
|
||||
$this->featureContext->getStepLineRef()
|
||||
$this->featureContext->getStepLineRef(),
|
||||
[],
|
||||
$headers
|
||||
);
|
||||
$this->rememberTheAvailableSpaces($response);
|
||||
return $response;
|
||||
@@ -578,6 +581,28 @@ class SpacesContext implements Context {
|
||||
$this->featureContext->setResponse($this->listAllAvailableSpacesOfUser($user, $query));
|
||||
}
|
||||
|
||||
/**
|
||||
* @When /^user "([^"]*)" lists all available spaces with headers using the Graph API$/
|
||||
*
|
||||
* @param string $user
|
||||
* @param TableNode $headersTable
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @throws GuzzleException
|
||||
* @throws Exception
|
||||
*/
|
||||
public function theUserListsAllHisAvailableSpacesWithHeadersUsingTheGraphApi(string $user, TableNode $headersTable): void {
|
||||
$this->featureContext->verifyTableNodeColumns(
|
||||
$headersTable,
|
||||
['header', 'value']
|
||||
);
|
||||
foreach ($headersTable as $row) {
|
||||
$headers[$row['header']] = $row ['value'];
|
||||
}
|
||||
$this->featureContext->setResponse($this->listAllAvailableSpacesOfUser($user, '', $headers));
|
||||
}
|
||||
|
||||
/**
|
||||
* The method is used on the administration setting tab, which only the Admin user and the Space admin user have access to
|
||||
*
|
||||
@@ -1807,12 +1832,14 @@ class SpacesContext implements Context {
|
||||
|
||||
/**
|
||||
* @When /^user "([^"]*)" copies (?:file|folder) "([^"]*)" from space "([^"]*)" to "([^"]*)" inside space "([^"]*)" using the WebDAV API$/
|
||||
* @When /^user "([^"]*)" copies (?:file|folder) "([^"]*)" from space "([^"]*)" to "([^"]*)" inside space "([^"]*)"(?: with following headers) using the WebDAV API$/
|
||||
*
|
||||
* @param string $user
|
||||
* @param string $fileSource
|
||||
* @param string $fromSpaceName
|
||||
* @param string $fileDestination
|
||||
* @param string $toSpaceName
|
||||
* @param TableNode|null $table
|
||||
*
|
||||
* @return void
|
||||
* @throws GuzzleException
|
||||
@@ -1822,10 +1849,22 @@ class SpacesContext implements Context {
|
||||
string $fileSource,
|
||||
string $fromSpaceName,
|
||||
string $fileDestination,
|
||||
string $toSpaceName
|
||||
string $toSpaceName,
|
||||
TableNode $table = null
|
||||
):void {
|
||||
$space = $this->getSpaceByName($user, $fromSpaceName);
|
||||
$headers['Destination'] = $this->destinationHeaderValueWithSpaceName($user, $fileDestination, $toSpaceName);
|
||||
|
||||
if ($table !== null) {
|
||||
$this->featureContext->verifyTableNodeColumns(
|
||||
$table,
|
||||
['header', 'value']
|
||||
);
|
||||
foreach ($table as $row) {
|
||||
$headers[$row['header']] = $this->featureContext->substituteInLineCodes($row['value']);
|
||||
}
|
||||
}
|
||||
|
||||
$fullUrl = $space["root"]["webDavUrl"] . '/' . ltrim($fileSource, "/");
|
||||
$this->featureContext->setResponse($this->copyFilesAndFoldersRequest($user, $fullUrl, $headers));
|
||||
}
|
||||
@@ -3514,17 +3553,44 @@ class SpacesContext implements Context {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @When /^user "([^"]*)" sends PROPFIND request to space "([^"]*)" with headers using the WebDAV API$/
|
||||
*
|
||||
* @param string $user
|
||||
* @param string $spaceName
|
||||
* @param TableNode $headersTable
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @throws JsonException
|
||||
*
|
||||
* @throws GuzzleException
|
||||
*/
|
||||
public function userSendsPropfindRequestToSpaceWithHeaders(string $user, string $spaceName, $headersTable): void {
|
||||
$this->featureContext->verifyTableNodeColumns(
|
||||
$headersTable,
|
||||
['header', 'value']
|
||||
);
|
||||
foreach ($headersTable as $row) {
|
||||
$headers[$row['header']] = $row ['value'];
|
||||
}
|
||||
$this->featureContext->setResponse(
|
||||
$this->sendPropfindRequestToSpace($user, $spaceName, '', $headers)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $user
|
||||
* @param string $spaceName
|
||||
* @param string|null $resource
|
||||
* @param array|null $headers
|
||||
*
|
||||
* @return ResponseInterface
|
||||
* @throws GuzzleException
|
||||
*
|
||||
* @throws JsonException
|
||||
*/
|
||||
public function sendPropfindRequestToSpace(string $user, string $spaceName, ?string $resource = ""): ResponseInterface {
|
||||
public function sendPropfindRequestToSpace(string $user, string $spaceName, ?string $resource = "", ?array $headers = []): ResponseInterface {
|
||||
$this->setSpaceIDByName($user, $spaceName);
|
||||
$properties = ['oc:permissions','oc:file-parent','oc:fileid','oc:share-types','oc:privatelink','d:resourcetype','oc:size','oc:name','d:getcontenttype','oc:tags','d:lockdiscovery','d:activelock'];
|
||||
return WebDavHelper::propfind(
|
||||
@@ -3536,7 +3602,9 @@ class SpacesContext implements Context {
|
||||
$this->featureContext->getStepLineRef(),
|
||||
"0",
|
||||
"files",
|
||||
WebDavHelper::DAV_VERSION_SPACES
|
||||
WebDavHelper::DAV_VERSION_SPACES,
|
||||
"",
|
||||
$headers
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -209,7 +209,8 @@ class TUSContext implements Context {
|
||||
|
||||
$client = new Client(
|
||||
$this->featureContext->getBaseUrl(),
|
||||
['verify' => false,
|
||||
[
|
||||
'verify' => false,
|
||||
'headers' => $headers
|
||||
]
|
||||
);
|
||||
@@ -230,6 +231,8 @@ class TUSContext implements Context {
|
||||
|
||||
if ($bytes !== null) {
|
||||
$client->file($sourceFile, $destination)->createWithUpload($client->getKey(), $bytes);
|
||||
} elseif (\filesize($sourceFile) === 0) {
|
||||
$client->file($sourceFile, $destination)->createWithUpload($client->getKey(), 0);
|
||||
} elseif ($noOfChunks === 1) {
|
||||
$client->file($sourceFile, $destination)->upload();
|
||||
} else {
|
||||
|
||||
@@ -140,11 +140,11 @@ Feature: sharing
|
||||
| old |
|
||||
| new |
|
||||
|
||||
@smokeTest @skipOnGraph
|
||||
@smokeTest
|
||||
Scenario Outline: check quota of owners parent directory of a shared file
|
||||
Given using <dav-path-version> DAV path
|
||||
And user "Brian" has been created with default attributes and without skeleton files
|
||||
And the quota of user "Brian" has been set to "0"
|
||||
And user "Admin" has changed the quota of the personal space of "Brian Murphy" space to "0"
|
||||
And user "Alice" has uploaded file "filesForUpload/lorem.txt" to "/myfile.txt"
|
||||
And user "Alice" has shared file "myfile.txt" with user "Brian"
|
||||
When user "Brian" uploads file "filesForUpload/textfile.txt" to "/Shares/myfile.txt" using the WebDAV API
|
||||
@@ -162,17 +162,17 @@ Feature: sharing
|
||||
| old |
|
||||
| new |
|
||||
|
||||
@skipOnGraph
|
||||
|
||||
Scenario Outline: uploading to a user shared folder with read/write permission when the sharer has insufficient quota does not work
|
||||
Given using <dav-path-version> DAV path
|
||||
And user "Brian" has been created with default attributes and small skeleton files
|
||||
And user "Brian" has been created with default attributes and without skeleton files
|
||||
And user "Alice" has created folder "FOLDER"
|
||||
And user "Alice" has created a share with settings
|
||||
| path | FOLDER |
|
||||
| shareType | user |
|
||||
| permissions | change |
|
||||
| shareWith | Brian |
|
||||
And the quota of user "Alice" has been set to "0"
|
||||
And user "Admin" has changed the quota of the personal space of "Alice Hansen" space to "1"
|
||||
When user "Brian" uploads file "filesForUpload/textfile.txt" to "/Shares/FOLDER/myfile.txt" using the WebDAV API
|
||||
Then the HTTP status code should be "507"
|
||||
And as "Alice" file "/FOLDER/myfile.txt" should not exist
|
||||
@@ -193,7 +193,7 @@ Feature: sharing
|
||||
| shareType | group |
|
||||
| permissions | change |
|
||||
| shareWith | grp1 |
|
||||
And the quota of user "Alice" has been set to "0"
|
||||
And user "Admin" has changed the quota of the personal space of "Alice Hansen" space to "1"
|
||||
When user "Brian" uploads file "filesForUpload/textfile.txt" to "/Shares/FOLDER/myfile.txt" using the WebDAV API
|
||||
Then the HTTP status code should be "507"
|
||||
And as "Alice" file "/FOLDER/myfile.txt" should not exist
|
||||
@@ -202,7 +202,7 @@ Feature: sharing
|
||||
| old |
|
||||
| new |
|
||||
|
||||
@skipOnGraph
|
||||
|
||||
Scenario Outline: uploading to a user shared folder with upload-only permission when the sharer has insufficient quota does not work
|
||||
Given using <dav-path-version> DAV path
|
||||
And user "Brian" has been created with default attributes and without skeleton files
|
||||
@@ -212,7 +212,7 @@ Feature: sharing
|
||||
| shareType | user |
|
||||
| permissions | create |
|
||||
| shareWith | Brian |
|
||||
And the quota of user "Alice" has been set to "0"
|
||||
And user "Admin" has changed the quota of the personal space of "Alice Hansen" space to "1"
|
||||
When user "Brian" uploads file "filesForUpload/textfile.txt" to "/Shares/FOLDER/myfile.txt" using the WebDAV API
|
||||
Then the HTTP status code should be "507"
|
||||
And as "Alice" file "/FOLDER/myfile.txt" should not exist
|
||||
@@ -221,7 +221,7 @@ Feature: sharing
|
||||
| old |
|
||||
| new |
|
||||
|
||||
@skipOnGraph
|
||||
|
||||
Scenario Outline: uploading to a group shared folder with upload-only permission when the sharer has insufficient quota does not work
|
||||
Given using <dav-path-version> DAV path
|
||||
And user "Brian" has been created with default attributes and without skeleton files
|
||||
@@ -233,7 +233,7 @@ Feature: sharing
|
||||
| shareType | group |
|
||||
| permissions | create |
|
||||
| shareWith | grp1 |
|
||||
And the quota of user "Alice" has been set to "0"
|
||||
And user "Admin" has changed the quota of the personal space of "Alice Hansen" space to "1"
|
||||
When user "Brian" uploads file "filesForUpload/textfile.txt" to "/Shares/FOLDER/myfile.txt" using the WebDAV API
|
||||
Then the HTTP status code should be "507"
|
||||
And as "Alice" file "/FOLDER/myfile.txt" should not exist
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@issue-1276 @issue-1277 @issue-1269
|
||||
@issue-1276 @issue-1269
|
||||
|
||||
Feature: changing a public link share
|
||||
As a user
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@issue-1276 @issue-1277
|
||||
@issue-1276
|
||||
|
||||
Feature: upload to a public link share
|
||||
As a user
|
||||
@@ -93,17 +93,17 @@ Feature: upload to a public link share
|
||||
| path | FOLDER |
|
||||
| permissions | change |
|
||||
| password | %public% |
|
||||
And the quota of user "Alice" has been set to "0"
|
||||
And user "Admin" has changed the quota of the personal space of "Alice Hansen" space to "1"
|
||||
When the public uploads file "test.txt" with password "%public%" and content "test2" using the new public WebDAV API
|
||||
Then the HTTP status code should be "507"
|
||||
|
||||
@issue-1290
|
||||
|
||||
Scenario: uploading file to a public shared folder with upload-only permission when the sharer has insufficient quota does not work with public API
|
||||
Given user "Alice" has created a public link share with settings
|
||||
| path | FOLDER |
|
||||
| permissions | create |
|
||||
| password | %public% |
|
||||
And the quota of user "Alice" has been set to "0"
|
||||
And user "Admin" has changed the quota of the personal space of "Alice Hansen" space to "1"
|
||||
When the public uploads file "test.txt" with password "%public%" and content "test2" using the new public WebDAV API
|
||||
Then the HTTP status code should be "507"
|
||||
|
||||
|
||||
@@ -418,3 +418,31 @@ Feature: dav-versions
|
||||
When user "Brian" tries to get versions of file "textfile0.txt" from "Alice"
|
||||
Then the HTTP status code should be "403"
|
||||
And the value of the item "//s:exception" in the response about user "Alice" should be "Sabre\DAV\Exception\Forbidden"
|
||||
|
||||
@issue-enterprise-6249
|
||||
Scenario: upload empty content file and check versions after multiple restores
|
||||
Given user "Alice" has uploaded file with content "" to "textfile.txt"
|
||||
And user "Alice" has uploaded file with content "test content" to "textfile.txt"
|
||||
And the version folder of file "textfile.txt" for user "Alice" should contain "1" element
|
||||
When user "Alice" restores version index "1" of file "textfile.txt" using the WebDAV API
|
||||
Then the HTTP status code should be "204"
|
||||
And the content of file "textfile.txt" for user "Alice" should be ""
|
||||
And the version folder of file "textfile.txt" for user "Alice" should contain "1" elements
|
||||
When user "Alice" restores version index "1" of file "textfile.txt" using the WebDAV API
|
||||
Then the HTTP status code should be "204"
|
||||
And the content of file "textfile.txt" for user "Alice" should be "test content"
|
||||
And the version folder of file "textfile.txt" for user "Alice" should contain "1" elements
|
||||
|
||||
|
||||
Scenario: update with empty content and check versions after multiple restores
|
||||
Given user "Alice" has uploaded file with content "test content" to "textfile.txt"
|
||||
And user "Alice" has uploaded file with content "" to "textfile.txt"
|
||||
And the version folder of file "textfile.txt" for user "Alice" should contain "1" element
|
||||
When user "Alice" restores version index "1" of file "textfile.txt" using the WebDAV API
|
||||
Then the HTTP status code should be "204"
|
||||
And the content of file "textfile.txt" for user "Alice" should be "test content"
|
||||
And the version folder of file "textfile.txt" for user "Alice" should contain "1" elements
|
||||
When user "Alice" restores version index "1" of file "textfile.txt" using the WebDAV API
|
||||
Then the HTTP status code should be "204"
|
||||
And the content of file "textfile.txt" for user "Alice" should be ""
|
||||
And the version folder of file "textfile.txt" for user "Alice" should contain "1" elements
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@issue-1313 @skipOnGraph
|
||||
@issue-1313 @skipOnReva
|
||||
Feature: get quota
|
||||
As a user
|
||||
I want to be able to find out my available storage quota
|
||||
@@ -11,9 +11,9 @@ Feature: get quota
|
||||
|
||||
Scenario Outline: retrieving folder quota when no quota is set
|
||||
Given using <dav-path-version> DAV path
|
||||
When the administrator gives unlimited quota to user "Alice" using the provisioning API
|
||||
When user "Admin" changes the quota of the "Alice Hansen" space to "0"
|
||||
Then the HTTP status code should be "200"
|
||||
And as user "Alice" folder "/" should contain a property "d:quota-available-bytes" with value "-3"
|
||||
And as user "Alice" folder "/" should contain a property "d:quota-available-bytes" with value "0"
|
||||
Examples:
|
||||
| dav-path-version |
|
||||
| old |
|
||||
@@ -23,66 +23,67 @@ Feature: get quota
|
||||
@smokeTest
|
||||
Scenario Outline: retrieving folder quota when quota is set
|
||||
Given using <dav-path-version> DAV path
|
||||
When the administrator sets the quota of user "Alice" to "10 MB" using the provisioning API
|
||||
When user "Admin" changes the quota of the "Alice Hansen" space to "10000"
|
||||
Then the HTTP status code should be "200"
|
||||
And as user "Alice" folder "/" should contain a property "d:quota-available-bytes" with value "10485406"
|
||||
And as user "Alice" folder "/" should contain a property "d:quota-available-bytes" with value "10000"
|
||||
Examples:
|
||||
| dav-path-version |
|
||||
| old |
|
||||
| new |
|
||||
| spaces |
|
||||
|
||||
|
||||
@issue-8197
|
||||
Scenario Outline: retrieving folder quota of shared folder with quota when no quota is set for recipient
|
||||
Given using <dav-path-version> DAV path
|
||||
And user "Brian" has been created with default attributes and without skeleton files
|
||||
And user "Alice" has been given unlimited quota
|
||||
And the quota of user "Brian" has been set to "10 MB"
|
||||
And user "Admin" has changed the quota of the personal space of "Alice Hansen" space to "0"
|
||||
And user "Admin" has changed the quota of the personal space of "Brian Murphy" space to "10000"
|
||||
And user "Brian" has created folder "/testquota"
|
||||
And user "Brian" has uploaded file "/testquota/Brian.txt" of size 1000 bytes
|
||||
And user "Brian" has created a share with settings
|
||||
| path | testquota |
|
||||
| shareType | user |
|
||||
| permissions | all |
|
||||
| shareWith | Alice |
|
||||
When user "Alice" gets the following properties of folder "/testquota" using the WebDAV API
|
||||
When user "Alice" gets the following properties of folder "<folder-path>" inside space "Shares" using the WebDAV API
|
||||
| propertyName |
|
||||
| d:quota-available-bytes |
|
||||
Then the HTTP status code should be "200"
|
||||
And the single response should contain a property "d:quota-available-bytes" with value "10485406"
|
||||
Then the HTTP status code should be "207"
|
||||
And the single response should contain a property "d:quota-available-bytes" with value "9000"
|
||||
Examples:
|
||||
| dav-path-version |
|
||||
| old |
|
||||
| new |
|
||||
| spaces |
|
||||
|
||||
| dav-path-version | folder-path |
|
||||
| old | /Shares/testquota |
|
||||
| new | /Shares/testquota |
|
||||
| spaces | /testquota |
|
||||
|
||||
@issue-8197
|
||||
Scenario Outline: retrieving folder quota when quota is set and a file was uploaded
|
||||
Given using <dav-path-version> DAV path
|
||||
And the quota of user "Alice" has been set to "1 KB"
|
||||
And user "Alice" has uploaded file "/prueba.txt" of size 93 bytes
|
||||
And user "Admin" has changed the quota of the personal space of "Alice Hansen" space to "10000"
|
||||
And user "Alice" has uploaded file "/prueba.txt" of size 1000 bytes
|
||||
When user "Alice" gets the following properties of folder "/" using the WebDAV API
|
||||
| propertyName |
|
||||
| d:quota-available-bytes |
|
||||
Then the HTTP status code should be "201"
|
||||
And the single response should contain a property "d:quota-available-bytes" with value "577"
|
||||
Then the HTTP status code should be "207"
|
||||
And the single response should contain a property "d:quota-available-bytes" with value "9000"
|
||||
Examples:
|
||||
| dav-path-version |
|
||||
| old |
|
||||
| new |
|
||||
| spaces |
|
||||
|
||||
@skipOnReva
|
||||
|
||||
Scenario Outline: retrieving folder quota when quota is set and a file was received
|
||||
Given using <dav-path-version> DAV path
|
||||
And user "Brian" has been created with default attributes and without skeleton files
|
||||
And the quota of user "Brian" has been set to "1 KB"
|
||||
And user "Admin" has changed the quota of the personal space of "Brian Murphy" space to "10000"
|
||||
And user "Alice" has uploaded file "/Alice.txt" of size 93 bytes
|
||||
And user "Alice" has shared file "Alice.txt" with user "Brian"
|
||||
When user "Brian" gets the following properties of folder "/" using the WebDAV API
|
||||
| propertyName |
|
||||
| d:quota-available-bytes |
|
||||
Then the HTTP status code should be "200"
|
||||
And the single response should contain a property "d:quota-available-bytes" with value "670"
|
||||
Then the HTTP status code should be "207"
|
||||
And the single response should contain a property "d:quota-available-bytes" with value "10000"
|
||||
Examples:
|
||||
| dav-path-version |
|
||||
| old |
|
||||
|
||||
@@ -208,3 +208,39 @@ Feature: upload file
|
||||
| spaces | "filewithLF-and-CR\r\n" | ZmlsZXdpdGhMRi1hbmQtQ1INCgo= |
|
||||
| spaces | "folder/file" | Zm9sZGVyL2ZpbGU= |
|
||||
| spaces | "my\\file" | bXkMaWxl |
|
||||
|
||||
|
||||
Scenario Outline: upload a zero-byte file
|
||||
Given using <dav-path-version> DAV path
|
||||
When user "Alice" uploads file "filesForUpload/zerobyte.txt" to "textfile.txt" using the TUS protocol on the WebDAV API
|
||||
Then the content of file "textfile.txt" for user "Alice" should be ""
|
||||
Examples:
|
||||
| dav-path-version |
|
||||
| old |
|
||||
| new |
|
||||
| spaces |
|
||||
|
||||
@issue-8003
|
||||
Scenario Outline: replace a file with zero-byte file
|
||||
Given using <dav-path-version> DAV path
|
||||
And user "Alice" has uploaded file with content "This is TUS upload" to "textfile.txt"
|
||||
When user "Alice" uploads file "filesForUpload/zerobyte.txt" to "textfile.txt" using the TUS protocol on the WebDAV API
|
||||
Then the content of file "textfile.txt" for user "Alice" should be ""
|
||||
Examples:
|
||||
| dav-path-version |
|
||||
| old |
|
||||
| new |
|
||||
| spaces |
|
||||
|
||||
@issue-8003
|
||||
Scenario Outline: replace a file inside a folder with zero-byte file
|
||||
Given using <dav-path-version> DAV path
|
||||
And user "Alice" has created folder "testFolder"
|
||||
And user "Alice" has uploaded file with content "This is TUS upload" to "testFolder/textfile.txt"
|
||||
When user "Alice" uploads file "filesForUpload/zerobyte.txt" to "testFolder/textfile.txt" using the TUS protocol on the WebDAV API
|
||||
Then the content of file "testFolder/textfile.txt" for user "Alice" should be ""
|
||||
Examples:
|
||||
| dav-path-version |
|
||||
| old |
|
||||
| new |
|
||||
| spaces |
|
||||
|
||||
@@ -25,6 +25,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/cs3org/reva/v2/pkg/storagespace"
|
||||
"google.golang.org/genproto/protobuf/field_mask"
|
||||
"google.golang.org/grpc"
|
||||
codes "google.golang.org/grpc/codes"
|
||||
gstatus "google.golang.org/grpc/status"
|
||||
@@ -320,7 +321,7 @@ func (s *service) GetPath(ctx context.Context, req *provider.GetPathRequest) (*p
|
||||
|
||||
return &provider.GetPathResponse{
|
||||
Status: status.NewOK(ctx),
|
||||
Path: receivedShare.MountPoint.Path,
|
||||
Path: filepath.Clean("/" + receivedShare.MountPoint.Path),
|
||||
}, nil
|
||||
|
||||
}
|
||||
@@ -397,7 +398,7 @@ func (s *service) ListStorageSpaces(ctx context.Context, req *provider.ListStora
|
||||
var shareInfo map[string]*provider.ResourceInfo
|
||||
var err error
|
||||
if fetchShares {
|
||||
receivedShares, shareInfo, err = s.fetchShares(ctx)
|
||||
receivedShares, shareInfo, err = s.fetchShares(ctx, req.Opaque, []string{}, &fieldmaskpb.FieldMask{ /*TODO mtime and etag only?*/ })
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "sharesstorageprovider: error calling ListReceivedSharesRequest")
|
||||
}
|
||||
@@ -708,7 +709,7 @@ func (s *service) Stat(ctx context.Context, req *provider.StatRequest) (*provide
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("missing user in context")
|
||||
}
|
||||
receivedShares, shareMd, err := s.fetchShares(ctx)
|
||||
receivedShares, shareMd, err := s.fetchShares(ctx, req.Opaque, req.ArbitraryMetadataKeys, req.FieldMask)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -804,7 +805,7 @@ func (s *service) ListContainer(ctx context.Context, req *provider.ListContainer
|
||||
// The root is empty, it is filled by mountpoints
|
||||
// so, when accessing the root via /dav/spaces, we need to list the accepted shares with their mountpoint
|
||||
|
||||
receivedShares, _, err := s.fetchShares(ctx)
|
||||
receivedShares, shareMd, err := s.fetchShares(ctx, req.Opaque, req.ArbitraryMetadataKeys, req.FieldMask)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "sharesstorageprovider: error calling ListReceivedSharesRequest")
|
||||
}
|
||||
@@ -820,37 +821,45 @@ func (s *service) ListContainer(ctx context.Context, req *provider.ListContainer
|
||||
continue
|
||||
}
|
||||
|
||||
statRes, err := gatewayClient.Stat(ctx, &provider.StatRequest{
|
||||
Opaque: req.Opaque,
|
||||
Ref: &provider.Reference{
|
||||
ResourceId: share.Share.ResourceId,
|
||||
Path: ".",
|
||||
},
|
||||
ArbitraryMetadataKeys: req.ArbitraryMetadataKeys,
|
||||
})
|
||||
switch {
|
||||
case err != nil:
|
||||
appctx.GetLogger(ctx).Error().
|
||||
Err(err).
|
||||
Interface("share", share).
|
||||
Msg("sharesstorageprovider: could not make stat request when listing virtual root, skipping")
|
||||
continue
|
||||
case statRes.Status.Code != rpc.Code_CODE_OK:
|
||||
appctx.GetLogger(ctx).Debug().
|
||||
Interface("share", share).
|
||||
Interface("status", statRes.Status).
|
||||
Msg("sharesstorageprovider: could not stat share when listing virtual root, skipping")
|
||||
continue
|
||||
info := shareMd[share.GetShare().GetId().GetOpaqueId()]
|
||||
if info == nil {
|
||||
if share.GetShare().GetResourceId().GetSpaceId() == "" {
|
||||
// convert backwards compatible share id
|
||||
share.Share.ResourceId.StorageId, share.Share.ResourceId.SpaceId = storagespace.SplitStorageID(share.GetShare().GetResourceId().GetSpaceId())
|
||||
}
|
||||
statRes, err := gatewayClient.Stat(ctx, &provider.StatRequest{
|
||||
Opaque: req.Opaque,
|
||||
Ref: &provider.Reference{
|
||||
ResourceId: share.Share.ResourceId,
|
||||
Path: ".",
|
||||
},
|
||||
ArbitraryMetadataKeys: req.ArbitraryMetadataKeys,
|
||||
})
|
||||
switch {
|
||||
case err != nil:
|
||||
appctx.GetLogger(ctx).Error().
|
||||
Err(err).
|
||||
Interface("share", share).
|
||||
Msg("sharesstorageprovider: could not make stat request when listing virtual root, skipping")
|
||||
continue
|
||||
case statRes.Status.Code != rpc.Code_CODE_OK:
|
||||
appctx.GetLogger(ctx).Debug().
|
||||
Interface("share", share).
|
||||
Interface("status", statRes.Status).
|
||||
Msg("sharesstorageprovider: could not stat share when listing virtual root, skipping")
|
||||
continue
|
||||
}
|
||||
info = statRes.Info
|
||||
}
|
||||
|
||||
// override info
|
||||
info := statRes.Info
|
||||
// override resource id info
|
||||
info.Id = &provider.ResourceId{
|
||||
StorageId: utils.ShareStorageProviderID,
|
||||
SpaceId: utils.ShareStorageSpaceID,
|
||||
OpaqueId: share.Share.Id.OpaqueId,
|
||||
}
|
||||
info.Path = filepath.Base(share.MountPoint.Path)
|
||||
info.Name = info.Path
|
||||
|
||||
infos = append(infos, info)
|
||||
}
|
||||
@@ -1067,7 +1076,13 @@ func (s *service) resolveAcceptedShare(ctx context.Context, ref *provider.Refere
|
||||
// look up share for this resourceid
|
||||
lsRes, err := sharingCollaborationClient.ListReceivedShares(ctx, &collaboration.ListReceivedSharesRequest{
|
||||
Filters: []*collaboration.Filter{
|
||||
// FIXME filter by accepted ... and by mountpoint?
|
||||
{
|
||||
Type: collaboration.Filter_TYPE_STATE,
|
||||
Term: &collaboration.Filter_State{
|
||||
State: collaboration.ShareState_SHARE_STATE_ACCEPTED,
|
||||
},
|
||||
},
|
||||
// TODO filter by mountpoint?
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
@@ -1077,6 +1092,7 @@ func (s *service) resolveAcceptedShare(ctx context.Context, ref *provider.Refere
|
||||
return nil, lsRes.Status, nil
|
||||
}
|
||||
for _, receivedShare := range lsRes.Shares {
|
||||
// make sure to skip unaccepted shares
|
||||
if receivedShare.State != collaboration.ShareState_SHARE_STATE_ACCEPTED {
|
||||
continue
|
||||
}
|
||||
@@ -1121,7 +1137,7 @@ func (s *service) rejectReceivedShare(ctx context.Context, receivedShare *collab
|
||||
return errtypes.NewErrtypeFromStatus(res.Status)
|
||||
}
|
||||
|
||||
func (s *service) fetchShares(ctx context.Context) ([]*collaboration.ReceivedShare, map[string]*provider.ResourceInfo, error) {
|
||||
func (s *service) fetchShares(ctx context.Context, opaque *typesv1beta1.Opaque, arbitraryMetadataKeys []string, fieldMask *field_mask.FieldMask) ([]*collaboration.ReceivedShare, map[string]*provider.ResourceInfo, error) {
|
||||
sharingCollaborationClient, err := s.sharingCollaborationSelector.Next()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
@@ -1152,7 +1168,12 @@ func (s *service) fetchShares(ctx context.Context) ([]*collaboration.ReceivedSha
|
||||
// convert backwards compatible share id
|
||||
rs.Share.ResourceId.StorageId, rs.Share.ResourceId.SpaceId = storagespace.SplitStorageID(rs.Share.ResourceId.StorageId)
|
||||
}
|
||||
sRes, err := gatewayClient.Stat(ctx, &provider.StatRequest{Ref: &provider.Reference{ResourceId: rs.Share.ResourceId}})
|
||||
sRes, err := gatewayClient.Stat(ctx, &provider.StatRequest{
|
||||
Opaque: opaque,
|
||||
Ref: &provider.Reference{ResourceId: rs.Share.ResourceId},
|
||||
ArbitraryMetadataKeys: arbitraryMetadataKeys,
|
||||
FieldMask: fieldMask,
|
||||
})
|
||||
if err != nil {
|
||||
appctx.GetLogger(ctx).Error().
|
||||
Err(err).
|
||||
|
||||
@@ -702,13 +702,27 @@ func (s *service) Delete(ctx context.Context, req *provider.DeleteRequest) (*pro
|
||||
}
|
||||
}
|
||||
|
||||
md, err := s.storage.GetMD(ctx, req.Ref, []string{}, []string{"id"})
|
||||
md, err := s.storage.GetMD(ctx, req.Ref, []string{}, []string{"id", "status"})
|
||||
if err != nil {
|
||||
return &provider.DeleteResponse{
|
||||
Status: status.NewStatusFromErrType(ctx, "can't stat resource to delete", err),
|
||||
}, nil
|
||||
}
|
||||
|
||||
if utils.ReadPlainFromOpaque(md.GetOpaque(), "status") == "processing" {
|
||||
return &provider.DeleteResponse{
|
||||
Status: &rpc.Status{
|
||||
Code: rpc.Code_CODE_UNAVAILABLE,
|
||||
Message: "file is processing",
|
||||
},
|
||||
Opaque: &typesv1beta1.Opaque{
|
||||
Map: map[string]*typesv1beta1.OpaqueEntry{
|
||||
"status": {Decoder: "plain", Value: []byte("processing")},
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
err = s.storage.Delete(ctx, req.Ref)
|
||||
|
||||
return &provider.DeleteResponse{
|
||||
|
||||
@@ -198,7 +198,7 @@ func (s *service) CreateShare(ctx context.Context, req *collaboration.CreateShar
|
||||
Status: status.NewPermissionDenied(ctx, nil, "no permission to add grants on shared resource"),
|
||||
}, err
|
||||
}
|
||||
// check if the requested share creation has sufficient permissions to do so.
|
||||
// check if the share creator has sufficient permissions to do so.
|
||||
if shareCreationAllowed := conversions.SufficientCS3Permissions(
|
||||
sRes.GetInfo().GetPermissionSet(),
|
||||
req.GetGrant().GetPermissions().GetPermissions(),
|
||||
@@ -207,6 +207,14 @@ func (s *service) CreateShare(ctx context.Context, req *collaboration.CreateShar
|
||||
Status: status.NewPermissionDenied(ctx, nil, "insufficient permissions to create that kind of share"),
|
||||
}, nil
|
||||
}
|
||||
// check if the requested permission are plausible for the Resource
|
||||
if sRes.GetInfo().GetType() == provider.ResourceType_RESOURCE_TYPE_FILE {
|
||||
if newPermissions := req.GetGrant().GetPermissions().GetPermissions(); newPermissions.GetCreateContainer() || newPermissions.GetMove() || newPermissions.GetDelete() {
|
||||
return &collaboration.CreateShareResponse{
|
||||
Status: status.NewInvalid(ctx, "cannot set the requested permissions on that type of resource"),
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
if !s.isPathAllowed(req.GetResourceInfo().GetPath()) {
|
||||
return &collaboration.CreateShareResponse{
|
||||
|
||||
2
vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/copy.go
generated
vendored
2
vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/copy.go
generated
vendored
@@ -303,6 +303,7 @@ func (s *svc) executePathCopy(ctx context.Context, selector pool.Selectable[gate
|
||||
return err
|
||||
}
|
||||
httpUploadReq.Header.Set(datagateway.TokenTransportHeader, uploadToken)
|
||||
httpUploadReq.ContentLength = int64(cp.sourceInfo.GetSize())
|
||||
|
||||
httpUploadRes, err := s.client.Do(httpUploadReq)
|
||||
if err != nil {
|
||||
@@ -521,6 +522,7 @@ func (s *svc) executeSpacesCopy(ctx context.Context, w http.ResponseWriter, sele
|
||||
return err
|
||||
}
|
||||
httpUploadReq.Header.Set(datagateway.TokenTransportHeader, uploadToken)
|
||||
httpUploadReq.ContentLength = int64(cp.sourceInfo.GetSize())
|
||||
|
||||
httpUploadRes, err := s.client.Do(httpUploadReq)
|
||||
if err != nil {
|
||||
|
||||
2
vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/ocdav.go
generated
vendored
2
vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/ocdav.go
generated
vendored
@@ -286,8 +286,6 @@ func (s *svc) ApplyLayout(ctx context.Context, ns string, useLoggedInUserNS bool
|
||||
|
||||
func addAccessHeaders(w http.ResponseWriter, r *http.Request) {
|
||||
headers := w.Header()
|
||||
// the webdav api is accessible from anywhere
|
||||
headers.Set("Access-Control-Allow-Origin", "*")
|
||||
// all resources served via the DAV endpoint should have the strictest possible as default
|
||||
headers.Set("Content-Security-Policy", "default-src 'none';")
|
||||
// disable sniffing the content type for IE
|
||||
|
||||
@@ -486,12 +486,16 @@ func (p *Handler) propfindResponse(ctx context.Context, w http.ResponseWriter, r
|
||||
w.Header().Set(net.HeaderDav, "1, 3, extended-mkcol")
|
||||
w.Header().Set(net.HeaderContentType, "application/xml; charset=utf-8")
|
||||
if sendTusHeaders {
|
||||
w.Header().Add(net.HeaderAccessControlExposeHeaders, strings.Join([]string{net.HeaderTusResumable, net.HeaderTusVersion, net.HeaderTusExtension}, ", "))
|
||||
w.Header().Add(net.HeaderAccessControlExposeHeaders, net.HeaderTusResumable)
|
||||
w.Header().Add(net.HeaderAccessControlExposeHeaders, net.HeaderTusVersion)
|
||||
w.Header().Add(net.HeaderAccessControlExposeHeaders, net.HeaderTusExtension)
|
||||
w.Header().Set(net.HeaderAccessControlExposeHeaders, strings.Join(w.Header().Values(net.HeaderAccessControlExposeHeaders), ", "))
|
||||
w.Header().Set(net.HeaderTusResumable, "1.0.0")
|
||||
w.Header().Set(net.HeaderTusVersion, "1.0.0")
|
||||
w.Header().Set(net.HeaderTusExtension, "creation,creation-with-upload,checksum,expiration")
|
||||
w.Header().Set(net.HeaderTusExtension, "creation, creation-with-upload, checksum, expiration")
|
||||
}
|
||||
w.Header().Set(net.HeaderVary, net.HeaderPrefer)
|
||||
w.Header().Add(net.HeaderVary, net.HeaderPrefer)
|
||||
w.Header().Set(net.HeaderVary, strings.Join(w.Header().Values(net.HeaderVary), ", "))
|
||||
if returnMinimal {
|
||||
w.Header().Set(net.HeaderPreferenceApplied, "return=minimal")
|
||||
}
|
||||
|
||||
91
vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/put.go
generated
vendored
91
vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/put.go
generated
vendored
@@ -292,58 +292,59 @@ func (s *svc) handlePut(ctx context.Context, w http.ResponseWriter, r *http.Requ
|
||||
}
|
||||
|
||||
// ony send actual PUT request if file has bytes. Otherwise the initiate file upload request creates the file
|
||||
// if length != 0 { // FIXME bring back 0 byte file upload handling, see https://github.com/owncloud/ocis/issues/2609
|
||||
|
||||
var ep, token string
|
||||
for _, p := range uRes.Protocols {
|
||||
if p.Protocol == "simple" {
|
||||
ep, token = p.UploadEndpoint, p.Token
|
||||
if length != 0 {
|
||||
var ep, token string
|
||||
for _, p := range uRes.Protocols {
|
||||
if p.Protocol == "simple" {
|
||||
ep, token = p.UploadEndpoint, p.Token
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
httpReq, err := rhttp.NewRequest(ctx, http.MethodPut, ep, r.Body)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
Propagator.Inject(ctx, propagation.HeaderCarrier(httpReq.Header))
|
||||
httpReq.Header.Set(datagateway.TokenTransportHeader, token)
|
||||
|
||||
httpRes, err := s.client.Do(httpReq)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("error doing PUT request to data service")
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
defer httpRes.Body.Close()
|
||||
if httpRes.StatusCode != http.StatusOK {
|
||||
if httpRes.StatusCode == http.StatusPartialContent {
|
||||
w.WriteHeader(http.StatusPartialContent)
|
||||
httpReq, err := rhttp.NewRequest(ctx, http.MethodPut, ep, r.Body)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
if httpRes.StatusCode == errtypes.StatusChecksumMismatch {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
b, err := errors.Marshal(http.StatusBadRequest, "The computed checksum does not match the one received from the client.", "")
|
||||
errors.HandleWebdavError(&log, w, b, err)
|
||||
Propagator.Inject(ctx, propagation.HeaderCarrier(httpReq.Header))
|
||||
httpReq.Header.Set(datagateway.TokenTransportHeader, token)
|
||||
httpReq.ContentLength = length
|
||||
|
||||
httpRes, err := s.client.Do(httpReq)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("error doing PUT request to data service")
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
defer httpRes.Body.Close()
|
||||
if httpRes.StatusCode != http.StatusOK {
|
||||
if httpRes.StatusCode == http.StatusPartialContent {
|
||||
w.WriteHeader(http.StatusPartialContent)
|
||||
return
|
||||
}
|
||||
if httpRes.StatusCode == errtypes.StatusChecksumMismatch {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
b, err := errors.Marshal(http.StatusBadRequest, "The computed checksum does not match the one received from the client.", "")
|
||||
errors.HandleWebdavError(&log, w, b, err)
|
||||
return
|
||||
}
|
||||
log.Error().Err(err).Msg("PUT request to data server failed")
|
||||
w.WriteHeader(httpRes.StatusCode)
|
||||
return
|
||||
}
|
||||
log.Error().Err(err).Msg("PUT request to data server failed")
|
||||
w.WriteHeader(httpRes.StatusCode)
|
||||
return
|
||||
}
|
||||
|
||||
// copy headers if they are present
|
||||
if httpRes.Header.Get(net.HeaderETag) != "" {
|
||||
w.Header().Set(net.HeaderETag, httpRes.Header.Get(net.HeaderETag))
|
||||
}
|
||||
if httpRes.Header.Get(net.HeaderOCETag) != "" {
|
||||
w.Header().Set(net.HeaderOCETag, httpRes.Header.Get(net.HeaderOCETag))
|
||||
}
|
||||
if httpRes.Header.Get(net.HeaderOCFileID) != "" {
|
||||
w.Header().Set(net.HeaderOCFileID, httpRes.Header.Get(net.HeaderOCFileID))
|
||||
}
|
||||
if httpRes.Header.Get(net.HeaderLastModified) != "" {
|
||||
w.Header().Set(net.HeaderLastModified, httpRes.Header.Get(net.HeaderLastModified))
|
||||
// copy headers if they are present
|
||||
if httpRes.Header.Get(net.HeaderETag) != "" {
|
||||
w.Header().Set(net.HeaderETag, httpRes.Header.Get(net.HeaderETag))
|
||||
}
|
||||
if httpRes.Header.Get(net.HeaderOCETag) != "" {
|
||||
w.Header().Set(net.HeaderOCETag, httpRes.Header.Get(net.HeaderOCETag))
|
||||
}
|
||||
if httpRes.Header.Get(net.HeaderOCFileID) != "" {
|
||||
w.Header().Set(net.HeaderOCFileID, httpRes.Header.Get(net.HeaderOCFileID))
|
||||
}
|
||||
if httpRes.Header.Get(net.HeaderLastModified) != "" {
|
||||
w.Header().Set(net.HeaderLastModified, httpRes.Header.Get(net.HeaderLastModified))
|
||||
}
|
||||
}
|
||||
|
||||
// file was new
|
||||
|
||||
85
vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/tus.go
generated
vendored
85
vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/tus.go
generated
vendored
@@ -252,56 +252,58 @@ func (s *svc) handleTusPost(ctx context.Context, w http.ResponseWriter, r *http.
|
||||
// for creation-with-upload extension forward bytes to dataprovider
|
||||
// TODO check this really streams
|
||||
if r.Header.Get(net.HeaderContentType) == "application/offset+octet-stream" {
|
||||
length, err := strconv.ParseInt(r.Header.Get(net.HeaderContentLength), 10, 64)
|
||||
if err != nil {
|
||||
log.Debug().Err(err).Msg("wrong request")
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
var httpRes *http.Response
|
||||
finishUpload := true
|
||||
if uploadLength > 0 {
|
||||
var httpRes *http.Response
|
||||
|
||||
httpReq, err := rhttp.NewRequest(ctx, http.MethodPatch, ep, r.Body)
|
||||
if err != nil {
|
||||
log.Debug().Err(err).Msg("wrong request")
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
Propagator.Inject(ctx, propagation.HeaderCarrier(httpReq.Header))
|
||||
httpReq, err := rhttp.NewRequest(ctx, http.MethodPatch, ep, r.Body)
|
||||
if err != nil {
|
||||
log.Debug().Err(err).Msg("wrong request")
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
Propagator.Inject(ctx, propagation.HeaderCarrier(httpReq.Header))
|
||||
|
||||
httpReq.Header.Set(net.HeaderContentType, r.Header.Get(net.HeaderContentType))
|
||||
httpReq.Header.Set(net.HeaderContentLength, r.Header.Get(net.HeaderContentLength))
|
||||
if r.Header.Get(net.HeaderUploadOffset) != "" {
|
||||
httpReq.Header.Set(net.HeaderUploadOffset, r.Header.Get(net.HeaderUploadOffset))
|
||||
} else {
|
||||
httpReq.Header.Set(net.HeaderUploadOffset, "0")
|
||||
}
|
||||
httpReq.Header.Set(net.HeaderTusResumable, r.Header.Get(net.HeaderTusResumable))
|
||||
httpReq.Header.Set(net.HeaderContentType, r.Header.Get(net.HeaderContentType))
|
||||
httpReq.Header.Set(net.HeaderContentLength, r.Header.Get(net.HeaderContentLength))
|
||||
if r.Header.Get(net.HeaderUploadOffset) != "" {
|
||||
httpReq.Header.Set(net.HeaderUploadOffset, r.Header.Get(net.HeaderUploadOffset))
|
||||
} else {
|
||||
httpReq.Header.Set(net.HeaderUploadOffset, "0")
|
||||
}
|
||||
httpReq.Header.Set(net.HeaderTusResumable, r.Header.Get(net.HeaderTusResumable))
|
||||
|
||||
httpRes, err = s.client.Do(httpReq)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("error doing PATCH request to data gateway")
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
defer httpRes.Body.Close()
|
||||
httpRes, err = s.client.Do(httpReq)
|
||||
if err != nil || httpRes == nil {
|
||||
log.Error().Err(err).Msg("error doing PATCH request to data gateway")
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
defer httpRes.Body.Close()
|
||||
|
||||
w.Header().Set(net.HeaderUploadOffset, httpRes.Header.Get(net.HeaderUploadOffset))
|
||||
w.Header().Set(net.HeaderTusResumable, httpRes.Header.Get(net.HeaderTusResumable))
|
||||
w.Header().Set(net.HeaderTusUploadExpires, httpRes.Header.Get(net.HeaderTusUploadExpires))
|
||||
if httpRes.StatusCode != http.StatusNoContent {
|
||||
w.WriteHeader(httpRes.StatusCode)
|
||||
return
|
||||
}
|
||||
if httpRes.StatusCode != http.StatusNoContent {
|
||||
w.WriteHeader(httpRes.StatusCode)
|
||||
return
|
||||
}
|
||||
|
||||
// check if upload was fully completed
|
||||
if length == 0 || httpRes.Header.Get(net.HeaderUploadOffset) == r.Header.Get(net.HeaderUploadLength) {
|
||||
// get uploaded file metadata
|
||||
w.Header().Set(net.HeaderUploadOffset, httpRes.Header.Get(net.HeaderUploadOffset))
|
||||
w.Header().Set(net.HeaderTusResumable, httpRes.Header.Get(net.HeaderTusResumable))
|
||||
w.Header().Set(net.HeaderTusUploadExpires, httpRes.Header.Get(net.HeaderTusUploadExpires))
|
||||
if httpRes.Header.Get(net.HeaderOCMtime) != "" {
|
||||
w.Header().Set(net.HeaderOCMtime, httpRes.Header.Get(net.HeaderOCMtime))
|
||||
}
|
||||
|
||||
if resid, err := storagespace.ParseID(httpRes.Header.Get(net.HeaderOCFileID)); err == nil {
|
||||
sReq.Ref = &provider.Reference{
|
||||
ResourceId: &resid,
|
||||
}
|
||||
}
|
||||
finishUpload = httpRes.Header.Get(net.HeaderUploadOffset) == r.Header.Get(net.HeaderUploadLength)
|
||||
}
|
||||
|
||||
// check if upload was fully completed
|
||||
if uploadLength == 0 || finishUpload {
|
||||
// get uploaded file metadata
|
||||
|
||||
sRes, err := client.Stat(ctx, sReq)
|
||||
if err != nil {
|
||||
@@ -311,7 +313,6 @@ func (s *svc) handleTusPost(ctx context.Context, w http.ResponseWriter, r *http.
|
||||
}
|
||||
|
||||
if sRes.Status.Code != rpc.Code_CODE_OK && sRes.Status.Code != rpc.Code_CODE_NOT_FOUND {
|
||||
|
||||
if sRes.Status.Code == rpc.Code_CODE_PERMISSION_DENIED {
|
||||
// the token expired during upload, so the stat failed
|
||||
// and we can't do anything about it.
|
||||
@@ -330,10 +331,6 @@ func (s *svc) handleTusPost(ctx context.Context, w http.ResponseWriter, r *http.
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
if httpRes != nil && httpRes.Header != nil && httpRes.Header.Get(net.HeaderOCMtime) != "" {
|
||||
// set the "accepted" value if returned in the upload response headers
|
||||
w.Header().Set(net.HeaderOCMtime, httpRes.Header.Get(net.HeaderOCMtime))
|
||||
}
|
||||
|
||||
// get WebDav permissions for file
|
||||
isPublic := false
|
||||
|
||||
@@ -36,6 +36,7 @@ import (
|
||||
"github.com/cs3org/reva/v2/internal/http/services/owncloud/ocs/response"
|
||||
"github.com/cs3org/reva/v2/pkg/appctx"
|
||||
"github.com/cs3org/reva/v2/pkg/conversions"
|
||||
"github.com/cs3org/reva/v2/pkg/errtypes"
|
||||
"github.com/cs3org/reva/v2/pkg/utils"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/pkg/errors"
|
||||
@@ -63,62 +64,110 @@ func (h *Handler) AcceptReceivedShare(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
rs, ocsResponse := getReceivedShareFromID(ctx, client, shareID)
|
||||
receivedShare, ocsResponse := getReceivedShareFromID(ctx, client, shareID)
|
||||
if ocsResponse != nil {
|
||||
response.WriteOCSResponse(w, r, *ocsResponse, nil)
|
||||
return
|
||||
}
|
||||
|
||||
sharedResource, ocsResponse := getSharedResource(ctx, client, rs.Share.Share.ResourceId)
|
||||
sharedResource, ocsResponse := getSharedResource(ctx, client, receivedShare.Share.ResourceId)
|
||||
if ocsResponse != nil {
|
||||
response.WriteOCSResponse(w, r, *ocsResponse, nil)
|
||||
return
|
||||
}
|
||||
|
||||
lrs, ocsResponse := getSharesList(ctx, client)
|
||||
if ocsResponse != nil {
|
||||
response.WriteOCSResponse(w, r, *ocsResponse, nil)
|
||||
mount, unmountedShares, err := GetMountpointAndUnmountedShares(ctx, client, sharedResource.Info)
|
||||
if err != nil {
|
||||
response.WriteOCSError(w, r, response.MetaServerError.StatusCode, "could not determine mountpoint", err)
|
||||
return
|
||||
}
|
||||
|
||||
// first update the requested share
|
||||
receivedShare.State = collaboration.ShareState_SHARE_STATE_ACCEPTED
|
||||
// we need to add a path to the share
|
||||
receivedShare.MountPoint = &provider.Reference{
|
||||
Path: mount,
|
||||
}
|
||||
|
||||
updateMask := &fieldmaskpb.FieldMask{Paths: []string{"state", "mount_point"}}
|
||||
data, meta, err := h.updateReceivedShare(r.Context(), receivedShare, updateMask)
|
||||
if err != nil {
|
||||
// we log an error for affected shares, for the actual share we return an error
|
||||
response.WriteOCSData(w, r, meta, data, err)
|
||||
return
|
||||
}
|
||||
response.WriteOCSSuccess(w, r, []*conversions.ShareData{data})
|
||||
|
||||
// then update other unmounted shares to the same resource
|
||||
for _, rs := range unmountedShares {
|
||||
if rs.GetShare().GetId().GetOpaqueId() == shareID {
|
||||
// we already updated this one
|
||||
continue
|
||||
}
|
||||
|
||||
rs.State = collaboration.ShareState_SHARE_STATE_ACCEPTED
|
||||
// set the same mountpoint as for the requested received share
|
||||
rs.MountPoint = &provider.Reference{
|
||||
Path: mount,
|
||||
}
|
||||
|
||||
_, _, err := h.updateReceivedShare(r.Context(), rs, updateMask)
|
||||
if err != nil {
|
||||
// we log an error for affected shares, the actual share was successful
|
||||
appctx.GetLogger(ctx).Error().Err(err).Str("received_share", shareID).Str("affected_share", rs.GetShare().GetId().GetOpaqueId()).Msg("could not update affected received share")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GetMountpointAndUnmountedShares returns a new or existing mountpoint for the given info and produces a list of unmounted received shares for the same resource
|
||||
func GetMountpointAndUnmountedShares(ctx context.Context, gwc gateway.GatewayAPIClient, info *provider.ResourceInfo) (string, []*collaboration.ReceivedShare, error) {
|
||||
unmountedShares := []*collaboration.ReceivedShare{}
|
||||
receivedShares, err := listReceivedShares(ctx, gwc)
|
||||
if err != nil {
|
||||
return "", unmountedShares, err
|
||||
}
|
||||
|
||||
// we need to sort the received shares by mount point in order to make things easier to evaluate.
|
||||
base := path.Base(sharedResource.GetInfo().GetPath())
|
||||
mount := base
|
||||
var mountedShares []*collaboration.ReceivedShare
|
||||
sharesToAccept := map[string]bool{shareID: true}
|
||||
for _, s := range lrs.Shares {
|
||||
if utils.ResourceIDEqual(s.Share.ResourceId, rs.Share.Share.GetResourceId()) {
|
||||
mount := filepath.Clean(info.Name)
|
||||
existingMountpoint := ""
|
||||
mountedShares := make([]*collaboration.ReceivedShare, 0, len(receivedShares))
|
||||
for _, s := range receivedShares {
|
||||
if utils.ResourceIDEqual(s.Share.ResourceId, info.GetId()) {
|
||||
if s.State == collaboration.ShareState_SHARE_STATE_ACCEPTED {
|
||||
mount = s.MountPoint.Path
|
||||
// a share to the resource already exists and is mounted, remember the mount point
|
||||
_, err := utils.GetResourceByID(ctx, s.Share.ResourceId, gwc)
|
||||
if err == nil {
|
||||
existingMountpoint = s.MountPoint.Path
|
||||
}
|
||||
} else {
|
||||
sharesToAccept[s.Share.Id.OpaqueId] = true
|
||||
}
|
||||
} else {
|
||||
if s.State == collaboration.ShareState_SHARE_STATE_ACCEPTED {
|
||||
s.Hidden = h.getReceivedShareHideFlagFromShareID(r.Context(), shareID)
|
||||
mountedShares = append(mountedShares, s)
|
||||
// a share to the resource already exists but is not mounted, collect the unmounted share
|
||||
unmountedShares = append(unmountedShares, s)
|
||||
}
|
||||
}
|
||||
|
||||
if s.State == collaboration.ShareState_SHARE_STATE_ACCEPTED {
|
||||
mountedShares = append(mountedShares, s)
|
||||
}
|
||||
}
|
||||
|
||||
compareMountPoint := func(i, j int) bool {
|
||||
sort.Slice(mountedShares, func(i, j int) bool {
|
||||
return mountedShares[i].MountPoint.Path > mountedShares[j].MountPoint.Path
|
||||
}
|
||||
sort.Slice(mountedShares, compareMountPoint)
|
||||
})
|
||||
|
||||
// now we have a list of shares, we want to iterate over all of them and check for name collisions
|
||||
if existingMountpoint != "" {
|
||||
// we want to reuse the same mountpoint for all unmounted shares to the same resource
|
||||
return existingMountpoint, unmountedShares, nil
|
||||
}
|
||||
|
||||
// we have a list of shares, we want to iterate over all of them and check for name collisions
|
||||
for i, ms := range mountedShares {
|
||||
if ms.MountPoint.Path == mount {
|
||||
// does the shared resource still exist?
|
||||
res, err := client.Stat(ctx, &provider.StatRequest{
|
||||
Ref: &provider.Reference{
|
||||
ResourceId: ms.Share.ResourceId,
|
||||
},
|
||||
})
|
||||
if err == nil && res.Status.Code == rpc.Code_CODE_OK {
|
||||
_, err := utils.GetResourceByID(ctx, ms.Share.ResourceId, gwc)
|
||||
if err == nil {
|
||||
// The mount point really already exists, we need to insert a number into the filename
|
||||
ext := filepath.Ext(base)
|
||||
name := strings.TrimSuffix(base, ext)
|
||||
ext := filepath.Ext(mount)
|
||||
name := strings.TrimSuffix(mount, ext)
|
||||
// be smart about .tar.(gz|bz) files
|
||||
if strings.HasSuffix(name, ".tar") {
|
||||
name = strings.TrimSuffix(name, ".tar")
|
||||
@@ -130,26 +179,7 @@ func (h *Handler) AcceptReceivedShare(w http.ResponseWriter, r *http.Request) {
|
||||
// TODO we could delete shares here if the stat returns code NOT FOUND ... but listening for file deletes would be better
|
||||
}
|
||||
}
|
||||
// we need to add a path to the share
|
||||
receivedShare := &collaboration.ReceivedShare{
|
||||
Share: &collaboration.Share{
|
||||
Id: &collaboration.ShareId{OpaqueId: shareID},
|
||||
},
|
||||
State: collaboration.ShareState_SHARE_STATE_ACCEPTED,
|
||||
Hidden: h.getReceivedShareHideFlagFromShareID(r.Context(), shareID),
|
||||
MountPoint: &provider.Reference{
|
||||
Path: mount,
|
||||
},
|
||||
}
|
||||
updateMask := &fieldmaskpb.FieldMask{Paths: []string{"state", "hidden", "mount_point"}}
|
||||
|
||||
for id := range sharesToAccept {
|
||||
data := h.updateReceivedShare(w, r, receivedShare, updateMask)
|
||||
// only render the data for the changed share
|
||||
if id == shareID && data != nil {
|
||||
response.WriteOCSSuccess(w, r, []*conversions.ShareData{data})
|
||||
}
|
||||
}
|
||||
return mount, unmountedShares, nil
|
||||
}
|
||||
|
||||
// RejectReceivedShare handles DELETE Requests on /apps/files_sharing/api/v1/shares/{shareid}
|
||||
@@ -166,15 +196,15 @@ func (h *Handler) RejectReceivedShare(w http.ResponseWriter, r *http.Request) {
|
||||
Share: &collaboration.Share{
|
||||
Id: &collaboration.ShareId{OpaqueId: shareID},
|
||||
},
|
||||
State: collaboration.ShareState_SHARE_STATE_REJECTED,
|
||||
Hidden: h.getReceivedShareHideFlagFromShareID(r.Context(), shareID),
|
||||
State: collaboration.ShareState_SHARE_STATE_REJECTED,
|
||||
}
|
||||
updateMask := &fieldmaskpb.FieldMask{Paths: []string{"state", "hidden"}}
|
||||
updateMask := &fieldmaskpb.FieldMask{Paths: []string{"state"}}
|
||||
|
||||
data := h.updateReceivedShare(w, r, receivedShare, updateMask)
|
||||
if data != nil {
|
||||
response.WriteOCSSuccess(w, r, []*conversions.ShareData{data})
|
||||
data, meta, err := h.updateReceivedShare(r.Context(), receivedShare, updateMask)
|
||||
if err != nil {
|
||||
response.WriteOCSData(w, r, meta, nil, err)
|
||||
}
|
||||
response.WriteOCSSuccess(w, r, []*conversions.ShareData{data})
|
||||
}
|
||||
|
||||
func (h *Handler) UpdateReceivedShare(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -199,18 +229,17 @@ func (h *Handler) UpdateReceivedShare(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
rs, _ := getReceivedShareFromID(r.Context(), client, shareID)
|
||||
if rs != nil && rs.Share != nil {
|
||||
receivedShare.State = rs.Share.State
|
||||
receivedShare.State = rs.State
|
||||
}
|
||||
|
||||
data := h.updateReceivedShare(w, r, receivedShare, updateMask)
|
||||
if data != nil {
|
||||
response.WriteOCSSuccess(w, r, []*conversions.ShareData{data})
|
||||
data, meta, err := h.updateReceivedShare(r.Context(), receivedShare, updateMask)
|
||||
if err != nil {
|
||||
response.WriteOCSData(w, r, meta, nil, err)
|
||||
}
|
||||
// TODO: do we need error handling here?
|
||||
response.WriteOCSSuccess(w, r, []*conversions.ShareData{data})
|
||||
}
|
||||
|
||||
func (h *Handler) updateReceivedShare(w http.ResponseWriter, r *http.Request, receivedShare *collaboration.ReceivedShare, fieldMask *fieldmaskpb.FieldMask) *conversions.ShareData {
|
||||
ctx := r.Context()
|
||||
func (h *Handler) updateReceivedShare(ctx context.Context, receivedShare *collaboration.ReceivedShare, fieldMask *fieldmaskpb.FieldMask) (*conversions.ShareData, response.Meta, error) {
|
||||
logger := appctx.GetLogger(ctx)
|
||||
|
||||
updateShareRequest := &collaboration.UpdateReceivedShareRequest{
|
||||
@@ -220,23 +249,19 @@ func (h *Handler) updateReceivedShare(w http.ResponseWriter, r *http.Request, re
|
||||
|
||||
client, err := h.getClient()
|
||||
if err != nil {
|
||||
response.WriteOCSError(w, r, response.MetaServerError.StatusCode, "error getting grpc gateway client", err)
|
||||
return nil
|
||||
return nil, response.MetaServerError, errors.Wrap(err, "error getting grpc gateway client")
|
||||
}
|
||||
|
||||
shareRes, err := client.UpdateReceivedShare(ctx, updateShareRequest)
|
||||
if err != nil {
|
||||
response.WriteOCSError(w, r, response.MetaServerError.StatusCode, "grpc update received share request failed", err)
|
||||
return nil
|
||||
return nil, response.MetaServerError, errors.Wrap(err, "grpc update received share request failed")
|
||||
}
|
||||
|
||||
if shareRes.Status.Code != rpc.Code_CODE_OK {
|
||||
if shareRes.Status.Code == rpc.Code_CODE_NOT_FOUND {
|
||||
response.WriteOCSError(w, r, response.MetaNotFound.StatusCode, "not found", nil)
|
||||
return nil
|
||||
return nil, response.MetaNotFound, errors.New(shareRes.Status.Message)
|
||||
}
|
||||
response.WriteOCSError(w, r, response.MetaServerError.StatusCode, "grpc update received share request failed", errors.Errorf("code: %d, message: %s", shareRes.Status.Code, shareRes.Status.Message))
|
||||
return nil
|
||||
return nil, response.MetaServerError, errors.Errorf("grpc update received share request failed: code: %d, message: %s", shareRes.Status.Code, shareRes.Status.Message)
|
||||
}
|
||||
|
||||
rs := shareRes.GetShare()
|
||||
@@ -244,27 +269,23 @@ func (h *Handler) updateReceivedShare(w http.ResponseWriter, r *http.Request, re
|
||||
info, status, err := h.getResourceInfoByID(ctx, client, rs.Share.ResourceId)
|
||||
if err != nil || status.Code != rpc.Code_CODE_OK {
|
||||
h.logProblems(logger, status, err, "could not stat, skipping")
|
||||
response.WriteOCSError(w, r, response.MetaServerError.StatusCode, "grpc get resource info failed", errors.Errorf("code: %d, message: %s", status.Code, status.Message))
|
||||
return nil
|
||||
return nil, response.MetaServerError, errors.Errorf("grpc get resource info failed: code: %d, message: %s", status.Code, status.Message)
|
||||
}
|
||||
|
||||
data, err := conversions.CS3Share2ShareData(r.Context(), rs.Share)
|
||||
if err != nil {
|
||||
logger.Debug().Interface("share", rs.Share).Interface("shareData", data).Err(err).Msg("could not CS3Share2ShareData, skipping")
|
||||
}
|
||||
data := conversions.CS3Share2ShareData(ctx, rs.Share)
|
||||
|
||||
data.State = mapState(rs.GetState())
|
||||
data.Hidden = rs.GetHidden()
|
||||
|
||||
h.addFileInfo(ctx, data, info)
|
||||
h.mapUserIds(r.Context(), client, data)
|
||||
h.mapUserIds(ctx, client, data)
|
||||
|
||||
if data.State == ocsStateAccepted {
|
||||
// Needed because received shares can be jailed in a folder in the users home
|
||||
data.Path = path.Join(h.sharePrefix, path.Base(info.Path))
|
||||
}
|
||||
|
||||
return data
|
||||
return data, response.MetaOK, nil
|
||||
}
|
||||
|
||||
func (h *Handler) updateReceivedFederatedShare(w http.ResponseWriter, r *http.Request, shareID string, rejectShare bool) {
|
||||
@@ -337,21 +358,8 @@ func (h *Handler) updateReceivedFederatedShare(w http.ResponseWriter, r *http.Re
|
||||
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()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
rs, _ := getReceivedShareFromID(ctx, client, shareID)
|
||||
if rs != nil {
|
||||
return rs.GetShare().GetHidden()
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// getReceivedShareFromID uses a client to the gateway to fetch a share based on its ID.
|
||||
func getReceivedShareFromID(ctx context.Context, client gateway.GatewayAPIClient, shareID string) (*collaboration.GetReceivedShareResponse, *response.Response) {
|
||||
func getReceivedShareFromID(ctx context.Context, client gateway.GatewayAPIClient, shareID string) (*collaboration.ReceivedShare, *response.Response) {
|
||||
s, err := client.GetReceivedShare(ctx, &collaboration.GetReceivedShareRequest{
|
||||
Ref: &collaboration.ShareReference{
|
||||
Spec: &collaboration.ShareReference_Id{
|
||||
@@ -376,7 +384,7 @@ func getReceivedShareFromID(ctx context.Context, client gateway.GatewayAPIClient
|
||||
return nil, arbitraryOcsResponse(response.MetaBadRequest.StatusCode, e.Error())
|
||||
}
|
||||
|
||||
return s, nil
|
||||
return s.Share, nil
|
||||
}
|
||||
|
||||
// getSharedResource attempts to get a shared resource from the storage from the resource reference.
|
||||
@@ -403,23 +411,17 @@ func getSharedResource(ctx context.Context, client gateway.GatewayAPIClient, res
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// getSharedResource gets the list of all shares for the current user.
|
||||
func getSharesList(ctx context.Context, client gateway.GatewayAPIClient) (*collaboration.ListReceivedSharesResponse, *response.Response) {
|
||||
shares, err := client.ListReceivedShares(ctx, &collaboration.ListReceivedSharesRequest{})
|
||||
// listReceivedShares list all received shares for the current user.
|
||||
func listReceivedShares(ctx context.Context, client gateway.GatewayAPIClient) ([]*collaboration.ReceivedShare, error) {
|
||||
res, err := client.ListReceivedShares(ctx, &collaboration.ListReceivedSharesRequest{})
|
||||
if err != nil {
|
||||
e := errors.Wrap(err, "error getting shares list")
|
||||
return nil, arbitraryOcsResponse(response.MetaNotFound.StatusCode, e.Error())
|
||||
return nil, errtypes.InternalError("grpc list received shares request failed")
|
||||
}
|
||||
|
||||
if shares.Status.Code != rpc.Code_CODE_OK {
|
||||
if shares.Status.Code == rpc.Code_CODE_NOT_FOUND {
|
||||
e := fmt.Errorf("not found")
|
||||
return nil, arbitraryOcsResponse(response.MetaNotFound.StatusCode, e.Error())
|
||||
}
|
||||
e := fmt.Errorf(shares.GetStatus().GetMessage())
|
||||
return nil, arbitraryOcsResponse(response.MetaServerError.StatusCode, e.Error())
|
||||
if err := errtypes.NewErrtypeFromStatus(res.Status); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return shares, nil
|
||||
return res.Shares, nil
|
||||
}
|
||||
|
||||
// arbitraryOcsResponse abstracts the boilerplate that is creating a response.Response struct.
|
||||
|
||||
@@ -303,11 +303,7 @@ func (h *Handler) CreateShare(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
s, err := conversions.CS3Share2ShareData(ctx, share)
|
||||
if err != nil {
|
||||
response.WriteOCSError(w, r, response.MetaServerError.StatusCode, "error mapping share data", err)
|
||||
return
|
||||
}
|
||||
s := conversions.CS3Share2ShareData(ctx, share)
|
||||
|
||||
h.addFileInfo(ctx, s, statRes.Info)
|
||||
|
||||
@@ -402,12 +398,12 @@ func (h *Handler) updateExistingShareMountpoints(ctx context.Context, shareType
|
||||
}
|
||||
granteeCtx = metadata.AppendToOutgoingContext(granteeCtx, ctxpkg.TokenHeader, authRes.Token)
|
||||
|
||||
lrs, ocsResponse := getSharesList(granteeCtx, client)
|
||||
if ocsResponse != nil {
|
||||
return ocsResponse.OCS.Meta.StatusCode, ocsResponse.OCS.Meta.Message, nil
|
||||
receivedShares, err := listReceivedShares(granteeCtx, client)
|
||||
if err != nil {
|
||||
return response.MetaServerError.StatusCode, "could not list shares", nil
|
||||
}
|
||||
|
||||
for _, s := range lrs.Shares {
|
||||
for _, s := range receivedShares {
|
||||
if s.GetShare().GetId() != share.Id && s.State == collaboration.ShareState_SHARE_STATE_ACCEPTED && utils.ResourceIDEqual(s.Share.ResourceId, info.GetId()) {
|
||||
updateRequest := &collaboration.UpdateReceivedShareRequest{
|
||||
Share: &collaboration.ReceivedShare{
|
||||
@@ -595,12 +591,8 @@ func (h *Handler) GetShare(w http.ResponseWriter, r *http.Request) {
|
||||
})
|
||||
if err == nil && uRes.GetShare() != nil {
|
||||
resourceID = uRes.Share.Share.ResourceId
|
||||
share, err = conversions.CS3Share2ShareData(ctx, uRes.Share.Share)
|
||||
share = conversions.CS3Share2ShareData(ctx, uRes.Share.Share)
|
||||
share.Hidden = uRes.Share.Hidden
|
||||
if err != nil {
|
||||
response.WriteOCSError(w, r, response.MetaServerError.StatusCode, "error mapping share data", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -635,11 +627,7 @@ func (h *Handler) GetShare(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
if err == nil && uRes.GetShare() != nil {
|
||||
resourceID = uRes.Share.ResourceId
|
||||
share, err = conversions.CS3Share2ShareData(ctx, uRes.Share)
|
||||
if err != nil {
|
||||
response.WriteOCSError(w, r, response.MetaServerError.StatusCode, "error mapping share data", err)
|
||||
return
|
||||
}
|
||||
share = conversions.CS3Share2ShareData(ctx, uRes.Share)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -874,11 +862,7 @@ func (h *Handler) updateShare(w http.ResponseWriter, r *http.Request, share *col
|
||||
h.statCache.RemoveStat(currentUser.Id, share.ResourceId)
|
||||
}
|
||||
|
||||
resultshare, err := conversions.CS3Share2ShareData(ctx, uRes.Share)
|
||||
if err != nil {
|
||||
response.WriteOCSError(w, r, response.MetaServerError.StatusCode, "error mapping share data", err)
|
||||
return
|
||||
}
|
||||
resultshare := conversions.CS3Share2ShareData(ctx, uRes.Share)
|
||||
|
||||
statReq := provider.StatRequest{Ref: &provider.Reference{
|
||||
ResourceId: uRes.Share.ResourceId,
|
||||
@@ -1072,11 +1056,7 @@ func (h *Handler) listSharesWithMe(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
data, err := conversions.CS3Share2ShareData(r.Context(), rs.Share)
|
||||
if err != nil {
|
||||
sublog.Debug().Interface("share", rs.Share).Interface("shareData", data).Err(err).Msg("could not CS3Share2ShareData, skipping")
|
||||
continue
|
||||
}
|
||||
data := conversions.CS3Share2ShareData(r.Context(), rs.Share)
|
||||
|
||||
data.State = mapState(rs.GetState())
|
||||
data.Hidden = rs.Hidden
|
||||
|
||||
@@ -281,11 +281,7 @@ func (h *Handler) removeUserShare(w http.ResponseWriter, r *http.Request, share
|
||||
},
|
||||
}
|
||||
|
||||
data, err := conversions.CS3Share2ShareData(ctx, share)
|
||||
if err != nil {
|
||||
response.WriteOCSError(w, r, response.MetaServerError.StatusCode, "deleting share failed", err)
|
||||
return
|
||||
}
|
||||
data := conversions.CS3Share2ShareData(ctx, share)
|
||||
// A deleted share should not have an ID.
|
||||
data.ID = ""
|
||||
|
||||
@@ -337,11 +333,7 @@ func (h *Handler) listUserShares(r *http.Request, filters []*collaboration.Filte
|
||||
|
||||
// build OCS response payload
|
||||
for _, s := range lsUserSharesResponse.Shares {
|
||||
data, err := conversions.CS3Share2ShareData(ctx, s)
|
||||
if err != nil {
|
||||
log.Debug().Interface("share", s).Interface("shareData", data).Err(err).Msg("could not CS3Share2ShareData, skipping")
|
||||
continue
|
||||
}
|
||||
data := conversions.CS3Share2ShareData(ctx, s)
|
||||
|
||||
info, status, err := h.getResourceInfoByID(ctx, client, s.ResourceId)
|
||||
if err != nil || status.Code != rpc.Code_CODE_OK {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user