diff --git a/.drone.star b/.drone.star index 830d75bde..d711f2734 100644 --- a/.drone.star +++ b/.drone.star @@ -147,14 +147,14 @@ def testPipelines(ctx): pipelines.append(coreApiTests(ctx, config['apiTests']['coreBranch'], config['apiTests']['coreCommit'], runPart, config['apiTests']['numberOfParts'], 'ocis')) pipelines += uiTests(ctx, config['uiTests']['phoenixBranch'], config['uiTests']['phoenixCommit']) - pipelines.append(accountsUITests(ctx, config['uiTests']['phoenixBranch'], config['uiTests']['phoenixCommit'], 'owncloud')) + pipelines.append(accountsUITests(ctx, config['uiTests']['phoenixBranch'], config['uiTests']['phoenixCommit'])) return pipelines def testing(ctx, module): steps = generate(module) + [ { 'name': 'vet', - 'image': 'webhippie/golang:1.13', + 'image': 'webhippie/golang:1.14', 'pull': 'always', 'commands': [ 'cd %s' % (module), @@ -169,7 +169,7 @@ def testing(ctx, module): }, { 'name': 'staticcheck', - 'image': 'webhippie/golang:1.13', + 'image': 'webhippie/golang:1.14', 'pull': 'always', 'commands': [ 'cd %s' % (module), @@ -184,7 +184,7 @@ def testing(ctx, module): }, { 'name': 'lint', - 'image': 'webhippie/golang:1.13', + 'image': 'webhippie/golang:1.14', 'pull': 'always', 'commands': [ 'cd %s' % (module), @@ -199,7 +199,7 @@ def testing(ctx, module): }, { 'name': 'test', - 'image': 'webhippie/golang:1.13', + 'image': 'webhippie/golang:1.14', 'pull': 'always', 'commands': [ 'cd %s' % (module), @@ -516,7 +516,7 @@ def uiTestPipeline(suiteName, phoenixBranch = 'master', phoenixCommit = '', stor }, } -def accountsUITests(ctx, phoenixBranch, phoenixCommitId, storage): +def accountsUITests(ctx, phoenixBranch, phoenixCommit, storage = 'owncloud'): return { 'kind': 'pipeline', 'type': 'docker', @@ -525,110 +525,48 @@ def accountsUITests(ctx, phoenixBranch, phoenixCommitId, storage): 'os': 'linux', 'arch': 'amd64', }, - 'steps': [ - { - 'name': 'build-ocis', - 'image': 'webhippie/golang:1.13', - 'pull': 'always', - 'commands': [ - 'cd ocis', - 'make build', - 'mkdir -p /srv/app/ocis/bin', - 'cp bin/ocis /srv/app/ocis/bin', - ], - 'volumes': [ - { - 'name': 'gopath', - 'path': '/srv/app' - }, - ] - }, - { - 'name': 'ocis-server', - 'image': 'webhippie/golang:1.13', - 'pull': 'always', - 'detach': True, - 'environment' : { - #'OCIS_LOG_LEVEL': 'debug', - 'STORAGE_HOME_DRIVER': '%s' % (storage), - 'STORAGE_STORAGE_OCIS_ROOT': '/srv/app/tmp/ocis/storage/users', - 'STORAGE_STORAGE_LOCAL_ROOT': '/srv/app/tmp/ocis/reva/root', - 'STORAGE_STORAGE_OWNCLOUD_DATADIR': '/srv/app/tmp/ocis/owncloud/data', - 'STORAGE_STORAGE_OWNCLOUD_REDIS_ADDR': 'redis:6379', - 'STORAGE_OIDC_ISSUER': 'https://ocis-server:9200', - 'STORAGE_LDAP_IDP': 'https://ocis-server:9200', - 'PROXY_OIDC_ISSUER': 'https://ocis-server:9200', - 'STORAGE_HOME_EXPOSE_DATA_SERVER': 'true', - 'STORAGE_HOME_DATA_SERVER_URL': 'http://ocis-server:9155/data', - 'STORAGE_DATAGATEWAY_PUBLIC_URL': 'https://ocis-server:9200/data', - 'STORAGE_FRONTEND_PUBLIC_URL': 'https://ocis-server:9200', - 'PHOENIX_WEB_CONFIG': '/drone/src/accounts/ui/tests/config/drone/ocis-config.json', - 'KONNECTD_IDENTIFIER_REGISTRATION_CONF': '/drone/src/accounts/ui/tests/config/drone/identifier-registration.yml', - 'KONNECTD_ISS': 'https://ocis-server:9200', - 'ACCOUNTS_STORAGE_DISK_PATH': '/srv/app/tmp/ocis-accounts', # Temporary workaround, don't use metadata storage - }, - 'commands': [ - 'mkdir -p /srv/app/tmp/reva', - # First run settings service because accounts need it to register the settings bundles - '/srv/app/ocis/bin/ocis settings &', - - # Wait for the settings service to start - "while [[ \"$(curl -s -o /dev/null -w ''%{http_code}'' localhost:9190)\" != \"404\" ]]; do sleep 2; done", - - # Now start the accounts service - '/srv/app/ocis/bin/ocis accounts &', - - # Wait for the accounts service to start - "while [[ \"$(curl -s -o /dev/null -w ''%{http_code}'' localhost:9181)\" != \"404\" ]]; do sleep 2; done", - - # Now run all the ocis services except the accounts and settings because they are already running - '/srv/app/ocis/bin/ocis server', - ], - 'volumes': [ - { - 'name': 'gopath', - 'path': '/srv/app' - }, - ] - }, + 'steps': + generate('ocis') + + build() + + ocisServer(storage) + [ { 'name': 'WebUIAcceptanceTests', - 'image': 'owncloudci/nodejs:10', + 'image': 'owncloudci/nodejs:11', 'pull': 'always', 'environment': { 'SERVER_HOST': 'https://ocis-server:9200', 'BACKEND_HOST': 'https://ocis-server:9200', 'RUN_ON_OCIS': 'true', - 'OCIS_REVA_DATA_ROOT': '/srv/app/tmp/ocis/owncloud', + 'OCIS_REVA_DATA_ROOT': '/srv/app/tmp/ocis/owncloud/data', 'OCIS_SKELETON_DIR': '/srv/app/testing/data/webUISkeleton', - 'PHOENIX_CONFIG': '/drone/src/accounts/ui/tests/config/drone/ocis-config.json', + 'PHOENIX_CONFIG': '/drone/src/ocis/tests/config/drone/ocis-config.json', 'TEST_TAGS': 'not @skipOnOCIS and not @skip', 'LOCAL_UPLOAD_DIR': '/uploads', + 'NODE_TLS_REJECT_UNAUTHORIZED': 0, 'PHOENIX_PATH': '/srv/app/phoenix', 'FEATURE_PATH': '/drone/src/accounts/ui/tests/acceptance/features', - 'NODE_TLS_REJECT_UNAUTHORIZED': '0' }, 'commands': [ - 'git clone --depth=1 https://github.com/owncloud/testing.git /srv/app/testing', - 'git clone -b %s --single-branch https://github.com/owncloud/phoenix /srv/app/phoenix' % (phoenixBranch), - 'cd /srv/app/phoenix', - 'git checkout %s' % (phoenixCommitId), + 'git clone -b master --depth=1 https://github.com/owncloud/testing.git /srv/app/testing', + 'git clone -b %s --single-branch --no-tags https://github.com/owncloud/phoenix.git /srv/app/phoenix' % (phoenixBranch), 'cp -r /srv/app/phoenix/tests/acceptance/filesForUpload/* /uploads', + 'cd /srv/app/phoenix', + ] + ([ + 'git checkout %s' % (phoenixCommit) + ] if phoenixCommit != '' else []) + [ 'yarn install-all', 'cd /drone/src/accounts', 'yarn install --all', 'make test-acceptance-webui' ], - 'volumes': [ - { - 'name': 'gopath', - 'path': '/srv/app', - }, - { - 'name': 'uploads', - 'path': '/uploads' - } - ], + 'volumes': [{ + 'name': 'gopath', + 'path': '/srv/app', + }, + { + 'name': 'uploads', + 'path': '/uploads' + }], }, ], 'services': [ @@ -867,7 +805,7 @@ def binary(ctx, name): generate('ocis') + [ { 'name': 'build', - 'image': 'webhippie/golang:1.13', + 'image': 'webhippie/golang:1.14', 'pull': 'always', 'commands': [ 'cd ocis', @@ -882,7 +820,7 @@ def binary(ctx, name): }, { 'name': 'finish', - 'image': 'webhippie/golang:1.13', + 'image': 'webhippie/golang:1.14', 'pull': 'always', 'commands': [ 'cd ocis', @@ -1073,7 +1011,7 @@ def changelog(ctx): 'steps': [ { 'name': 'generate', - 'image': 'webhippie/golang:1.13', + 'image': 'webhippie/golang:1.14', 'pull': 'always', 'commands': [ 'cd ocis', @@ -1237,7 +1175,7 @@ def docs(ctx): }, { 'name': 'generate-config-docs', - 'image': 'webhippie/golang:1.13', + 'image': 'webhippie/golang:1.14', 'commands': generateConfigDocs, 'volumes': [ { @@ -1325,7 +1263,7 @@ def generate(module): return [ { 'name': 'generate', - 'image': 'webhippie/golang:1.13', + 'image': 'webhippie/golang:1.14', 'pull': 'always', 'commands': [ 'cd %s' % (module), @@ -1400,7 +1338,7 @@ def ocisServer(storage): return [ { 'name': 'ocis-server', - 'image': 'webhippie/golang:1.13', + 'image': 'webhippie/golang:1.14', 'pull': 'always', 'detach': True, 'environment' : { @@ -1423,7 +1361,6 @@ def ocisServer(storage): 'KONNECTD_IDENTIFIER_REGISTRATION_CONF': '/drone/src/ocis/tests/config/drone/identifier-registration.yml', 'KONNECTD_ISS': 'https://ocis-server:9200', 'KONNECTD_TLS': 'true', - 'ACCOUNTS_DATA_PATH': '/srv/app/tmp/ocis-accounts/', }, 'commands': [ 'apk add mailcap', # install /etc/mime.types @@ -1489,7 +1426,7 @@ def build(): return [ { 'name': 'build', - 'image': 'webhippie/golang:1.13', + 'image': 'webhippie/golang:1.14', 'pull': 'always', 'commands': [ 'cd ocis', diff --git a/accounts/go.mod b/accounts/go.mod index dc13f404b..a13239f3c 100644 --- a/accounts/go.mod +++ b/accounts/go.mod @@ -3,21 +3,14 @@ module github.com/owncloud/ocis/accounts go 1.13 require ( - github.com/CiscoM31/godata v0.0.0-20191007193734-c2c4ebb1b415 - github.com/blevesearch/bleve v1.0.9 - github.com/cs3org/go-cs3apis v0.0.0-20200730121022-c4f3d4f7ddfd - github.com/cs3org/reva v1.1.0 - github.com/cznic/b v0.0.0-20181122101859-a26611c4d92d // indirect - github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548 // indirect - github.com/cznic/strutil v0.0.0-20181122101858-275e90344537 // indirect - github.com/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c // indirect - github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 // indirect - github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4 // indirect + github.com/cs3org/go-cs3apis v0.0.0-20200810113633-b00aca449666 + github.com/cs3org/reva v1.2.2-0.20200924071957-e6676516e61e github.com/go-chi/chi v4.1.2+incompatible github.com/go-chi/render v1.0.1 github.com/gofrs/uuid v3.3.0+incompatible github.com/golang/protobuf v1.4.2 - github.com/jmhodges/levigo v1.0.0 // indirect + github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99 // indirect + github.com/iancoleman/strcase v0.1.2 github.com/mennanov/fieldmask-utils v0.3.2 github.com/micro/cli/v2 v2.1.2 github.com/micro/go-micro/v2 v2.9.1 @@ -25,16 +18,16 @@ require ( github.com/olekukonko/tablewriter v0.0.4 github.com/owncloud/ocis/ocis-pkg v0.0.0-20200918114005-1a0ddd2190ee github.com/owncloud/ocis/settings v0.0.0-20200918114005-1a0ddd2190ee - github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect + github.com/owncloud/ocis/storage v0.0.0-20201015120921-38358ba4d4df + github.com/prometheus/client_golang v1.7.1 github.com/restic/calens v0.2.0 - github.com/rs/zerolog v1.19.0 + github.com/rs/zerolog v1.20.0 github.com/spf13/viper v1.7.0 github.com/stretchr/testify v1.6.1 - github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c // indirect github.com/tredoe/osutil v1.0.5 golang.org/x/net v0.0.0-20200822124328-c89045814202 google.golang.org/genproto v0.0.0-20200624020401-64a14ca9d1ad - google.golang.org/grpc v1.31.0 + google.golang.org/grpc v1.32.0 google.golang.org/protobuf v1.25.0 ) diff --git a/accounts/go.sum b/accounts/go.sum index 90357906f..07f8c42a2 100644 --- a/accounts/go.sum +++ b/accounts/go.sum @@ -50,8 +50,6 @@ github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c/go.mod h1:chxPXzS github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/CiscoM31/godata v0.0.0-20191007193734-c2c4ebb1b415 h1:rATYsVSP89BOyS9/o+cdGZ8qU7AHh4frtS59nFPoX8k= -github.com/CiscoM31/godata v0.0.0-20191007193734-c2c4ebb1b415/go.mod h1:tjaihnMBH6p5DVnGBksDQQHpErbrLvb9ek6cEWuyc7E= github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= github.com/Masterminds/goutils v1.1.0 h1:zukEsf/1JZwCMgHiK3GZftabmxiCw4apj3a28RPBiVg= github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= @@ -68,10 +66,9 @@ github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcy github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw= github.com/Microsoft/hcsshim v0.8.7-0.20191101173118-65519b62243c/go.mod h1:7xhjOwRV2+0HXGmM0jxaEu+ZiXJFoVZOTfL/dmqbrD8= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk= +github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87/go.mod h1:iGLljf5n9GjT6kc0HBvyI1nOKnGQbNB66VzSNbK5iks= -github.com/RoaringBitmap/roaring v0.4.21 h1:WJ/zIlNX4wQZ9x8Ey33O1UaD9TCTakYsdLFSBcTwH+8= -github.com/RoaringBitmap/roaring v0.4.21/go.mod h1:D0gp8kJQgE1A4LQ5wFLggQEyvDi06Mq5mKs52e1TwOo= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= github.com/UnnoTed/fileb0x v1.1.4 h1:IUgFzgBipF/ujNx9wZgkrKOF3oltUuXMSoaejrBws+A= @@ -103,12 +100,16 @@ github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:l github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496 h1:zV3ejI06GQ59hwDQAvmK1qxOQGB3WuVTRoY0okPTAv0= github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg= +github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535 h1:4daAzAu0S6Vi7/lbWECcX0j45yZReDZ56BQsrVBOEEY= +github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg= github.com/ascarter/requestid v0.0.0-20170313220838-5b76ab3d4aee h1:3T/l+vMotQ7cDSLWNAn2Vg1SAQ3mdyLgBWWBitSS3uU= github.com/ascarter/requestid v0.0.0-20170313220838-5b76ab3d4aee/go.mod h1:u7Wtt4WATGGgae9mURNGQQqxAudPKrxfsbSDSGOso+g= github.com/aws/aws-sdk-go v1.20.1/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.23.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.23.19/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.33.19/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= +github.com/aws/aws-sdk-go v1.34.12 h1:7UbBEYDUa4uW0YmRnOd806MS1yoJMcaodBWDzvBShAI= +github.com/aws/aws-sdk-go v1.34.12/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= github.com/aws/aws-xray-sdk-go v0.9.4/go.mod h1:XtMKdBQfpVut+tJEwI7+dJFRxxRdxHDyVNp2tHXRq04= github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f/go.mod h1:AuiFmCCPBSrqvVMvuqFuk0qogytodnVFVSN5CeJB8Gc= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= @@ -120,26 +121,6 @@ github.com/bitly/go-simplejson v0.5.0 h1:6IH+V8/tVMab511d5bn4M7EwGXZf9Hj6i2xSwkN github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA= github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= -github.com/blevesearch/bleve v1.0.9 h1:kqw/Ank/61UV9/Bx9kCcnfH6qWPgmS8O5LNfpsgzASg= -github.com/blevesearch/bleve v1.0.9/go.mod h1:tb04/rbU29clbtNgorgFd8XdJea4x3ybYaOjWKr+UBU= -github.com/blevesearch/blevex v0.0.0-20190916190636-152f0fe5c040 h1:SjYVcfJVZoCfBlg+fkaq2eoZHTf5HaJfaTeTkOtyfHQ= -github.com/blevesearch/blevex v0.0.0-20190916190636-152f0fe5c040/go.mod h1:WH+MU2F4T0VmSdaPX+Wu5GYoZBrYWdOZWSjzvYcDmqQ= -github.com/blevesearch/go-porterstemmer v1.0.3 h1:GtmsqID0aZdCSNiY8SkuPJ12pD4jI+DdXTAn4YRcHCo= -github.com/blevesearch/go-porterstemmer v1.0.3/go.mod h1:angGc5Ht+k2xhJdZi511LtmxuEf0OVpvUUNrwmM1P7M= -github.com/blevesearch/mmap-go v1.0.2 h1:JtMHb+FgQCTTYIhtMvimw15dJwu1Y5lrZDMOFXVWPk0= -github.com/blevesearch/mmap-go v1.0.2/go.mod h1:ol2qBqYaOUsGdm7aRMRrYGgPvnwLe6Y+7LMvAB5IbSA= -github.com/blevesearch/segment v0.9.0 h1:5lG7yBCx98or7gK2cHMKPukPZ/31Kag7nONpoBt22Ac= -github.com/blevesearch/segment v0.9.0/go.mod h1:9PfHYUdQCgHktBgvtUOF4x+pc4/l8rdH0u5spnW85UQ= -github.com/blevesearch/snowballstem v0.9.0 h1:lMQ189YspGP6sXvZQ4WZ+MLawfV8wOmPoD/iWeNXm8s= -github.com/blevesearch/snowballstem v0.9.0/go.mod h1:PivSj3JMc8WuaFkTSRDW2SlrulNWPl4ABg1tC/hlgLs= -github.com/blevesearch/zap/v11 v11.0.9 h1:wlSrDBeGN1G4M51NQHIXca23ttwUfQpWaK7uhO5lRSo= -github.com/blevesearch/zap/v11 v11.0.9/go.mod h1:47hzinvmY2EvvJruzsSCJpro7so8L1neseaGjrtXHOY= -github.com/blevesearch/zap/v12 v12.0.9 h1:PpatkY+BLVFZf0Ok3/fwgI/I4RU0z5blXFGuQANmqXk= -github.com/blevesearch/zap/v12 v12.0.9/go.mod h1:paQuvxy7yXor+0Mx8p2KNmJgygQbQNN+W6HRfL5Hvwc= -github.com/blevesearch/zap/v13 v13.0.1 h1:NSCM6uKu77Vn/x9nlPp4pE1o/bftqcOWZEHSyZVpGBQ= -github.com/blevesearch/zap/v13 v13.0.1/go.mod h1:XmyNLMvMf8Z5FjLANXwUeDW3e1+o77TTGUWrth7T9WI= -github.com/blevesearch/zap/v14 v14.0.0 h1:HF8Ysjm13qxB0jTGaKLlatNXmJbQD8bY+PrPxm5v4hE= -github.com/blevesearch/zap/v14 v14.0.0/go.mod h1:sUc/gPGJlFbSQ2ZUh/wGRYwkKx+Dg/5p+dd+eq6QMXk= github.com/bmatcuk/doublestar v1.1.1 h1:YroD6BJCZBYx06yYFEWvUuKVWQn3vLLQAVmDmvTSaiQ= github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w= github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY= @@ -147,6 +128,7 @@ github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dR github.com/bmizerany/pat v0.0.0-20170815010413-6226ea591a40 h1:y4B3+GPxKlrigF1ha5FFErxK+sr6sWxQovRMzwMhejo= github.com/bmizerany/pat v0.0.0-20170815010413-6226ea591a40/go.mod h1:8rLXio+WjiTceGBHIoTvn60HIbs7Hm7bcHjyrSqYB9c= github.com/bwmarrin/discordgo v0.20.2/go.mod h1:O9S4p+ofTFwB02em7jkpkV8M3R0/PUVOwN61zSZ0r4Q= +github.com/c-bata/go-prompt v0.2.3/go.mod h1:VzqtzE2ksDBcdln8G7mk2RX9QyGjH+OVqOCSiVIqS34= github.com/caddyserver/certmagic v0.10.6 h1:sCya6FmfaN74oZE46kqfaFOVoROD/mF36rTQfjN7TZc= github.com/caddyserver/certmagic v0.10.6/go.mod h1:Y8jcUBctgk/IhpAzlHKfimZNyXCkfGgRTC0orl8gROQ= github.com/cenkalti/backoff v2.1.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= @@ -162,6 +144,7 @@ github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+ github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ= github.com/cheggaaa/pb v1.0.28/go.mod h1:pQciLPpbU0oxA0h+VJYYLxO+XeDQb5pZijXscXHm81s= +github.com/cheggaaa/pb v1.0.29/go.mod h1:W40334L7FMC5JKWldsTWbdGjLo0RxUKK73K+TuPxX30= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= @@ -200,10 +183,6 @@ github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f h1:JOrtw2xFKzlg+ github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f h1:lBNOc5arjvs8E5mO2tbpBpLoyyu8B6e44T7hJy6potg= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/couchbase/ghistogram v0.1.0/go.mod h1:s1Jhy76zqfEecpNWJfWUiKZookAFaiGOEoyzgHt9i7k= -github.com/couchbase/moss v0.1.0/go.mod h1:9MaHIaRuy9pvLPUJxB8sh8OrLfyDczECVL37grCIubs= -github.com/couchbase/vellum v1.0.1 h1:qrj9ohvZedvc51S5KzPfJ6P6z0Vqzv7Lx7k3mVc2WOk= -github.com/couchbase/vellum v1.0.1/go.mod h1:FcwrEivFpNi24R3jLOs3n+fs5RnuQnQqCLBJ1uAg1W4= github.com/cpu/goacmedns v0.0.1/go.mod h1:sesf/pNnCYwUevQEQfEwY0Y3DydlQWSGZbaMElOWxok= github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= @@ -213,21 +192,26 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsr github.com/creack/pty v1.1.9 h1:uDmaGzcdjhF4i/plgjmEsriH11Y0o7RKapEf/LDaM3w= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/cs3org/cato v0.0.0-20200626150132-28a40e643719/go.mod h1:XJEZ3/EQuI3BXTp/6DUzFr850vlxq11I6satRtz0YQ4= +github.com/cs3org/cato v0.0.0-20200828125504-e418fc54dd5e/go.mod h1:XJEZ3/EQuI3BXTp/6DUzFr850vlxq11I6satRtz0YQ4= github.com/cs3org/go-cs3apis v0.0.0-20200730121022-c4f3d4f7ddfd h1:uMaudkC7znaiIKT9rxIhoRYzrhTg1Nc78X7XEqhmjSk= github.com/cs3org/go-cs3apis v0.0.0-20200730121022-c4f3d4f7ddfd/go.mod h1:UXha4TguuB52H14EMoSsCqDj7k8a/t7g4gVP+bgY5LY= +github.com/cs3org/go-cs3apis v0.0.0-20200810113633-b00aca449666 h1:E7VsSSN/2YZLSwrDMJJdAWU11lP7W1qkcXbrslb0PM0= +github.com/cs3org/go-cs3apis v0.0.0-20200810113633-b00aca449666/go.mod h1:UXha4TguuB52H14EMoSsCqDj7k8a/t7g4gVP+bgY5LY= github.com/cs3org/reva v1.1.0 h1:Gih6ECHvMMGSx523SFluFlDmNMuhYelXYShdWvjvW38= github.com/cs3org/reva v1.1.0/go.mod h1:fBzTrNuAKdQ62ybjpdu8nyhBin90/3/3s6DGQDCdBp4= -github.com/cznic/b v0.0.0-20181122101859-a26611c4d92d h1:SwD98825d6bdB+pEuTxWOXiSjBrHdOl/UVp75eI7JT8= -github.com/cznic/b v0.0.0-20181122101859-a26611c4d92d/go.mod h1:URriBxXwVq5ijiJ12C7iIZqlA69nTlI+LgI6/pwftG8= -github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548 h1:iwZdTE0PVqJCos1vaoKsclOGD3ADKpshg3SRtYBbwso= -github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548/go.mod h1:e6NPNENfs9mPDVNRekM7lKScauxd5kXTr1Mfyig6TDM= -github.com/cznic/strutil v0.0.0-20181122101858-275e90344537 h1:MZRmHqDBd0vxNwenEbKSQqRVT24d3C05ft8kduSwlqM= -github.com/cznic/strutil v0.0.0-20181122101858-275e90344537/go.mod h1:AHHPPPXTw0h6pVabbcbyGRK1DckRn7r/STdZEeIDzZc= +github.com/cs3org/reva v1.2.2-0.20200924071957-e6676516e61e h1:khITGSnfDXtByQsLezoXgocUgGHJBBn0BPsUihGvk7w= +github.com/cs3org/reva v1.2.2-0.20200924071957-e6676516e61e/go.mod h1:DOV5SjpOBKN+aWfOHLdA4KiLQkpyC786PQaXEdRAZ0M= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgraph-io/ristretto v0.0.1/go.mod h1:T40EBc7CJke8TkpiYfGGKAeFjSaxuFXhuXRyumBd6RE= +github.com/dgraph-io/ristretto v0.0.2/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= +github.com/dgraph-io/ristretto v0.0.3 h1:jh22xisGBjrEVnRZ1DVTpBVQm0Xndu8sMl0CWDzSIBI= +github.com/dgraph-io/ristretto v0.0.3/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA= +github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8= github.com/dnaeon/go-vcr v0.0.0-20180814043457-aafff18a5cc2/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= @@ -254,13 +238,8 @@ github.com/evanphx/json-patch/v5 v5.0.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2Vvl github.com/eventials/go-tus v0.0.0-20200718001131-45c7ec8f5d59 h1:t2+zxJPT/jq/YOx/JRsoByAZI/GHOxYJ7MKeillEX4U= github.com/eventials/go-tus v0.0.0-20200718001131-45c7ec8f5d59/go.mod h1:XYuK1S5+kS6FGhlIUFuZFPvWiSrOIoLk6+ro33Xce3Y= github.com/exoscale/egoscale v0.18.1/go.mod h1:Z7OOdzzTOz1Q1PjQXumlz9Wn/CddH0zSYdCF3rnBKXE= -github.com/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c h1:8ISkoahWXwZR41ois5lSJBSVw4D0OV19Ht/JSTzvSv0= -github.com/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c/go.mod h1:Yg+htXGokKKdzcwhuNDwVvN+uBxDGXJ7G/VN1d8fa64= -github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 h1:JWuenKqqX8nojtoVVWjGfOF9635RETekkoH6Cc9SX0A= -github.com/facebookgo/stack v0.0.0-20160209184415-751773369052/go.mod h1:UbMTZqLaRiH3MsBH8va0n7s1pQYcu3uTb8G4tygF4Zg= -github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4 h1:7HZCaLC5+BZpmbhCOZJ293Lz68O7PYrF2EzeiFMwCLk= -github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4/go.mod h1:5tD+neXqOorC30/tWg0LCSkrqj/AR6gu8yY8/fpw1q0= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fatih/structs v1.0.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ= @@ -268,6 +247,8 @@ github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI github.com/forestgiant/sliceutil v0.0.0-20160425183142-94783f95db6c/go.mod h1:pFdJbAhRf7rh6YYMUdIQGyzne6zYL1tCUW8QV2B3UfY= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsouza/go-dockerclient v1.6.0/go.mod h1:YWwtNPuL4XTX1SKJQk86cWPmmqwx+4np9qfPbb+znGc= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= @@ -275,10 +256,6 @@ github.com/gizak/termui/v3 v3.1.0 h1:ZZmVDgwHl7gR7elfKf1xc4IudXZ5qqfDh4wExk4Iajc github.com/gizak/termui/v3 v3.1.0/go.mod h1:bXQEBkJpzxUAKf0+xq9MSWAvWZlE7c+aidmyFlkYTrY= github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0= github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= -github.com/glycerine/go-unsnap-stream v0.0.0-20181221182339-f9677308dec2 h1:Ujru1hufTHVb++eG6OuNDKMxZnGIvF6o/u8q/8h2+I4= -github.com/glycerine/go-unsnap-stream v0.0.0-20181221182339-f9677308dec2/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE= -github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31 h1:gclg6gY70GLy3PbkQ1AERPfmLMMagS60DKF78eWwLn8= -github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24= github.com/go-acme/lego/v3 v3.4.0 h1:deB9NkelA+TfjGHVw8J7iKl/rMtffcGMWSMmptvMv0A= github.com/go-acme/lego/v3 v3.4.0/go.mod h1:xYbLDuxq3Hy4bMUT1t9JIuz6GWIWb3m5X+TeTHYaT7M= github.com/go-asn1-ber/asn1-ber v1.5.1 h1:pDbRAunXzIUXfx4CB2QJFv5IuPiuoW+sWvr/Us009o8= @@ -517,6 +494,7 @@ github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfb github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3 h1:GV+pQPG/EUUbkh47niozDcADz6go/dUwhVzdUQHIVRw= github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -534,8 +512,7 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= -github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/gomodule/redigo v1.8.2 h1:H5XSIre1MB5NbPYFp+i1NBbb5qN1W8Y8YAQoAYbkm8k= github.com/gomodule/redigo v1.8.2/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo= @@ -561,11 +538,14 @@ github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/google/uuid v1.1.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/gophercloud/gophercloud v0.3.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= github.com/gopherjs/gopherjs v0.0.0-20181004151105-1babbf986f6f/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99 h1:twflg0XRTjwKpxb/jFExr4HGq6on2dEOmnL6FV+fgPw= github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= @@ -588,6 +568,8 @@ github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmg github.com/grpc-ecosystem/go-grpc-middleware v1.1.0/go.mod h1:f5nM7jw/oeRSadq3xCzHAvxcr8HZnzsqU6ILg/0NiiE= github.com/grpc-ecosystem/go-grpc-middleware v1.2.0 h1:0IKlLyQ3Hs9nDaiK5cSHAGmcQEIC8l2Ts1u6x5Dfrqg= github.com/grpc-ecosystem/go-grpc-middleware v1.2.0/go.mod h1:mJzapYve32yjrKlk9GbyCZHuPgZsrbyIbyKhSzOpg6s= +github.com/grpc-ecosystem/go-grpc-middleware v1.2.2 h1:FlFbCRLd5Jr4iYXZufAvgWN6Ao0JrI5chLINnUXDDr0= +github.com/grpc-ecosystem/go-grpc-middleware v1.2.2/go.mod h1:EaizFBKfUKtMIF5iaDEhniwNedqGo9FuLFzppDr3uwI= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.8.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= @@ -631,12 +613,15 @@ github.com/huandu/xstrings v1.2.0 h1:yPeWdRnmynF7p+lLYz0H2tthW9lqhMJrQV/U7yy4wX0 github.com/huandu/xstrings v1.2.0/go.mod h1:DvyZB1rfVYsBIigL8HwpZgxHwXozlTgGqn63UyNX5k4= github.com/huandu/xstrings v1.3.0 h1:gvV6jG9dTgFEncxo+AF7PH6MZXi/vZl25owA/8Dg8Wo= github.com/huandu/xstrings v1.3.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= +github.com/iancoleman/strcase v0.1.2 h1:gnomlvw9tnV3ITTAxzKSgTF+8kFWcU/f+TgttpXGz1U= +github.com/iancoleman/strcase v0.1.2/go.mod h1:SK73tn/9oHe+/Y0h39VT4UCxmurVJkR5NA7kMEAOgSE= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df/go.mod h1:QMZY7/J/KSQEhKWFeDesPjMj+wCHReeknARU3wqlyN4= github.com/imdario/mergo v0.3.7/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.9 h1:UauaLniWCFHWd+Jp9oCEkTBj8VO/9DKg3PV3VCNMDIg= github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/jackc/fake v0.0.0-20150926172116-812a484cc733/go.mod h1:WrMFNQdiFJ80sQsxDoMokWK1W5TQtxBFNpzWTD84ibQ= github.com/jackc/pgx v3.2.0+incompatible/go.mod h1:0ZGrqGqkRlliWnWB4zKnWtjbSWbGkVEFm4TeybAXq+I= @@ -647,8 +632,6 @@ github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJS github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.3.0 h1:OS12ieG61fsCg5+qLJ+SsW9NicxNkg3b25OyT2yCeUc= github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik= -github.com/jmhodges/levigo v1.0.0 h1:q5EC36kV79HWeTBWsod3mG11EgStG3qArTKcvlksN1U= -github.com/jmhodges/levigo v1.0.0/go.mod h1:Q6Qx+uH3RAqyK4rFQroq9RL7mdkABMcfhEI+nNuzMJQ= github.com/jmoiron/sqlx v0.0.0-20180614180643-0dae4fefe7c0/go.mod h1:IiEW3SEiiErVyFdH8NTuWjSifiEQKUoyK3LNqr2kCHU= github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks= github.com/joho/godotenv v1.2.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= @@ -681,15 +664,17 @@ github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvW github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/cpuid v1.2.3 h1:CCtW0xUnWGVINKvE/WWOYKdsPV6mawAtvQuSl8guwQs= github.com/klauspost/cpuid v1.2.3/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= -github.com/kljensen/snowball v0.6.0/go.mod h1:27N7E8fVU5H68RlUmnWwZCfxgt4POBJfENGMvNRhldw= github.com/kolo/xmlrpc v0.0.0-20190717152603-07c4ee3fd181/go.mod h1:o03bZfuBwAXHetKXuInt4S7omeXUu62/A845kiycsSQ= github.com/konsorten/go-windows-terminal-sequences v0.0.0-20180402223658-b729f2633dfe/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= +github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= @@ -733,9 +718,12 @@ github.com/marten-seemann/qpack v0.1.0/go.mod h1:LFt1NU/Ptjip0C2CPkhimBz5CGE3WGD github.com/marten-seemann/qtls v0.4.1/go.mod h1:pxVXcHHw1pNIt8Qo0pwSYQEoZ8yYOOPXTCZLQQunvRc= github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= @@ -749,6 +737,8 @@ github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW github.com/mattn/go-tty v0.0.0-20180219170247-931426f7535a/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE= github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw= github.com/mattn/goveralls v0.0.5/go.mod h1:Xg2LHi51faXLyKXwsndxiW6uxEEQT9+3sjGzzwU4xy0= +github.com/mattn/goveralls v0.0.6 h1:cr8Y0VMo/MnEZBjxNN/vh6G90SZ7IMb6lms1dzMoO+Y= +github.com/mattn/goveralls v0.0.6/go.mod h1:h8b4ow6FxSPMQHF6o2ve3qsclnffZjYTNEKmLesRwqw= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mennanov/fieldmask-utils v0.3.2 h1:AkHXYBEOoyvocl8YhzoStATRnto5OH1PY4Rj78I5Cuc= @@ -788,6 +778,7 @@ github.com/mitchellh/mapstructure v1.0.0/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.3.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.3.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.3.3 h1:SzB1nHZ2Xi+17FP0zVQBHIZqvwRN9408fJO8h+eeNA8= github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY= @@ -803,9 +794,6 @@ github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwd github.com/monoculum/formam v0.0.0-20180901015400-4e68be1d79ba/go.mod h1:RKgILGEJq24YyJ2ban8EO0RUVSJlF1pGsEvoLEACr/Q= github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/moul/http2curl v0.0.0-20170919181001-9ac6cf4d929b/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ= -github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae/go.mod h1:qAyveg+e4CE+eKJXWVjKXM4ck2QobLqTDytGJbLLhJg= -github.com/mschoch/smat v0.2.0 h1:8imxQsjDm8yFEAVBe7azKmKSgzSkZXDuKkSq9374khM= -github.com/mschoch/smat v0.2.0/go.mod h1:kc9mz7DoBKqDyiRL7VZN8KvXQMWeTaVnttLRXOlotKw= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04/go.mod h1:5sN+Lt1CaY4wsPvgQH/jsuJi4XO2ssZbdsIizr4CVC8= github.com/nats-io/jwt v0.3.2 h1:+RB5hMpXUUA2dfxuhBTEkMOrYmM+gKIZYS1KjSostMI= @@ -834,6 +822,7 @@ github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQ github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= +github.com/oleiade/reflections v1.0.0 h1:0ir4pc6v8/PJ0yw5AEtMddfXpWBXg9cnG7SgSoJuCgY= github.com/oleiade/reflections v1.0.0/go.mod h1:RbATFBbKYkVdqmSFtx13Bb/tVhR0lgOBXunWTZKeL4w= github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/olekukonko/tablewriter v0.0.4 h1:vHD/YYe1Wolo78koG299f7V/VAS08c6IpCLn+Ejf/w8= @@ -862,16 +851,25 @@ github.com/ory/dockertest v3.3.5+incompatible/go.mod h1:1vX4m9wsvi00u5bseYwXaSnh github.com/ory/fosite v0.29.0/go.mod h1:0atSZmXO7CAcs6NPMI/Qtot8tmZYj04Nddoold4S2h0= github.com/ory/fosite v0.32.2 h1:iRV495P/9EyoYQ8qEHYxFQeeYCdDFawqjAML+qiMF9s= github.com/ory/fosite v0.32.2/go.mod h1:UeBhRgW6nAjTcd8S7kAo0IFsY/rTPyOXPq/t8N20Q8I= +github.com/ory/fosite v0.33.0 h1:tK+3Luazv4vIBJY3uagOBryAQ3IG3cs6kfo8piGBhAY= +github.com/ory/fosite v0.33.0/go.mod h1:h+ize9gk0GvRyGjabriqSEmTkMhny+O95cijb8DVqPE= github.com/ory/go-acc v0.0.0-20181118080137-ddc355013f90/go.mod h1:sxnvPCxChFuSmTJGj8FdMupeq1BezCiEpDjTUXQ4hf4= github.com/ory/go-acc v0.2.1/go.mod h1:0omgy2aa3nDBJ45VAKeLHH8ccPBudxLeic4xiDRtug0= +github.com/ory/go-acc v0.2.5 h1:31irXHzG2vnKQSE4weJm7AdfrnpaVjVCq3nD7viXCJE= +github.com/ory/go-acc v0.2.5/go.mod h1:4Kb/UnPcT8qRAk3IAxta+hvVapdxTLWtrr7bFLlEgpw= github.com/ory/go-convenience v0.1.0 h1:zouLKfF2GoSGnJwGq+PE/nJAE6dj2Zj5QlTgmMTsTS8= github.com/ory/go-convenience v0.1.0/go.mod h1:uEY/a60PL5c12nYz4V5cHY03IBmwIAEm8TWB0yn9KNs= github.com/ory/gojsonreference v0.0.0-20190720135523-6b606c2d8ee8/go.mod h1:wsH1C4nIeeQClDtD5AH7kF1uTS6zWyqfjVDTmB0Em7A= github.com/ory/gojsonschema v1.1.1-0.20190919112458-f254ca73d5e9/go.mod h1:BNZpdJgB74KOLSsWFvzw6roXg1I6O51WO8roMmW+T7Y= github.com/ory/herodot v0.6.2/go.mod h1:3BOneqcyBsVybCPAJoi92KN2BpJHcmDqAMcAAaJiJow= github.com/ory/viper v1.5.6/go.mod h1:TYmpFpKLxjQwvT4f0QPpkOn4sDXU1kDgAwJpgLYiQ28= +github.com/ory/viper v1.7.5 h1:+xVdq7SU3e1vNaCsk/ixsfxE4zylk1TJUiJrY647jUE= +github.com/ory/viper v1.7.5/go.mod h1:ypOuyJmEUb3oENywQZRgeAMwqgOyDqwboO1tj3DjTaM= github.com/ory/x v0.0.85/go.mod h1:s44V8t3xyjWZREcU+mWlp4h302rTuM4aLXcW+y5FbQ8= github.com/ovh/go-ovh v0.0.0-20181109152953-ba5adb4cf014/go.mod h1:joRatxRJaZBsY3JAOEMcoOp05CnZzsx4scTxi95DHyQ= +github.com/owncloud/flaex v0.0.0-20200411150708-dce59891a203/go.mod h1:jip86t4OVURJTf8CM/0e2qcji/Y4NG3l2lR8kex4JWw= +github.com/owncloud/ocis/storage v0.0.0-20201015120921-38358ba4d4df h1:PhRLD+WTGIfQ1T4MqBabp6/1Q8H/iwxjlygh6xzao0A= +github.com/owncloud/ocis/storage v0.0.0-20201015120921-38358ba4d4df/go.mod h1:s9kJvxtBlHEi5qc1TuPAdz2bprk9yGFe+FSOeC76Pbs= github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c h1:rp5dCmg/yLR3mgFuSOe4oEnDDmGLROTvMragMUXpTQw= github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c/go.mod h1:X07ZCGwUbLaax7L0S3Tw4hpejzu63ZrrQiUe6W0hcy0= github.com/parnurzeal/gorequest v0.2.15/go.mod h1:3Kh2QUMJoqw3icWAecsyzkpY7UzRfDhbRdTjtNwNiUE= @@ -884,8 +882,8 @@ github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181 github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.4.0 h1:u3Z1r+oOXJIkxqw34zVhyPgjBsm6X2wn21NWs/HfSeg= github.com/pelletier/go-toml v1.4.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo= -github.com/philhofer/fwd v1.0.0 h1:UbZqGr5Y38ApvM/V/jEljVxwocdweyH+vmYvRPBnbqQ= -github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= +github.com/pelletier/go-toml v1.8.0 h1:Keo9qb7iRJs2voHvunFtuuYFsbWeOBh8/P9v/kVMFtw= +github.com/pelletier/go-toml v1.8.0/go.mod h1:D6yutnOGMveHEPV7VQOuvI/gXY61bv+9bAOTRnLElKs= github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -893,6 +891,8 @@ github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= +github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= +github.com/pkg/term v0.0.0-20200520122047-c3ffed290a03/go.mod h1:Z9+Ul5bCbBKnbCvdOWbLqTHhJiYV414CURZJba6L8qA= github.com/pkg/xattr v0.4.1 h1:dhclzL6EqOXNaPDWqoeb9tIxATfBSmjqL0b4DpSjwRw= github.com/pkg/xattr v0.4.1/go.mod h1:W2cGD0TBEus7MkUgv0tNZ9JutLtVO3cXu+IBRuHqnFs= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -936,9 +936,6 @@ github.com/prometheus/statsd_exporter v0.15.0/go.mod h1:Dv8HnkoLQkeEjkIE4/2ndAA7 github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/rainycape/memcache v0.0.0-20150622160815-1031fa0ce2f2/go.mod h1:7tZKcyumwBO6qip7RNQ5r77yrssm9bfCowcLEBcU5IA= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= -github.com/rcrowley/go-metrics v0.0.0-20190826022208-cac0b30c2563/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= -github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk= -github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/restic/calens v0.2.0 h1:LVNAtmFc+Pb4ODX66qdX1T3Di1P0OTLyUsVyvM/xD7E= github.com/restic/calens v0.2.0/go.mod h1:UXwyAKS4wsgUZGEc7NrzzygJbLsQZIo3wl+62Q1wvmU= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= @@ -953,6 +950,8 @@ github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/rs/zerolog v1.19.0 h1:hYz4ZVdUgjXTBUmrkrw55j1nHx68LfOKIQk5IYtyScg= github.com/rs/zerolog v1.19.0/go.mod h1:IzD0RJ65iWH0w97OQQebJEvTZYvsCUm9WVLWBQrJRjo= +github.com/rs/zerolog v1.20.0 h1:38k9hgtUBdxFwE34yS8rTHmHBa4eN16E4DJlv177LNs= +github.com/rs/zerolog v1.20.0/go.mod h1:IzD0RJ65iWH0w97OQQebJEvTZYvsCUm9WVLWBQrJRjo= github.com/rubenv/sql-migrate v0.0.0-20190212093014-1007f53448d7/go.mod h1:WS0rl9eEliYI8DPnr3TOwz4439pay+qNgzJoVya/DmY= github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= @@ -1001,17 +1000,24 @@ github.com/soheilhy/cmux v0.1.4 h1:0HKaf1o97UwFjHH9o5XsHUOF+tqmdA7KEzXLpiyaw0E= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE= github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 h1:qLC7fQah7D6K1B0ujays3HV9gkFtllcxhzImRR7ArPQ= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.2.0/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= +github.com/spf13/afero v1.3.2 h1:GDarE4TJQI52kYSbSAmLiId1Elfj+xgSDqrUZxFhxlU= +github.com/spf13/afero v1.3.2/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= github.com/spf13/cast v1.2.0/go.mod h1:r2rcYCSwa1IExKTDiTfzaxqT2FNHs8hODu4LnUfgKEg= github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng= +github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= +github.com/spf13/cobra v1.0.0 h1:6m/oheQuQ13N9ks4hubMG6BnvwOeaJrqSPLahSnczz8= +github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= @@ -1023,11 +1029,10 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An github.com/spf13/viper v1.2.1/go.mod h1:P4AexN0a+C9tGAnUFNwDMYYZv3pjFuvmeiMyKRaNVlI= github.com/spf13/viper v1.3.1/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= +github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= github.com/spf13/viper v1.6.3/go.mod h1:jUMtyi0/lB5yZH/FjyGAoH7IMNrIhlBf6pXZmbMDvzw= github.com/spf13/viper v1.7.0 h1:xVKxvI7ouOI5I+U9s2eeiUfMaWBVoXA3AWskkrqK0VM= github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= -github.com/steveyen/gtreap v0.1.0 h1:CjhzTa274PyJLJuMZwIzCO1PfC00oRa8d1Kc78bFXJM= -github.com/steveyen/gtreap v0.1.0/go.mod h1:kl/5J7XbrOmlIbYIXdRHDDE5QxHqpk0cmkT7Z4dM9/Y= github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -1038,22 +1043,20 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/studio-b12/gowebdav v0.0.0-20200303150724-9380631c29a1 h1:TPyHV/OgChqNcnYqCoCvIFjR9TU60gFXXBKnhOBzVEI= +github.com/studio-b12/gowebdav v0.0.0-20200303150724-9380631c29a1/go.mod h1:gCcfDlA1Y7GqOaeEKw5l9dOGx1VLdc/HuQSlQAaZ30s= github.com/subosito/gotenv v1.1.1/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE= github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= -github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c h1:g+WoO5jjkqGAzHWCjJB1zZfXPIAaDpzXIEJ0eS6B5Ok= -github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c/go.mod h1:ahpPrc7HpcfEWDQRZEmnXMzHY03mLDYMCxeDzy46i+8= github.com/technoweenie/multipartstreamer v1.0.1/go.mod h1:jNVxdtShOxzAsukZwTSw6MDx5eUJoiEBsSvzDU9uzog= github.com/tidwall/gjson v1.3.2/go.mod h1:P256ACg0Mn+j1RXIDXoss50DeIABTYK1PULOJHhxOls= github.com/tidwall/match v1.0.1/go.mod h1:LujAq0jyVjBy028G1WhWfIzbpQfMO8bBZ6Tyb0+pL9E= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tidwall/sjson v1.0.4/go.mod h1:bURseu1nuBkFpIES5cz6zBtjmYeOQmEESshn7VpF15Y= github.com/timewasted/linode v0.0.0-20160829202747-37e84520dcf7/go.mod h1:imsgLplxEC/etjIhdr3dNzV3JeT27LbVu5pYWm0JCBY= -github.com/tinylib/msgp v1.1.0 h1:9fQd+ICuRIu/ue4vxJZu6/LzxN0HwMds2nq/0cFvxHU= -github.com/tinylib/msgp v1.1.0/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20200122045848-3419fae592fc h1:yUaosFVTJwnltaHbSNC3i82I92quFs+OFPRl8kNMVwo= github.com/tmc/grpc-websocket-proxy v0.0.0-20200122045848-3419fae592fc/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= @@ -1085,8 +1088,6 @@ github.com/valyala/fasttemplate v0.0.0-20170224212429-dcecefd839c4 h1:gKMu1Bf6QI github.com/valyala/fasttemplate v0.0.0-20170224212429-dcecefd839c4/go.mod h1:50wTf68f99/Zt14pr046Tgt3Lp2vLyFZKzbFXTOabXw= github.com/vimeo/go-util v1.2.0/go.mod h1:s13SMDTSO7AjH1nbgp707mfN5JFIWUFDU5MDDuRRtKs= github.com/vultr/govultr v0.1.4/go.mod h1:9H008Uxr/C4vFNGLqKx232C206GL0PBHzOP0809bGNA= -github.com/willf/bitset v1.1.10 h1:NotGKqX0KwQ72NUzqrjZq5ipPNDQex9lo3WpaS8L2sc= -github.com/willf/bitset v1.1.10/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= github.com/xanzy/ssh-agent v0.2.1 h1:TCbipTQL2JiiCprBWx9frJ2eJlCYT00NmctrHxVAr70= github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= @@ -1098,6 +1099,8 @@ github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c/go.mod h1:UrdRz5enIKZ63MEE3IF9l2/ebyx59GyGgPi+tICQdmM= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.4 h1:hi1bXHMVrlQh6WwxAy+qZCV/SYIlqo+Ushwdpa4tAKg= @@ -1150,16 +1153,20 @@ golang.org/x/crypto v0.0.0-20190418165655-df01cb2cc480/go.mod h1:WFFai1msRO1wXaE golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190829043050-9756ffdc2472/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190927123631-a832865fa7ad/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200320181102-891825fb96df/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899 h1:DZhuSZLsGlFL4CmhA8BcRA0mnthyA/nZ00AqCUo7vHg= +golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -1170,6 +1177,7 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= @@ -1190,6 +1198,8 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180611182652-db08ff08e862/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180816102801-aaf60122140d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1232,6 +1242,7 @@ golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200421231249-e086a090c8fd/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200625001655-4c5254603344 h1:vGXIOMxbNfDTk/aXCmfdLgkrSV+Z2tcbze+pEc3v5W4= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= @@ -1278,12 +1289,12 @@ golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181206074257-70b957f3b65e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181221143128-b4a75ba826a6/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190102155601-82a175fd1598/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190116161447-11f53e031339/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1297,12 +1308,13 @@ golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190712062909-fae7ac547cb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191105231009-c1f44814a5cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191113165036-4c7a9d0fe056/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1310,21 +1322,28 @@ golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200523222454-059865788121 h1:rITEj+UZHYC927n8GT97eC3zrpzXdb/voyeOuVKS46o= golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1 h1:ogLJMz+qpzav7lGMh10LMvAkM/fAoGlaiiHYiFYdm80= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200720211630-cb9d2d5c5666 h1:gVCS+QOncANNPlmlO1AhlU3oxs4V9z+gTtPwIk3p2N8= +golang.org/x/sys v0.0.0-20200720211630-cb9d2d5c5666/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190921001708-c4c64cad1fd0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -1392,6 +1411,9 @@ golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapK golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4 h1:kDtqNkeBrZb8B+atrj50B5XLHpzXXqcCdZPP/ApQ5NY= golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200522201501-cb1345f3a375/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200721223218-6123e77877b2 h1:kxDWg8KNMtpGjI/XVKGgOtSljTnVg/PrjhS8+0pxjLE= +golang.org/x/tools v0.0.0-20200721223218-6123e77877b2/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 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 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= @@ -1441,6 +1463,7 @@ google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4 google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20200527145253-8367513e4ece h1:1YM0uhfumvoDu9sx8+RyWwTI63zoCQvI23IYFRlvte0= @@ -1483,6 +1506,8 @@ gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.44.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.57.0 h1:9unxIsFcTt4I55uWluz+UmL95q4kdJ0buvQ1ZIqVQww= +gopkg.in/ini.v1 v1.57.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/mail.v2 v2.0.0-20180731213649-a0242b2233b4/go.mod h1:htwXN1Qh09vZJ1NVKxQqHPBaCBbzKhp5GzuJEA4VJWw= gopkg.in/ns1/ns1-go.v2 v2.0.0-20190730140822-b51389932cbc/go.mod h1:VV+3haRsgDiVLxyifmMBrBIuCWFBPYKbRssXB9z67Hw= gopkg.in/resty.v1 v1.9.1/go.mod h1:vo52Hzryw9PnPHcJfPsBiFW62XhNx5OczbV9y+IMpgc= @@ -1492,6 +1517,8 @@ gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76 gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/square/go-jose.v2 v2.5.0 h1:OZ4sdq+Y+SHfYB7vfthi1Ei8b0vkP8ZPQgUfUwdUSqo= gopkg.in/square/go-jose.v2 v2.5.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/square/go-jose.v2 v2.5.1 h1:7odma5RETjNHWJnR32wx8t+Io4djHE1PqxCFx3iiZ2w= +gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/telegram-bot-api.v4 v4.6.4/go.mod h1:5DpGO5dbumb40px+dXcwCpcjmeHNYLpk0bp3XRNvWDM= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= @@ -1505,6 +1532,9 @@ gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5 h1:ymVxjfMaHvXD8RqPRmzHHsB3VvucivSkIAvJFDI5O3c= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c h1:grhR+C34yXImVGp7EzNk+DTIk+323eIUWOmEevy6bDo= diff --git a/accounts/pkg/config/config.go b/accounts/pkg/config/config.go index 6240b06b1..d3730f003 100644 --- a/accounts/pkg/config/config.go +++ b/accounts/pkg/config/config.go @@ -77,16 +77,27 @@ type CS3 struct { ProviderAddr string DataURL string DataPrefix string + JWTSecret string } -// ServiceUser defines the user required for EOS +// ServiceUser defines the user required for EOS. type ServiceUser struct { - UUID string + UUID string Username string UID int64 GID int64 } +// Index defines config for indexes. +type Index struct { + UID, GID Bound +} + +// Bound defines a lower and upper bound. +type Bound struct { + Lower, Upper int64 +} + // Config merges all Account config parameters. type Config struct { LDAP LDAP @@ -97,6 +108,7 @@ type Config struct { Log Log TokenManager TokenManager Repo Repo + Index Index ServiceUser ServiceUser } diff --git a/accounts/pkg/flagset/flagset.go b/accounts/pkg/flagset/flagset.go index e23642636..88e67f274 100644 --- a/accounts/pkg/flagset/flagset.go +++ b/accounts/pkg/flagset/flagset.go @@ -127,6 +127,13 @@ func ServerWithConfig(cfg *config.Config) []cli.Flag { EnvVars: []string{"ACCOUNTS_STORAGE_CS3_DATA_PREFIX"}, Destination: &cfg.Repo.CS3.DataPrefix, }, + &cli.StringFlag{ + Name: "storage-cs3-jwt-secret", + Value: "Pive-Fumkiu4", + Usage: "Used to create JWT to talk to reva, should equal reva's jwt-secret", + EnvVars: []string{"ACCOUNTS_STORAGE_CS3_JWT_SECRET"}, + Destination: &cfg.Repo.CS3.JWTSecret, + }, &cli.StringFlag{ Name: "service-user-uuid", Value: "95cb8724-03b2-11eb-a0a6-c33ef8ef53ad", @@ -155,6 +162,34 @@ func ServerWithConfig(cfg *config.Config) []cli.Flag { EnvVars: []string{"ACCOUNTS_SERVICE_USER_GID"}, Destination: &cfg.ServiceUser.GID, }, + &cli.Int64Flag{ + Name: "uid-index-lower-bound", + Value: 0, + Usage: "define a starting point for the account UID", + EnvVars: []string{"ACCOUNTS_UID_INDEX_LOWER_BOUND"}, + Destination: &cfg.Index.UID.Lower, + }, + &cli.Int64Flag{ + Name: "gid-index-lower-bound", + Value: 1000, + Usage: "define a starting point for the account GID", + EnvVars: []string{"ACCOUNTS_GID_INDEX_LOWER_BOUND"}, + Destination: &cfg.Index.GID.Lower, + }, + &cli.Int64Flag{ + Name: "uid-index-upper-bound", + Value: 0, + Usage: "define an ending point for the account UID", + EnvVars: []string{"ACCOUNTS_UID_INDEX_UPPER_BOUND"}, + Destination: &cfg.Index.UID.Upper, + }, + &cli.Int64Flag{ + Name: "gid-index-upper-bound", + Value: 1000, + Usage: "define an ending point for the account GID", + EnvVars: []string{"ACCOUNTS_GID_INDEX_UPPER_BOUND"}, + Destination: &cfg.Index.GID.Upper, + }, } } diff --git a/accounts/pkg/indexer/errors/errors.go b/accounts/pkg/indexer/errors/errors.go new file mode 100644 index 000000000..f96213f42 --- /dev/null +++ b/accounts/pkg/indexer/errors/errors.go @@ -0,0 +1,35 @@ +package errors + +import ( + "fmt" +) + +// AlreadyExistsErr implements the Error interface. +type AlreadyExistsErr struct { + TypeName, Key, Value string +} + +func (e *AlreadyExistsErr) Error() string { + return fmt.Sprintf("%s with %s=%s does already exist", e.TypeName, e.Key, e.Value) +} + +// IsAlreadyExistsErr checks whether an error is of type AlreadyExistsErr. +func IsAlreadyExistsErr(e error) bool { + _, ok := e.(*AlreadyExistsErr) + return ok +} + +// NotFoundErr implements the Error interface. +type NotFoundErr struct { + TypeName, Key, Value string +} + +func (e *NotFoundErr) Error() string { + return fmt.Sprintf("%s with %s=%s not found", e.TypeName, e.Key, e.Value) +} + +// IsNotFoundErr checks whether an error is of type IsNotFoundErr. +func IsNotFoundErr(e error) bool { + _, ok := e.(*NotFoundErr) + return ok +} diff --git a/accounts/pkg/indexer/index/cs3/autoincrement.go b/accounts/pkg/indexer/index/cs3/autoincrement.go new file mode 100644 index 000000000..465521588 --- /dev/null +++ b/accounts/pkg/indexer/index/cs3/autoincrement.go @@ -0,0 +1,387 @@ +package cs3 + +import ( + "context" + "fmt" + "io/ioutil" + "net/http" + "os" + "path" + "path/filepath" + "sort" + "strconv" + "strings" + + idxerrs "github.com/owncloud/ocis/accounts/pkg/indexer/errors" + + user "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" + v1beta11 "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1" + provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" + "github.com/cs3org/reva/pkg/rgrpc/todo/pool" + "github.com/cs3org/reva/pkg/token" + "github.com/cs3org/reva/pkg/token/manager/jwt" + "google.golang.org/grpc/metadata" + + "github.com/owncloud/ocis/accounts/pkg/indexer/index" + "github.com/owncloud/ocis/accounts/pkg/indexer/option" + "github.com/owncloud/ocis/accounts/pkg/indexer/registry" +) + +// Autoincrement are fields for an index of type autoincrement. +type Autoincrement struct { + indexBy string + typeName string + filesDir string + indexBaseDir string + indexRootDir string + + tokenManager token.Manager + storageProvider provider.ProviderAPIClient + dataProvider dataProviderClient // Used to create and download data via http, bypassing reva upload protocol + + cs3conf *Config +} + +func init() { + registry.IndexConstructorRegistry["cs3"]["autoincrement"] = NewAutoincrementIndex +} + +// NewAutoincrementIndex instantiates a new AutoincrementIndex instance. +func NewAutoincrementIndex(o ...option.Option) index.Index { + opts := &option.Options{} + for _, opt := range o { + opt(opts) + } + + u := &Autoincrement{ + indexBy: opts.IndexBy, + typeName: opts.TypeName, + filesDir: opts.FilesDir, + indexBaseDir: path.Join(opts.DataDir, "index.cs3"), + indexRootDir: path.Join(path.Join(opts.DataDir, "index.cs3"), strings.Join([]string{"autoincrement", opts.TypeName, opts.IndexBy}, ".")), + cs3conf: &Config{ + ProviderAddr: opts.ProviderAddr, + DataURL: opts.DataURL, + DataPrefix: opts.DataPrefix, + JWTSecret: opts.JWTSecret, + ServiceUserName: opts.ServiceUserName, + ServiceUserUUID: opts.ServiceUserUUID, + }, + dataProvider: dataProviderClient{ + baseURL: singleJoiningSlash(opts.DataURL, opts.DataPrefix), + client: http.Client{ + Transport: http.DefaultTransport, + }, + }, + } + + return u +} + +// Init initializes an autoincrement index. +func (idx *Autoincrement) Init() error { + tokenManager, err := jwt.New(map[string]interface{}{ + "secret": idx.cs3conf.JWTSecret, + }) + + if err != nil { + return err + } + + idx.tokenManager = tokenManager + + client, err := pool.GetStorageProviderServiceClient(idx.cs3conf.ProviderAddr) + if err != nil { + return err + } + + idx.storageProvider = client + + ctx := context.Background() + tk, err := idx.authenticate(ctx) + if err != nil { + return err + } + ctx = metadata.AppendToOutgoingContext(ctx, token.TokenHeader, tk) + + if err := idx.makeDirIfNotExists(ctx, idx.indexBaseDir); err != nil { + return err + } + + if err := idx.makeDirIfNotExists(ctx, idx.indexRootDir); err != nil { + return err + } + + return nil +} + +// Lookup exact lookup by value. +func (idx *Autoincrement) Lookup(v string) ([]string, error) { + searchPath := path.Join(idx.indexRootDir, v) + oldname, err := idx.resolveSymlink(searchPath) + if err != nil { + if os.IsNotExist(err) { + err = &idxerrs.NotFoundErr{TypeName: idx.typeName, Key: idx.indexBy, Value: v} + } + + return nil, err + } + + return []string{oldname}, nil +} + +// Add a new value to the index. +func (idx *Autoincrement) Add(id, v string) (string, error) { + var newName string + if v == "" { + next, err := idx.next() + if err != nil { + return "", err + } + newName = path.Join(idx.indexRootDir, strconv.Itoa(next)) + } else { + newName = path.Join(idx.indexRootDir, v) + } + if err := idx.createSymlink(id, newName); err != nil { + if os.IsExist(err) { + return "", &idxerrs.AlreadyExistsErr{TypeName: idx.typeName, Key: idx.indexBy, Value: v} + } + + return "", err + } + + return newName, nil +} + +// Remove a value v from an index. +func (idx *Autoincrement) Remove(id string, v string) error { + if v == "" { + return nil + } + searchPath := path.Join(idx.indexRootDir, v) + _, err := idx.resolveSymlink(searchPath) + if err != nil { + if os.IsNotExist(err) { + err = &idxerrs.NotFoundErr{TypeName: idx.typeName, Key: idx.indexBy, Value: v} + } + + return err + } + + ctx := context.Background() + t, err := idx.authenticate(ctx) + if err != nil { + return err + } + + deletePath := path.Join("/meta", idx.indexRootDir, v) + ctx = metadata.AppendToOutgoingContext(ctx, token.TokenHeader, t) + resp, err := idx.storageProvider.Delete(ctx, &provider.DeleteRequest{ + Ref: &provider.Reference{ + Spec: &provider.Reference_Path{Path: deletePath}, + }, + }) + + if err != nil { + return err + } + + // TODO Handle other error codes? + if resp.Status.Code == v1beta11.Code_CODE_NOT_FOUND { + return &idxerrs.NotFoundErr{} + } + + return err +} + +// Update index from to . +func (idx *Autoincrement) Update(id, oldV, newV string) error { + if err := idx.Remove(id, oldV); err != nil { + return err + } + + if _, err := idx.Add(id, newV); err != nil { + return err + } + + return nil +} + +// Search allows for glob search on the index. +func (idx *Autoincrement) Search(pattern string) ([]string, error) { + ctx := context.Background() + t, err := idx.authenticate(ctx) + if err != nil { + return nil, err + } + + ctx = metadata.AppendToOutgoingContext(ctx, token.TokenHeader, t) + res, err := idx.storageProvider.ListContainer(ctx, &provider.ListContainerRequest{ + Ref: &provider.Reference{ + Spec: &provider.Reference_Path{Path: path.Join("/meta", idx.indexRootDir)}, + }, + }) + + if err != nil { + return nil, err + } + + searchPath := idx.indexRootDir + matches := make([]string, 0) + for _, i := range res.GetInfos() { + if found, err := filepath.Match(pattern, path.Base(i.Path)); found { + if err != nil { + return nil, err + } + + oldPath, err := idx.resolveSymlink(path.Join(searchPath, path.Base(i.Path))) + if err != nil { + return nil, err + } + matches = append(matches, oldPath) + } + } + + return matches, nil +} + +// CaseInsensitive undocumented. +func (idx *Autoincrement) CaseInsensitive() bool { + return false +} + +// IndexBy undocumented. +func (idx *Autoincrement) IndexBy() string { + return idx.indexBy +} + +// TypeName undocumented. +func (idx *Autoincrement) TypeName() string { + return idx.typeName +} + +// FilesDir undocumented. +func (idx *Autoincrement) FilesDir() string { + return idx.filesDir +} + +func (idx *Autoincrement) createSymlink(oldname, newname string) error { + t, err := idx.authenticate(context.TODO()) + if err != nil { + return err + } + + if _, err := idx.resolveSymlink(newname); err == nil { + return os.ErrExist + } + + resp, err := idx.dataProvider.put(newname, strings.NewReader(oldname), t) + if err != nil { + return err + } + if err = resp.Body.Close(); err != nil { + return err + } + + return nil + +} + +func (idx *Autoincrement) resolveSymlink(name string) (string, error) { + t, err := idx.authenticate(context.TODO()) + if err != nil { + return "", err + } + + resp, err := idx.dataProvider.get(name, t) + if err != nil { + return "", err + } + + if resp.StatusCode != http.StatusOK { + if resp.StatusCode == http.StatusNotFound { + return "", os.ErrNotExist + } + + return "", fmt.Errorf("could not resolve symlink %s, got status %v", name, resp.StatusCode) + } + + b, err := ioutil.ReadAll(resp.Body) + if err != nil { + return "", err + } + if err = resp.Body.Close(); err != nil { + return "", err + } + return string(b), err +} + +func (idx *Autoincrement) makeDirIfNotExists(ctx context.Context, folder string) error { + var rootPathRef = &provider.Reference{ + Spec: &provider.Reference_Path{Path: fmt.Sprintf("/meta/%v", folder)}, + } + + resp, err := idx.storageProvider.Stat(ctx, &provider.StatRequest{ + Ref: rootPathRef, + }) + + if err != nil { + return err + } + + if resp.Status.Code == v1beta11.Code_CODE_NOT_FOUND { + _, err := idx.storageProvider.CreateContainer(ctx, &provider.CreateContainerRequest{ + Ref: rootPathRef, + }) + + if err != nil { + return err + } + } + + return nil +} + +func (idx *Autoincrement) authenticate(ctx context.Context) (token string, err error) { + u := &user.User{ + Id: &user.UserId{OpaqueId: idx.cs3conf.ServiceUserUUID}, + Groups: []string{}, + } + return idx.tokenManager.MintToken(ctx, u) +} + +func (idx *Autoincrement) next() (int, error) { + ctx := context.Background() + t, err := idx.authenticate(ctx) + if err != nil { + return -1, err + } + + ctx = metadata.AppendToOutgoingContext(ctx, token.TokenHeader, t) + res, err := idx.storageProvider.ListContainer(ctx, &provider.ListContainerRequest{ + Ref: &provider.Reference{ + Spec: &provider.Reference_Path{Path: path.Join("/meta", idx.indexRootDir)}, + }, + }) + + if err != nil { + return -1, err + } + + if len(res.GetInfos()) == 0 { + return 0, nil + } + + infos := res.GetInfos() + sort.Slice(infos, func(i, j int) bool { + a, _ := strconv.Atoi(path.Base(infos[i].Path)) + b, _ := strconv.Atoi(path.Base(infos[j].Path)) + return a < b + }) + + latest, err := strconv.Atoi(path.Base(infos[len(infos)-1].Path)) // would returning a string be a better interface? + if err != nil { + return -1, err + } + return latest + 1, nil +} diff --git a/accounts/pkg/indexer/index/cs3/autoincrement_test.go b/accounts/pkg/indexer/index/cs3/autoincrement_test.go new file mode 100644 index 000000000..e9d7f4aae --- /dev/null +++ b/accounts/pkg/indexer/index/cs3/autoincrement_test.go @@ -0,0 +1,83 @@ +package cs3 + +// +//import ( +// "os" +// "testing" +// +// "github.com/owncloud/ocis/accounts/pkg/config" +// "github.com/owncloud/ocis/accounts/pkg/indexer/option" +// . "github.com/owncloud/ocis/accounts/pkg/indexer/test" +// "github.com/stretchr/testify/assert" +//) +// +//const cs3RootFolder = "/var/tmp/ocis/storage/users/data" +// +//func TestAutoincrementIndexAdd(t *testing.T) { +// dataDir, err := WriteIndexTestData(Data, "ID", cs3RootFolder) +// assert.NoError(t, err) +// cfg := generateConfig() +// +// sut := NewAutoincrementIndex( +// option.WithTypeName(GetTypeFQN(User{})), +// option.WithIndexBy("UID"), +// option.WithDataURL(cfg.Repo.CS3.DataURL), +// option.WithDataPrefix(cfg.Repo.CS3.DataPrefix), +// option.WithJWTSecret(cfg.Repo.CS3.JWTSecret), +// option.WithProviderAddr(cfg.Repo.CS3.ProviderAddr), +// ) +// +// assert.NoError(t, sut.Init()) +// +// for i := 0; i < 5; i++ { +// res, err := sut.Add("abcdefg-123", "") +// assert.NoError(t, err) +// t.Log(res) +// } +// +// _ = os.RemoveAll(dataDir) +//} +// +//func BenchmarkAutoincrementIndexAdd(b *testing.B) { +// dataDir, err := WriteIndexTestData(Data, "ID", cs3RootFolder) +// assert.NoError(b, err) +// cfg := generateConfig() +// +// sut := NewAutoincrementIndex( +// option.WithTypeName(GetTypeFQN(User{})), +// option.WithIndexBy("UID"), +// option.WithDataURL(cfg.Repo.CS3.DataURL), +// option.WithDataPrefix(cfg.Repo.CS3.DataPrefix), +// option.WithJWTSecret(cfg.Repo.CS3.JWTSecret), +// option.WithProviderAddr(cfg.Repo.CS3.ProviderAddr), +// ) +// +// err = sut.Init() +// assert.NoError(b, err) +// +// for n := 0; n < b.N; n++ { +// _, err := sut.Add("abcdefg-123", "") +// if err != nil { +// b.Error(err) +// } +// assert.NoError(b, err) +// } +// +// _ = os.RemoveAll(dataDir) +//} +// +//func generateConfig() config.Config { +// return config.Config{ +// Repo: config.Repo{ +// Disk: config.Disk{ +// Path: "", +// }, +// CS3: config.CS3{ +// ProviderAddr: "0.0.0.0:9215", +// DataURL: "http://localhost:9216", +// DataPrefix: "data", +// JWTSecret: "Pive-Fumkiu4", +// }, +// }, +// } +//} diff --git a/accounts/pkg/indexer/index/cs3/data_provider_client.go b/accounts/pkg/indexer/index/cs3/data_provider_client.go new file mode 100644 index 000000000..3a2d42f7e --- /dev/null +++ b/accounts/pkg/indexer/index/cs3/data_provider_client.go @@ -0,0 +1,45 @@ +package cs3 + +import ( + "io" + "net/http" + "strings" +) + +type dataProviderClient struct { + client http.Client + baseURL string +} + +func (d dataProviderClient) put(url string, body io.Reader, token string) (*http.Response, error) { + req, err := http.NewRequest(http.MethodPut, singleJoiningSlash(d.baseURL, url), body) + if err != nil { + return nil, err + } + + req.Header.Add("x-access-token", token) + return d.client.Do(req) +} + +func (d dataProviderClient) get(url string, token string) (*http.Response, error) { + req, err := http.NewRequest(http.MethodGet, singleJoiningSlash(d.baseURL, url), nil) + if err != nil { + return nil, err + } + + req.Header.Add("x-access-token", token) + return d.client.Do(req) +} + +// TODO: this is copied from proxy. Find a better solution or move it to ocis-pkg +func singleJoiningSlash(a, b string) string { + aslash := strings.HasSuffix(a, "/") + bslash := strings.HasPrefix(b, "/") + switch { + case aslash && bslash: + return a + b[1:] + case !aslash && !bslash: + return a + "/" + b + } + return a + b +} diff --git a/accounts/pkg/indexer/index/cs3/non_unique.go b/accounts/pkg/indexer/index/cs3/non_unique.go new file mode 100644 index 000000000..c017d791b --- /dev/null +++ b/accounts/pkg/indexer/index/cs3/non_unique.go @@ -0,0 +1,410 @@ +package cs3 + +import ( + "context" + "fmt" + "io/ioutil" + "net/http" + "os" + "path" + "path/filepath" + "strings" + + user "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" + v1beta11 "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1" + provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" + "github.com/cs3org/reva/pkg/rgrpc/todo/pool" + "github.com/cs3org/reva/pkg/token" + "github.com/cs3org/reva/pkg/token/manager/jwt" + idxerrs "github.com/owncloud/ocis/accounts/pkg/indexer/errors" + "github.com/owncloud/ocis/accounts/pkg/indexer/index" + "github.com/owncloud/ocis/accounts/pkg/indexer/option" + "github.com/owncloud/ocis/accounts/pkg/indexer/registry" + "google.golang.org/grpc/metadata" +) + +func init() { + registry.IndexConstructorRegistry["cs3"]["non_unique"] = NewNonUniqueIndexWithOptions +} + +// NonUnique are fields for an index of type non_unique. +type NonUnique struct { + caseInsensitive bool + indexBy string + typeName string + filesDir string + indexBaseDir string + indexRootDir string + + tokenManager token.Manager + storageProvider provider.ProviderAPIClient + dataProvider dataProviderClient // Used to create and download data via http, bypassing reva upload protocol + + cs3conf *Config +} + +// NewNonUniqueIndexWithOptions instantiates a new NonUniqueIndex instance. +// /var/tmp/ocis-accounts/index.cs3/Pets/Bro* +// ├── Brown/ +// │ └── rebef-123 -> /var/tmp/testfiles-395764020/pets/rebef-123 +// ├── Green/ +// │ ├── goefe-789 -> /var/tmp/testfiles-395764020/pets/goefe-789 +// │ └── xadaf-189 -> /var/tmp/testfiles-395764020/pets/xadaf-189 +// └── White/ +// └── wefwe-456 -> /var/tmp/testfiles-395764020/pets/wefwe-456 +func NewNonUniqueIndexWithOptions(o ...option.Option) index.Index { + opts := &option.Options{} + for _, opt := range o { + opt(opts) + } + + return &NonUnique{ + caseInsensitive: opts.CaseInsensitive, + indexBy: opts.IndexBy, + typeName: opts.TypeName, + filesDir: opts.FilesDir, + indexBaseDir: path.Join(opts.DataDir, "index.cs3"), + indexRootDir: path.Join(path.Join(opts.DataDir, "index.cs3"), strings.Join([]string{"non_unique", opts.TypeName, opts.IndexBy}, ".")), + cs3conf: &Config{ + ProviderAddr: opts.ProviderAddr, + DataURL: opts.DataURL, + DataPrefix: opts.DataPrefix, + JWTSecret: opts.JWTSecret, + ServiceUserName: opts.ServiceUserName, + ServiceUserUUID: opts.ServiceUserUUID, + }, + dataProvider: dataProviderClient{ + baseURL: singleJoiningSlash(opts.DataURL, opts.DataPrefix), + client: http.Client{ + Transport: http.DefaultTransport, + }, + }, + } +} + +// Init initializes a non_unique index. +func (idx *NonUnique) Init() error { + tokenManager, err := jwt.New(map[string]interface{}{ + "secret": idx.cs3conf.JWTSecret, + }) + + if err != nil { + return err + } + + idx.tokenManager = tokenManager + + client, err := pool.GetStorageProviderServiceClient(idx.cs3conf.ProviderAddr) + if err != nil { + return err + } + + idx.storageProvider = client + + ctx := context.Background() + tk, err := idx.authenticate(ctx) + if err != nil { + return err + } + ctx = metadata.AppendToOutgoingContext(ctx, token.TokenHeader, tk) + + if err := idx.makeDirIfNotExists(ctx, idx.indexBaseDir); err != nil { + return err + } + + if err := idx.makeDirIfNotExists(ctx, idx.indexRootDir); err != nil { + return err + } + + return nil +} + +// Lookup exact lookup by value. +func (idx *NonUnique) Lookup(v string) ([]string, error) { + if idx.caseInsensitive { + v = strings.ToLower(v) + } + var matches = make([]string, 0) + ctx, err := idx.getAuthenticatedContext(context.Background()) + if err != nil { + return nil, err + } + + res, err := idx.storageProvider.ListContainer(ctx, &provider.ListContainerRequest{ + Ref: &provider.Reference{ + Spec: &provider.Reference_Path{Path: path.Join("/meta", idx.indexRootDir, v)}, + }, + }) + + if err != nil { + return nil, err + } + + for _, info := range res.Infos { + matches = append(matches, path.Base(info.Path)) + } + + return matches, nil +} + +// Add a new value to the index. +func (idx *NonUnique) Add(id, v string) (string, error) { + if v == "" { + return "", nil + } + if idx.caseInsensitive { + v = strings.ToLower(v) + } + ctx, err := idx.getAuthenticatedContext(context.Background()) + if err != nil { + return "", err + } + + newName := path.Join(idx.indexRootDir, v) + if err := idx.makeDirIfNotExists(ctx, newName); err != nil { + return "", err + } + + if err := idx.createSymlink(id, path.Join(newName, id)); err != nil { + if os.IsExist(err) { + return "", &idxerrs.AlreadyExistsErr{TypeName: idx.typeName, Key: idx.indexBy, Value: v} + } + + return "", err + } + + return newName, nil +} + +// Remove a value v from an index. +func (idx *NonUnique) Remove(id string, v string) error { + if v == "" { + return nil + } + if idx.caseInsensitive { + v = strings.ToLower(v) + } + ctx, err := idx.getAuthenticatedContext(context.Background()) + if err != nil { + return err + } + + deletePath := path.Join("/meta", idx.indexRootDir, v, id) + resp, err := idx.storageProvider.Delete(ctx, &provider.DeleteRequest{ + Ref: &provider.Reference{ + Spec: &provider.Reference_Path{Path: deletePath}, + }, + }) + + if err != nil { + return err + } + + if resp.Status.Code == v1beta11.Code_CODE_NOT_FOUND { + return &idxerrs.NotFoundErr{TypeName: idx.typeName, Key: idx.indexBy, Value: v} + } + + toStat := path.Join("/meta", idx.indexRootDir, v) + lcResp, err := idx.storageProvider.ListContainer(ctx, &provider.ListContainerRequest{ + Ref: &provider.Reference{ + Spec: &provider.Reference_Path{Path: toStat}, + }, + }) + if err != nil { + return err + } + + if len(lcResp.Infos) == 0 { + deletePath = path.Join("/meta", idx.indexRootDir, v) + _, err := idx.storageProvider.Delete(ctx, &provider.DeleteRequest{ + Ref: &provider.Reference{ + Spec: &provider.Reference_Path{Path: deletePath}, + }, + }) + if err != nil { + return err + } + } + + return nil +} + +// Update index from to . +func (idx *NonUnique) Update(id, oldV, newV string) error { + if idx.caseInsensitive { + oldV = strings.ToLower(oldV) + newV = strings.ToLower(newV) + } + + if err := idx.Remove(id, oldV); err != nil { + return err + } + + if _, err := idx.Add(id, newV); err != nil { + return err + } + + return nil +} + +// Search allows for glob search on the index. +func (idx *NonUnique) Search(pattern string) ([]string, error) { + if idx.caseInsensitive { + pattern = strings.ToLower(pattern) + } + + ctx, err := idx.getAuthenticatedContext(context.Background()) + if err != nil { + return nil, err + } + + foldersMatched := make([]string, 0) + matches := make([]string, 0) + res, err := idx.storageProvider.ListContainer(ctx, &provider.ListContainerRequest{ + Ref: &provider.Reference{ + Spec: &provider.Reference_Path{Path: path.Join("/meta", idx.indexRootDir)}, + }, + }) + + if err != nil { + return nil, err + } + + for _, i := range res.Infos { + if found, err := filepath.Match(pattern, path.Base(i.Path)); found { + if err != nil { + return nil, err + } + + foldersMatched = append(foldersMatched, i.Path) + } + } + + for i := range foldersMatched { + res, _ := idx.storageProvider.ListContainer(ctx, &provider.ListContainerRequest{ + Ref: &provider.Reference{ + Spec: &provider.Reference_Path{Path: foldersMatched[i]}, + }, + }) + + for _, info := range res.Infos { + matches = append(matches, path.Base(info.Path)) + } + } + + return matches, nil +} + +// CaseInsensitive undocumented. +func (idx *NonUnique) CaseInsensitive() bool { + return idx.caseInsensitive +} + +// IndexBy undocumented. +func (idx *NonUnique) IndexBy() string { + return idx.indexBy +} + +// TypeName undocumented. +func (idx *NonUnique) TypeName() string { + return idx.typeName +} + +// FilesDir undocumented. +func (idx *NonUnique) FilesDir() string { + return idx.filesDir +} + +func (idx *NonUnique) authenticate(ctx context.Context) (token string, err error) { + u := &user.User{ + Id: &user.UserId{OpaqueId: idx.cs3conf.ServiceUserUUID}, + Groups: []string{}, + } + return idx.tokenManager.MintToken(ctx, u) +} + +func (idx *NonUnique) makeDirIfNotExists(ctx context.Context, folder string) error { + var rootPathRef = &provider.Reference{ + Spec: &provider.Reference_Path{Path: fmt.Sprintf("/meta/%v", folder)}, + } + + resp, err := idx.storageProvider.Stat(ctx, &provider.StatRequest{ + Ref: rootPathRef, + }) + + if err != nil { + return err + } + + if resp.Status.Code == v1beta11.Code_CODE_NOT_FOUND { + _, err := idx.storageProvider.CreateContainer(ctx, &provider.CreateContainerRequest{ + Ref: rootPathRef, + }) + + if err != nil { + return err + } + } + + return nil +} + +func (idx *NonUnique) createSymlink(oldname, newname string) error { + t, err := idx.authenticate(context.TODO()) + if err != nil { + return err + } + + if _, err := idx.resolveSymlink(newname); err == nil { + return os.ErrExist + } + + resp, err := idx.dataProvider.put(newname, strings.NewReader(oldname), t) + if err != nil { + return err + } + if err = resp.Body.Close(); err != nil { + return err + } + + return nil + +} + +func (idx *NonUnique) resolveSymlink(name string) (string, error) { + t, err := idx.authenticate(context.TODO()) + if err != nil { + return "", err + } + + resp, err := idx.dataProvider.get(name, t) + if err != nil { + return "", err + } + + if resp.StatusCode != http.StatusOK { + if resp.StatusCode == http.StatusNotFound { + return "", os.ErrNotExist + } + + return "", fmt.Errorf("could not resolve symlink %s, got status %v", name, resp.StatusCode) + } + + b, err := ioutil.ReadAll(resp.Body) + if err != nil { + return "", err + } + if err = resp.Body.Close(); err != nil { + return "", err + } + return string(b), err +} + +func (idx *NonUnique) getAuthenticatedContext(ctx context.Context) (context.Context, error) { + t, err := idx.authenticate(ctx) + if err != nil { + return nil, err + } + ctx = metadata.AppendToOutgoingContext(ctx, token.TokenHeader, t) + return ctx, nil +} diff --git a/accounts/pkg/indexer/index/cs3/non_unique_test.go b/accounts/pkg/indexer/index/cs3/non_unique_test.go new file mode 100644 index 000000000..ad09ed4c0 --- /dev/null +++ b/accounts/pkg/indexer/index/cs3/non_unique_test.go @@ -0,0 +1,68 @@ +package cs3 + +// +//import ( +// "os" +// "path" +// "testing" +// +// "github.com/owncloud/ocis/accounts/pkg/config" +// "github.com/owncloud/ocis/accounts/pkg/indexer/option" +// . "github.com/owncloud/ocis/accounts/pkg/indexer/test" +// "github.com/stretchr/testify/assert" +//) +// +//func TestCS3NonUniqueIndex_FakeSymlink(t *testing.T) { +// dataDir, err := WriteIndexTestData(Data, "ID", cs3RootFolder) +// assert.NoError(t, err) +// cfg := config.Config{ +// Repo: config.Repo{ +// Disk: config.Disk{ +// Path: "", +// }, +// CS3: config.CS3{ +// ProviderAddr: "0.0.0.0:9215", +// DataURL: "http://localhost:9216", +// DataPrefix: "data", +// JWTSecret: "Pive-Fumkiu4", +// }, +// }, +// } +// +// sut := NewNonUniqueIndexWithOptions( +// option.WithTypeName(GetTypeFQN(User{})), +// option.WithIndexBy("UserName"), +// option.WithFilesDir(path.Join(cfg.Repo.Disk.Path, "/meta")), +// option.WithDataDir(cfg.Repo.Disk.Path), +// option.WithDataURL(cfg.Repo.CS3.DataURL), +// option.WithDataPrefix(cfg.Repo.CS3.DataPrefix), +// option.WithJWTSecret(cfg.Repo.CS3.JWTSecret), +// option.WithProviderAddr(cfg.Repo.CS3.ProviderAddr), +// ) +// +// err = sut.Init() +// assert.NoError(t, err) +// +// res, err := sut.Add("abcdefg-123", "mikey") +// assert.NoError(t, err) +// t.Log(res) +// +// resLookup, err := sut.Lookup("mikey") +// assert.NoError(t, err) +// t.Log(resLookup) +// +// err = sut.Update("abcdefg-123", "mikey", "mickeyX") +// assert.NoError(t, err) +// +// searchRes, err := sut.Search("m*") +// assert.NoError(t, err) +// assert.Len(t, searchRes, 1) +// assert.Equal(t, searchRes[0], "abcdefg-123") +// +// resp, err := sut.Lookup("mikey") +// assert.Len(t, resp, 0) +// assert.NoError(t, err) +// +// _ = os.RemoveAll(dataDir) +// +//} diff --git a/accounts/pkg/indexer/index/cs3/unique.go b/accounts/pkg/indexer/index/cs3/unique.go new file mode 100644 index 000000000..a5526b47c --- /dev/null +++ b/accounts/pkg/indexer/index/cs3/unique.go @@ -0,0 +1,372 @@ +package cs3 + +import ( + "context" + "fmt" + "io/ioutil" + "net/http" + "os" + "path" + "path/filepath" + "strings" + + user "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" + v1beta11 "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1" + provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" + "github.com/cs3org/reva/pkg/rgrpc/todo/pool" + "github.com/cs3org/reva/pkg/token" + "github.com/cs3org/reva/pkg/token/manager/jwt" + idxerrs "github.com/owncloud/ocis/accounts/pkg/indexer/errors" + "github.com/owncloud/ocis/accounts/pkg/indexer/index" + "github.com/owncloud/ocis/accounts/pkg/indexer/option" + "github.com/owncloud/ocis/accounts/pkg/indexer/registry" + "google.golang.org/grpc/metadata" +) + +// Unique are fields for an index of type non_unique. +type Unique struct { + caseInsensitive bool + indexBy string + typeName string + filesDir string + indexBaseDir string + indexRootDir string + + tokenManager token.Manager + storageProvider provider.ProviderAPIClient + dataProvider dataProviderClient // Used to create and download data via http, bypassing reva upload protocol + + cs3conf *Config +} + +// Config represents cs3conf. Should be deprecated in favor of config.Config. +type Config struct { + ProviderAddr string + DataURL string + DataPrefix string + JWTSecret string + ServiceUserName string + ServiceUserUUID string +} + +func init() { + registry.IndexConstructorRegistry["cs3"]["unique"] = NewUniqueIndexWithOptions +} + +// NewUniqueIndexWithOptions instantiates a new UniqueIndex instance. Init() should be +// called afterward to ensure correct on-disk structure. +func NewUniqueIndexWithOptions(o ...option.Option) index.Index { + opts := &option.Options{} + for _, opt := range o { + opt(opts) + } + + u := &Unique{ + caseInsensitive: opts.CaseInsensitive, + indexBy: opts.IndexBy, + typeName: opts.TypeName, + filesDir: opts.FilesDir, + indexBaseDir: path.Join(opts.DataDir, "index.cs3"), + indexRootDir: path.Join(path.Join(opts.DataDir, "index.cs3"), strings.Join([]string{"unique", opts.TypeName, opts.IndexBy}, ".")), + cs3conf: &Config{ + ProviderAddr: opts.ProviderAddr, + DataURL: opts.DataURL, + DataPrefix: opts.DataPrefix, + JWTSecret: opts.JWTSecret, + ServiceUserName: opts.ServiceUserName, + ServiceUserUUID: opts.ServiceUserUUID, + }, + dataProvider: dataProviderClient{ + baseURL: singleJoiningSlash(opts.DataURL, opts.DataPrefix), + client: http.Client{ + Transport: http.DefaultTransport, + }, + }, + } + + return u +} + +// Init initializes a unique index. +func (idx *Unique) Init() error { + tokenManager, err := jwt.New(map[string]interface{}{ + "secret": idx.cs3conf.JWTSecret, + }) + + if err != nil { + return err + } + + idx.tokenManager = tokenManager + + client, err := pool.GetStorageProviderServiceClient(idx.cs3conf.ProviderAddr) + if err != nil { + return err + } + + idx.storageProvider = client + + ctx := context.Background() + tk, err := idx.authenticate(ctx) + if err != nil { + return err + } + ctx = metadata.AppendToOutgoingContext(ctx, token.TokenHeader, tk) + + if err := idx.makeDirIfNotExists(ctx, idx.indexBaseDir); err != nil { + return err + } + + if err := idx.makeDirIfNotExists(ctx, idx.indexRootDir); err != nil { + return err + } + + return nil +} + +// Lookup exact lookup by value. +func (idx *Unique) Lookup(v string) ([]string, error) { + if idx.caseInsensitive { + v = strings.ToLower(v) + } + searchPath := path.Join(idx.indexRootDir, v) + oldname, err := idx.resolveSymlink(searchPath) + if err != nil { + if os.IsNotExist(err) { + err = &idxerrs.NotFoundErr{TypeName: idx.typeName, Key: idx.indexBy, Value: v} + } + + return nil, err + } + + return []string{oldname}, nil +} + +// Add adds a value to the index, returns the path to the root-document +func (idx *Unique) Add(id, v string) (string, error) { + if v == "" { + return "", nil + } + if idx.caseInsensitive { + v = strings.ToLower(v) + } + newName := path.Join(idx.indexRootDir, v) + if err := idx.createSymlink(id, newName); err != nil { + if os.IsExist(err) { + return "", &idxerrs.AlreadyExistsErr{TypeName: idx.typeName, Key: idx.indexBy, Value: v} + } + + return "", err + } + + return newName, nil +} + +// Remove a value v from an index. +func (idx *Unique) Remove(id string, v string) error { + if v == "" { + return nil + } + if idx.caseInsensitive { + v = strings.ToLower(v) + } + searchPath := path.Join(idx.indexRootDir, v) + _, err := idx.resolveSymlink(searchPath) + if err != nil { + if os.IsNotExist(err) { + err = &idxerrs.NotFoundErr{TypeName: idx.typeName, Key: idx.indexBy, Value: v} + } + + return err + } + + ctx := context.Background() + t, err := idx.authenticate(ctx) + if err != nil { + return err + } + + deletePath := path.Join("/meta", idx.indexRootDir, v) + ctx = metadata.AppendToOutgoingContext(ctx, token.TokenHeader, t) + resp, err := idx.storageProvider.Delete(ctx, &provider.DeleteRequest{ + Ref: &provider.Reference{ + Spec: &provider.Reference_Path{Path: deletePath}, + }, + }) + + if err != nil { + return err + } + + // TODO Handle other error codes? + if resp.Status.Code == v1beta11.Code_CODE_NOT_FOUND { + return &idxerrs.NotFoundErr{} + } + + return err +} + +// Update index from to . +func (idx *Unique) Update(id, oldV, newV string) error { + if idx.caseInsensitive { + oldV = strings.ToLower(oldV) + newV = strings.ToLower(newV) + } + + if err := idx.Remove(id, oldV); err != nil { + return err + } + + if _, err := idx.Add(id, newV); err != nil { + return err + } + + return nil +} + +// Search allows for glob search on the index. +func (idx *Unique) Search(pattern string) ([]string, error) { + if idx.caseInsensitive { + pattern = strings.ToLower(pattern) + } + + ctx := context.Background() + t, err := idx.authenticate(ctx) + if err != nil { + return nil, err + } + + ctx = metadata.AppendToOutgoingContext(ctx, token.TokenHeader, t) + res, err := idx.storageProvider.ListContainer(ctx, &provider.ListContainerRequest{ + Ref: &provider.Reference{ + Spec: &provider.Reference_Path{Path: path.Join("/meta", idx.indexRootDir)}, + }, + }) + + if err != nil { + return nil, err + } + + searchPath := idx.indexRootDir + matches := make([]string, 0) + for _, i := range res.GetInfos() { + if found, err := filepath.Match(pattern, path.Base(i.Path)); found { + if err != nil { + return nil, err + } + + oldPath, err := idx.resolveSymlink(path.Join(searchPath, path.Base(i.Path))) + if err != nil { + return nil, err + } + matches = append(matches, oldPath) + } + } + + return matches, nil +} + +// CaseInsensitive undocumented. +func (idx *Unique) CaseInsensitive() bool { + return idx.caseInsensitive +} + +// IndexBy undocumented. +func (idx *Unique) IndexBy() string { + return idx.indexBy +} + +// TypeName undocumented. +func (idx *Unique) TypeName() string { + return idx.typeName +} + +// FilesDir undocumented. +func (idx *Unique) FilesDir() string { + return idx.filesDir +} + +func (idx *Unique) createSymlink(oldname, newname string) error { + t, err := idx.authenticate(context.TODO()) + if err != nil { + return err + } + + if _, err := idx.resolveSymlink(newname); err == nil { + return os.ErrExist + } + + resp, err := idx.dataProvider.put(newname, strings.NewReader(oldname), t) + if err != nil { + return err + } + if err = resp.Body.Close(); err != nil { + return err + } + + return nil + +} + +func (idx *Unique) resolveSymlink(name string) (string, error) { + t, err := idx.authenticate(context.TODO()) + if err != nil { + return "", err + } + + resp, err := idx.dataProvider.get(name, t) + if err != nil { + return "", err + } + + if resp.StatusCode != http.StatusOK { + if resp.StatusCode == http.StatusNotFound { + return "", os.ErrNotExist + } + + return "", fmt.Errorf("could not resolve symlink %s, got status %v", name, resp.StatusCode) + } + + b, err := ioutil.ReadAll(resp.Body) + if err != nil { + return "", err + } + if err = resp.Body.Close(); err != nil { + return "", err + } + return string(b), err +} + +func (idx *Unique) makeDirIfNotExists(ctx context.Context, folder string) error { + var rootPathRef = &provider.Reference{ + Spec: &provider.Reference_Path{Path: fmt.Sprintf("/meta/%v", folder)}, + } + + resp, err := idx.storageProvider.Stat(ctx, &provider.StatRequest{ + Ref: rootPathRef, + }) + + if err != nil { + return err + } + + if resp.Status.Code == v1beta11.Code_CODE_NOT_FOUND { + _, err := idx.storageProvider.CreateContainer(ctx, &provider.CreateContainerRequest{ + Ref: rootPathRef, + }) + + if err != nil { + return err + } + } + + return nil +} + +func (idx *Unique) authenticate(ctx context.Context) (token string, err error) { + u := &user.User{ + Id: &user.UserId{OpaqueId: idx.cs3conf.ServiceUserUUID}, + Groups: []string{}, + } + return idx.tokenManager.MintToken(ctx, u) +} diff --git a/accounts/pkg/indexer/index/cs3/unique_test.go b/accounts/pkg/indexer/index/cs3/unique_test.go new file mode 100644 index 000000000..e0f58bc31 --- /dev/null +++ b/accounts/pkg/indexer/index/cs3/unique_test.go @@ -0,0 +1,107 @@ +package cs3 + +// +//import ( +// "os" +// "path" +// "testing" +// +// "github.com/owncloud/ocis/accounts/pkg/config" +// "github.com/owncloud/ocis/accounts/pkg/indexer/option" +// . "github.com/owncloud/ocis/accounts/pkg/indexer/test" +// "github.com/stretchr/testify/assert" +//) +// +//func TestCS3UniqueIndex_FakeSymlink(t *testing.T) { +// dataDir, err := WriteIndexTestData(Data, "ID", cs3RootFolder) +// assert.NoError(t, err) +// cfg := config.Config{ +// Repo: config.Repo{ +// Disk: config.Disk{ +// Path: "", +// }, +// CS3: config.CS3{ +// ProviderAddr: "0.0.0.0:9215", +// DataURL: "http://localhost:9216", +// DataPrefix: "data", +// JWTSecret: "Pive-Fumkiu4", +// }, +// }, +// } +// +// sut := NewUniqueIndexWithOptions( +// option.WithTypeName(GetTypeFQN(User{})), +// option.WithIndexBy("UserName"), +// option.WithFilesDir(path.Join(cfg.Repo.Disk.Path, "/meta")), +// option.WithDataDir(cfg.Repo.Disk.Path), +// option.WithDataURL(cfg.Repo.CS3.DataURL), +// option.WithDataPrefix(cfg.Repo.CS3.DataPrefix), +// option.WithJWTSecret(cfg.Repo.CS3.JWTSecret), +// option.WithProviderAddr(cfg.Repo.CS3.ProviderAddr), +// ) +// +// err = sut.Init() +// assert.NoError(t, err) +// +// res, err := sut.Add("abcdefg-123", "mikey") +// assert.NoError(t, err) +// t.Log(res) +// +// resLookup, err := sut.Lookup("mikey") +// assert.NoError(t, err) +// t.Log(resLookup) +// +// err = sut.Update("abcdefg-123", "mikey", "mickeyX") +// assert.NoError(t, err) +// +// searchRes, err := sut.Search("m*") +// assert.NoError(t, err) +// assert.Len(t, searchRes, 1) +// assert.Equal(t, searchRes[0], "abcdefg-123") +// +// _ = os.RemoveAll(dataDir) +//} +// +//func TestCS3UniqueIndexSearch(t *testing.T) { +// dataDir, err := WriteIndexTestData(Data, "ID", cs3RootFolder) +// assert.NoError(t, err) +// cfg := config.Config{ +// Repo: config.Repo{ +// Disk: config.Disk{ +// Path: "", +// }, +// CS3: config.CS3{ +// ProviderAddr: "0.0.0.0:9215", +// DataURL: "http://localhost:9216", +// DataPrefix: "data", +// JWTSecret: "Pive-Fumkiu4", +// }, +// }, +// } +// +// sut := NewUniqueIndexWithOptions( +// option.WithTypeName(GetTypeFQN(User{})), +// option.WithIndexBy("UserName"), +// option.WithFilesDir(path.Join(cfg.Repo.Disk.Path, "/meta")), +// option.WithDataDir(cfg.Repo.Disk.Path), +// option.WithDataURL(cfg.Repo.CS3.DataURL), +// option.WithDataPrefix(cfg.Repo.CS3.DataPrefix), +// option.WithJWTSecret(cfg.Repo.CS3.JWTSecret), +// option.WithProviderAddr(cfg.Repo.CS3.ProviderAddr), +// ) +// +// err = sut.Init() +// assert.NoError(t, err) +// +// _, err = sut.Add("hijklmn-456", "mikey") +// assert.NoError(t, err) +// +// _, err = sut.Add("ewf4ofk-555", "jacky") +// assert.NoError(t, err) +// +// res, err := sut.Search("*y") +// assert.NoError(t, err) +// t.Log(res) +// +// _ = os.RemoveAll(dataDir) +//} diff --git a/accounts/pkg/indexer/index/disk/autoincrement.go b/accounts/pkg/indexer/index/disk/autoincrement.go new file mode 100644 index 000000000..170e274b9 --- /dev/null +++ b/accounts/pkg/indexer/index/disk/autoincrement.go @@ -0,0 +1,262 @@ +package disk + +import ( + "errors" + "os" + "path" + "path/filepath" + "reflect" + "sort" + "strconv" + "strings" + + idxerrs "github.com/owncloud/ocis/accounts/pkg/indexer/errors" + + "github.com/owncloud/ocis/accounts/pkg/indexer/index" + "github.com/owncloud/ocis/accounts/pkg/indexer/option" + "github.com/owncloud/ocis/accounts/pkg/indexer/registry" +) + +// Autoincrement are fields for an index of type autoincrement. +type Autoincrement struct { + indexBy string + typeName string + filesDir string + indexBaseDir string + indexRootDir string + + bound *option.Bound +} + +// - Creating an autoincrement index has to be thread safe. +// - Validation: autoincrement indexes should only work on integers. + +func init() { + registry.IndexConstructorRegistry["disk"]["autoincrement"] = NewAutoincrementIndex +} + +// NewAutoincrementIndex instantiates a new AutoincrementIndex instance. Init() should be +// called afterward to ensure correct on-disk structure. +func NewAutoincrementIndex(o ...option.Option) index.Index { + opts := &option.Options{} + for _, opt := range o { + opt(opts) + } + + // validate the field + if opts.Entity == nil { + panic("invalid autoincrement index: configured without entity") + } + + k, err := getKind(opts.Entity, opts.IndexBy) + if !isValidKind(k) || err != nil { + panic("invalid autoincrement index: configured on non-numeric field") + } + + return &Autoincrement{ + indexBy: opts.IndexBy, + typeName: opts.TypeName, + filesDir: opts.FilesDir, + bound: opts.Bound, + indexBaseDir: path.Join(opts.DataDir, "index.disk"), + indexRootDir: path.Join(path.Join(opts.DataDir, "index.disk"), strings.Join([]string{"autoincrement", opts.TypeName, opts.IndexBy}, ".")), + } +} + +var ( + validKinds = []reflect.Kind{ + reflect.Int, + reflect.Int8, + reflect.Int16, + reflect.Int32, + reflect.Int64, + } +) + +// Init initializes an autoincrement index. +func (idx *Autoincrement) Init() error { + if _, err := os.Stat(idx.filesDir); err != nil { + return err + } + + if err := os.MkdirAll(idx.indexRootDir, 0777); err != nil { + return err + } + + return nil +} + +// Lookup exact lookup by value. +func (idx *Autoincrement) Lookup(v string) ([]string, error) { + searchPath := path.Join(idx.indexRootDir, v) + if err := isValidSymlink(searchPath); err != nil { + if os.IsNotExist(err) { + err = &idxerrs.NotFoundErr{TypeName: idx.typeName, Key: idx.indexBy, Value: v} + } + + return nil, err + } + + p, err := os.Readlink(searchPath) + if err != nil { + return []string{}, nil + } + + return []string{p}, err +} + +// Add a new value to the index. +func (idx *Autoincrement) Add(id, v string) (string, error) { + nextID, err := idx.next() + if err != nil { + return "", err + } + oldName := filepath.Join(idx.filesDir, id) + var newName string + if v == "" { + newName = filepath.Join(idx.indexRootDir, strconv.Itoa(nextID)) + } else { + newName = filepath.Join(idx.indexRootDir, v) + } + err = os.Symlink(oldName, newName) + if errors.Is(err, os.ErrExist) { + return "", &idxerrs.AlreadyExistsErr{TypeName: idx.typeName, Key: idx.indexBy, Value: v} + } + + return newName, err +} + +// Remove a value v from an index. +func (idx *Autoincrement) Remove(id string, v string) error { + if v == "" { + return nil + } + searchPath := path.Join(idx.indexRootDir, v) + return os.Remove(searchPath) +} + +// Update index from to . +func (idx *Autoincrement) Update(id, oldV, newV string) error { + oldPath := path.Join(idx.indexRootDir, oldV) + if err := isValidSymlink(oldPath); err != nil { + if os.IsNotExist(err) { + return &idxerrs.NotFoundErr{TypeName: idx.TypeName(), Key: idx.IndexBy(), Value: oldV} + } + + return err + } + + newPath := path.Join(idx.indexRootDir, newV) + err := isValidSymlink(newPath) + if err == nil { + return &idxerrs.AlreadyExistsErr{TypeName: idx.typeName, Key: idx.indexBy, Value: newV} + } + + if os.IsNotExist(err) { + err = os.Rename(oldPath, newPath) + } + + return err +} + +// Search allows for glob search on the index. +func (idx *Autoincrement) Search(pattern string) ([]string, error) { + paths, err := filepath.Glob(path.Join(idx.indexRootDir, pattern)) + if err != nil { + return nil, err + } + + if len(paths) == 0 { + return nil, &idxerrs.NotFoundErr{TypeName: idx.typeName, Key: idx.indexBy, Value: pattern} + } + + res := make([]string, 0) + for _, p := range paths { + if err := isValidSymlink(p); err != nil { + return nil, err + } + + src, err := os.Readlink(p) + if err != nil { + return nil, err + } + + res = append(res, src) + } + + return res, nil +} + +// CaseInsensitive undocumented. +func (idx *Autoincrement) CaseInsensitive() bool { + return false +} + +// IndexBy undocumented. +func (idx *Autoincrement) IndexBy() string { + return idx.indexBy +} + +// TypeName undocumented. +func (idx *Autoincrement) TypeName() string { + return idx.typeName +} + +// FilesDir undocumented. +func (idx *Autoincrement) FilesDir() string { + return idx.filesDir +} + +func isValidKind(k reflect.Kind) bool { + for _, v := range validKinds { + if k == v { + return true + } + } + return false +} + +func getKind(i interface{}, field string) (reflect.Kind, error) { + r := reflect.ValueOf(i) + return reflect.Indirect(r).FieldByName(field).Kind(), nil +} + +func readDir(dirname string) ([]os.FileInfo, error) { + f, err := os.Open(dirname) + if err != nil { + return nil, err + } + list, err := f.Readdir(-1) + f.Close() + if err != nil { + return nil, err + } + sort.Slice(list, func(i, j int) bool { + a, _ := strconv.Atoi(list[i].Name()) + b, _ := strconv.Atoi(list[j].Name()) + return a < b + }) + return list, nil +} + +func (idx *Autoincrement) next() (int, error) { + files, err := readDir(idx.indexRootDir) + if err != nil { + return -1, err + } + + if len(files) == 0 { + return int(idx.bound.Lower), nil + } + + latest, err := strconv.Atoi(path.Base(files[len(files)-1].Name())) + if err != nil { + return -1, err + } + + if int64(latest) < idx.bound.Lower { + return int(idx.bound.Lower), nil + } + + return latest + 1, nil +} diff --git a/accounts/pkg/indexer/index/disk/autoincrement_test.go b/accounts/pkg/indexer/index/disk/autoincrement_test.go new file mode 100644 index 000000000..cd0ecb2cc --- /dev/null +++ b/accounts/pkg/indexer/index/disk/autoincrement_test.go @@ -0,0 +1,299 @@ +package disk + +import ( + "io/ioutil" + "os" + "path/filepath" + "testing" + + "github.com/owncloud/ocis/accounts/pkg/indexer/option" + //. "github.com/owncloud/ocis/accounts/pkg/indexer/test" + "github.com/owncloud/ocis/accounts/pkg/proto/v0" + "github.com/stretchr/testify/assert" +) + +func TestIsValidKind(t *testing.T) { + scenarios := []struct { + panics bool + name string + indexBy string + entity struct { + Number int + Name string + NumberFloat float32 + } + }{ + { + name: "valid autoincrement index", + panics: false, + indexBy: "Number", + entity: struct { + Number int + Name string + NumberFloat float32 + }{ + Name: "tesy-mc-testace", + }, + }, + { + name: "create autoincrement index on a non-existing field", + panics: true, + indexBy: "Age", + entity: struct { + Number int + Name string + NumberFloat float32 + }{ + Name: "tesy-mc-testace", + }, + }, + { + name: "attempt to create an autoincrement index with no entity", + panics: true, + indexBy: "Age", + }, + { + name: "create autoincrement index on a non-numeric field", + panics: true, + indexBy: "Name", + entity: struct { + Number int + Name string + NumberFloat float32 + }{ + Name: "tesy-mc-testace", + }, + }, + } + + for _, scenario := range scenarios { + t.Run(scenario.name, func(t *testing.T) { + if scenario.panics { + assert.Panics(t, func() { + _ = NewAutoincrementIndex( + option.WithEntity(scenario.entity), + option.WithIndexBy(scenario.indexBy), + ) + }) + } else { + assert.NotPanics(t, func() { + _ = NewAutoincrementIndex( + option.WithEntity(scenario.entity), + option.WithIndexBy(scenario.indexBy), + ) + }) + } + }) + } +} + +func TestNext(t *testing.T) { + scenarios := []struct { + name string + expected int + indexBy string + entity interface{} + }{ + { + name: "get next value", + expected: 0, + indexBy: "Number", + entity: struct { + Number int + Name string + NumberFloat float32 + }{ + Name: "tesy-mc-testace", + }, + }, + } + + for _, scenario := range scenarios { + t.Run(scenario.name, func(t *testing.T) { + tmpDir, err := createTmpDirStr() + assert.NoError(t, err) + + err = os.MkdirAll(filepath.Join(tmpDir, "data"), 0777) + assert.NoError(t, err) + + i := NewAutoincrementIndex( + option.WithBounds(&option.Bound{ + Lower: 0, + Upper: 0, + }), + option.WithDataDir(tmpDir), + option.WithFilesDir(filepath.Join(tmpDir, "data")), + option.WithEntity(scenario.entity), + option.WithTypeName("LambdaType"), + option.WithIndexBy(scenario.indexBy), + ) + + err = i.Init() + assert.NoError(t, err) + + tmpFile, err := os.Create(filepath.Join(tmpDir, "data", "test-example")) + assert.NoError(t, err) + assert.NoError(t, tmpFile.Close()) + + oldName, err := i.Add("test-example", "") + assert.NoError(t, err) + assert.Equal(t, "0", filepath.Base(oldName)) + + oldName, err = i.Add("test-example", "") + assert.NoError(t, err) + assert.Equal(t, "1", filepath.Base(oldName)) + + oldName, err = i.Add("test-example", "") + assert.NoError(t, err) + assert.Equal(t, "2", filepath.Base(oldName)) + t.Log(oldName) + + _ = os.RemoveAll(tmpDir) + }) + } +} + +func TestLowerBound(t *testing.T) { + scenarios := []struct { + name string + expected int + indexBy string + entity interface{} + }{ + { + name: "get next value with a lower bound specified", + expected: 0, + indexBy: "Number", + entity: struct { + Number int + Name string + NumberFloat float32 + }{ + Name: "tesy-mc-testace", + }, + }, + } + + for _, scenario := range scenarios { + t.Run(scenario.name, func(t *testing.T) { + tmpDir, err := createTmpDirStr() + assert.NoError(t, err) + + err = os.MkdirAll(filepath.Join(tmpDir, "data"), 0777) + assert.NoError(t, err) + + i := NewAutoincrementIndex( + option.WithBounds(&option.Bound{ + Lower: 1000, + }), + option.WithDataDir(tmpDir), + option.WithFilesDir(filepath.Join(tmpDir, "data")), + option.WithEntity(scenario.entity), + option.WithTypeName("LambdaType"), + option.WithIndexBy(scenario.indexBy), + ) + + err = i.Init() + assert.NoError(t, err) + + tmpFile, err := os.Create(filepath.Join(tmpDir, "data", "test-example")) + assert.NoError(t, err) + assert.NoError(t, tmpFile.Close()) + + oldName, err := i.Add("test-example", "") + assert.NoError(t, err) + assert.Equal(t, "1000", filepath.Base(oldName)) + + oldName, err = i.Add("test-example", "") + assert.NoError(t, err) + assert.Equal(t, "1001", filepath.Base(oldName)) + + oldName, err = i.Add("test-example", "") + assert.NoError(t, err) + assert.Equal(t, "1002", filepath.Base(oldName)) + t.Log(oldName) + + _ = os.RemoveAll(tmpDir) + }) + } +} + +func TestAdd(t *testing.T) { + tmpDir, err := createTmpDirStr() + assert.NoError(t, err) + + err = os.MkdirAll(filepath.Join(tmpDir, "data"), 0777) + assert.NoError(t, err) + + tmpFile, err := os.Create(filepath.Join(tmpDir, "data", "test-example")) + assert.NoError(t, err) + assert.NoError(t, tmpFile.Close()) + + i := NewAutoincrementIndex( + option.WithBounds(&option.Bound{ + Lower: 0, + Upper: 0, + }), + option.WithDataDir(tmpDir), + option.WithFilesDir(filepath.Join(tmpDir, "data")), + option.WithEntity(&proto.Account{}), + option.WithTypeName("owncloud.Account"), + option.WithIndexBy("UidNumber"), + ) + + err = i.Init() + assert.NoError(t, err) + + _, err = i.Add("test-example", "") + if err != nil { + t.Error(err) + } +} + +func BenchmarkAdd(b *testing.B) { + tmpDir, err := createTmpDirStr() + assert.NoError(b, err) + + err = os.MkdirAll(filepath.Join(tmpDir, "data"), 0777) + assert.NoError(b, err) + + tmpFile, err := os.Create(filepath.Join(tmpDir, "data", "test-example")) + assert.NoError(b, err) + assert.NoError(b, tmpFile.Close()) + + i := NewAutoincrementIndex( + option.WithBounds(&option.Bound{ + Lower: 0, + Upper: 0, + }), + option.WithDataDir(tmpDir), + option.WithFilesDir(filepath.Join(tmpDir, "data")), + option.WithEntity(struct { + Number int + Name string + NumberFloat float32 + }{}), + option.WithTypeName("LambdaType"), + option.WithIndexBy("Number"), + ) + + err = i.Init() + assert.NoError(b, err) + + for n := 0; n < b.N; n++ { + _, err := i.Add("test-example", "") + if err != nil { + b.Error(err) + } + assert.NoError(b, err) + } +} + +func createTmpDirStr() (string, error) { + name, err := ioutil.TempDir("/var/tmp", "testfiles-*") + if err != nil { + return "", err + } + + return name, nil +} diff --git a/accounts/pkg/indexer/index/disk/non_unique.go b/accounts/pkg/indexer/index/disk/non_unique.go new file mode 100644 index 000000000..8f0c7d694 --- /dev/null +++ b/accounts/pkg/indexer/index/disk/non_unique.go @@ -0,0 +1,235 @@ +package disk + +import ( + "errors" + "io/ioutil" + "os" + "path" + "path/filepath" + "strings" + + idxerrs "github.com/owncloud/ocis/accounts/pkg/indexer/errors" + "github.com/owncloud/ocis/accounts/pkg/indexer/index" + "github.com/owncloud/ocis/accounts/pkg/indexer/option" + "github.com/owncloud/ocis/accounts/pkg/indexer/registry" +) + +// NonUnique is able to index an document by a key which might contain non-unique values +// +// /var/tmp/testfiles-395764020/index.disk/PetByColor/ +// ├── Brown +// │ └── rebef-123 -> /var/tmp/testfiles-395764020/pets/rebef-123 +// ├── Green +// │ ├── goefe-789 -> /var/tmp/testfiles-395764020/pets/goefe-789 +// │ └── xadaf-189 -> /var/tmp/testfiles-395764020/pets/xadaf-189 +// └── White +// └── wefwe-456 -> /var/tmp/testfiles-395764020/pets/wefwe-456 +type NonUnique struct { + caseInsensitive bool + indexBy string + typeName string + filesDir string + indexBaseDir string + indexRootDir string +} + +func init() { + registry.IndexConstructorRegistry["disk"]["non_unique"] = NewNonUniqueIndexWithOptions +} + +// NewNonUniqueIndexWithOptions instantiates a new UniqueIndex instance. Init() should be +// called afterward to ensure correct on-disk structure. +func NewNonUniqueIndexWithOptions(o ...option.Option) index.Index { + opts := &option.Options{} + for _, opt := range o { + opt(opts) + } + + return &NonUnique{ + caseInsensitive: opts.CaseInsensitive, + indexBy: opts.IndexBy, + typeName: opts.TypeName, + filesDir: opts.FilesDir, + indexBaseDir: path.Join(opts.DataDir, "index.disk"), + indexRootDir: path.Join(path.Join(opts.DataDir, "index.disk"), strings.Join([]string{"non_unique", opts.TypeName, opts.IndexBy}, ".")), + } +} + +// Init initializes a unique index. +func (idx *NonUnique) Init() error { + if _, err := os.Stat(idx.filesDir); err != nil { + return err + } + + if err := os.MkdirAll(idx.indexRootDir, 0777); err != nil { + return err + } + + return nil +} + +// Lookup exact lookup by value. +func (idx *NonUnique) Lookup(v string) ([]string, error) { + if idx.caseInsensitive { + v = strings.ToLower(v) + } + searchPath := path.Join(idx.indexRootDir, v) + fi, err := ioutil.ReadDir(searchPath) + if os.IsNotExist(err) { + return []string{}, &idxerrs.NotFoundErr{TypeName: idx.typeName, Key: idx.indexBy, Value: v} + } + + if err != nil { + return []string{}, err + } + + var ids []string = nil + for _, f := range fi { + ids = append(ids, f.Name()) + } + + if len(ids) == 0 { + return []string{}, &idxerrs.NotFoundErr{TypeName: idx.typeName, Key: idx.indexBy, Value: v} + } + + return ids, nil +} + +// Add adds a value to the index, returns the path to the root-document +func (idx *NonUnique) Add(id, v string) (string, error) { + if v == "" { + return "", nil + } + if idx.caseInsensitive { + v = strings.ToLower(v) + } + oldName := path.Join(idx.filesDir, id) + newName := path.Join(idx.indexRootDir, v, id) + + if err := os.MkdirAll(path.Join(idx.indexRootDir, v), 0777); err != nil { + return "", err + } + + err := os.Symlink(oldName, newName) + if errors.Is(err, os.ErrExist) { + return "", &idxerrs.AlreadyExistsErr{TypeName: idx.typeName, Key: idx.indexBy, Value: v} + } + + return newName, err + +} + +// Remove a value v from an index. +func (idx *NonUnique) Remove(id string, v string) error { + if v == "" { + return nil + } + if idx.caseInsensitive { + v = strings.ToLower(v) + } + res, err := filepath.Glob(path.Join(idx.indexRootDir, "/*/", id)) + if err != nil { + return err + } + + for _, p := range res { + if err := os.Remove(p); err != nil { + return err + } + } + + // Remove value directory if it is empty + valueDir := path.Join(idx.indexRootDir, v) + fi, err := ioutil.ReadDir(valueDir) + if err != nil { + return err + } + + if len(fi) == 0 { + if err := os.RemoveAll(valueDir); err != nil { + return err + } + } + + return nil +} + +// Update index from to . +func (idx *NonUnique) Update(id, oldV, newV string) (err error) { + if idx.caseInsensitive { + oldV = strings.ToLower(oldV) + newV = strings.ToLower(newV) + } + oldDir := path.Join(idx.indexRootDir, oldV) + oldPath := path.Join(oldDir, id) + newDir := path.Join(idx.indexRootDir, newV) + newPath := path.Join(newDir, id) + + if _, err = os.Stat(oldPath); os.IsNotExist(err) { + return &idxerrs.NotFoundErr{TypeName: idx.typeName, Key: idx.indexBy, Value: oldV} + } + + if err != nil { + return + } + + if err = os.MkdirAll(newDir, 0777); err != nil { + return + } + + if err = os.Rename(oldPath, newPath); err != nil { + return + } + + di, err := ioutil.ReadDir(oldDir) + if err != nil { + return err + } + + if len(di) == 0 { + err = os.RemoveAll(oldDir) + if err != nil { + return + } + } + + return + +} + +// Search allows for glob search on the index. +func (idx *NonUnique) Search(pattern string) ([]string, error) { + if idx.caseInsensitive { + pattern = strings.ToLower(pattern) + } + paths, err := filepath.Glob(path.Join(idx.indexRootDir, pattern, "*")) + if err != nil { + return nil, err + } + + if len(paths) == 0 { + return nil, &idxerrs.NotFoundErr{TypeName: idx.typeName, Key: idx.indexBy, Value: pattern} + } + + return paths, nil +} + +// CaseInsensitive undocumented. +func (idx *NonUnique) CaseInsensitive() bool { + return idx.caseInsensitive +} + +// IndexBy undocumented. +func (idx *NonUnique) IndexBy() string { + return idx.indexBy +} + +// TypeName undocumented. +func (idx *NonUnique) TypeName() string { + return idx.typeName +} + +// FilesDir undocumented. +func (idx *NonUnique) FilesDir() string { + return idx.filesDir +} diff --git a/accounts/pkg/indexer/index/disk/non_unique_test.go b/accounts/pkg/indexer/index/disk/non_unique_test.go new file mode 100644 index 000000000..a60c3474c --- /dev/null +++ b/accounts/pkg/indexer/index/disk/non_unique_test.go @@ -0,0 +1,113 @@ +package disk + +import ( + "fmt" + "os" + "path" + "testing" + + "github.com/owncloud/ocis/accounts/pkg/config" + "github.com/owncloud/ocis/accounts/pkg/indexer/errors" + "github.com/owncloud/ocis/accounts/pkg/indexer/index" + "github.com/owncloud/ocis/accounts/pkg/indexer/option" + . "github.com/owncloud/ocis/accounts/pkg/indexer/test" + "github.com/stretchr/testify/assert" +) + +func TestNonUniqueIndexAdd(t *testing.T) { + sut, dataPath := getNonUniqueIdxSut(t, Pet{}, "Color") + + ids, err := sut.Lookup("Green") + assert.NoError(t, err) + assert.EqualValues(t, []string{"goefe-789", "xadaf-189"}, ids) + + ids, err = sut.Lookup("White") + assert.NoError(t, err) + assert.EqualValues(t, []string{"wefwe-456"}, ids) + + ids, err = sut.Lookup("Cyan") + assert.Error(t, err) + assert.EqualValues(t, []string{}, ids) + + _ = os.RemoveAll(dataPath) + +} + +func TestNonUniqueIndexUpdate(t *testing.T) { + sut, dataPath := getNonUniqueIdxSut(t, Pet{}, "Color") + + err := sut.Update("goefe-789", "Green", "Black") + assert.NoError(t, err) + + err = sut.Update("xadaf-189", "Green", "Black") + assert.NoError(t, err) + + assert.DirExists(t, path.Join(dataPath, fmt.Sprintf("index.disk/non_unique.%v.Color/Black", GetTypeFQN(Pet{})))) + assert.NoDirExists(t, path.Join(dataPath, fmt.Sprintf("index.disk/non_unique.%v.Color/Green", GetTypeFQN(Pet{})))) + + _ = os.RemoveAll(dataPath) +} + +func TestNonUniqueIndexDelete(t *testing.T) { + sut, dataPath := getNonUniqueIdxSut(t, Pet{}, "Color") + assert.FileExists(t, path.Join(dataPath, fmt.Sprintf("index.disk/non_unique.%v.Color/Green/goefe-789", GetTypeFQN(Pet{})))) + + err := sut.Remove("goefe-789", "Green") + assert.NoError(t, err) + assert.NoFileExists(t, path.Join(dataPath, fmt.Sprintf("index.disk/non_unique.%v.Color/Green/goefe-789", GetTypeFQN(Pet{})))) + assert.FileExists(t, path.Join(dataPath, fmt.Sprintf("index.disk/non_unique.%v.Color/Green/xadaf-189", GetTypeFQN(Pet{})))) + + _ = os.RemoveAll(dataPath) +} + +func TestNonUniqueIndexSearch(t *testing.T) { + sut, dataPath := getNonUniqueIdxSut(t, Pet{}, "Email") + + res, err := sut.Search("Gr*") + + assert.NoError(t, err) + assert.Len(t, res, 2) + + assert.Equal(t, "goefe-789", path.Base(res[0])) + assert.Equal(t, "xadaf-189", path.Base(res[1])) + + _, err = sut.Search("does-not-exist@example.com") + assert.Error(t, err) + assert.IsType(t, &errors.NotFoundErr{}, err) + + _ = os.RemoveAll(dataPath) +} + +// entity: used to get the fully qualified name for the index root path. +func getNonUniqueIdxSut(t *testing.T, entity interface{}, indexBy string) (index.Index, string) { + dataPath, _ := WriteIndexTestData(Data, "ID", "") + cfg := config.Config{ + Repo: config.Repo{ + Disk: config.Disk{ + Path: dataPath, + }, + }, + } + + sut := NewNonUniqueIndexWithOptions( + option.WithTypeName(GetTypeFQN(entity)), + option.WithIndexBy(indexBy), + option.WithFilesDir(path.Join(cfg.Repo.Disk.Path, "pets")), + option.WithDataDir(cfg.Repo.Disk.Path), + ) + err := sut.Init() + if err != nil { + t.Fatal(err) + } + + for _, u := range Data["pets"] { + pkVal := ValueOf(u, "ID") + idxByVal := ValueOf(u, "Color") + _, err := sut.Add(pkVal, idxByVal) + if err != nil { + t.Fatal(err) + } + } + + return sut, dataPath +} diff --git a/accounts/pkg/indexer/index/disk/unique.go b/accounts/pkg/indexer/index/disk/unique.go new file mode 100644 index 000000000..7b9436223 --- /dev/null +++ b/accounts/pkg/indexer/index/disk/unique.go @@ -0,0 +1,222 @@ +package disk + +import ( + "errors" + "fmt" + "os" + "path" + "path/filepath" + "strings" + + idxerrs "github.com/owncloud/ocis/accounts/pkg/indexer/errors" + "github.com/owncloud/ocis/accounts/pkg/indexer/index" + "github.com/owncloud/ocis/accounts/pkg/indexer/option" + "github.com/owncloud/ocis/accounts/pkg/indexer/registry" +) + +// Unique ensures that only one document of the same type and key-value combination can exist in the index. +// +// Modeled by creating a indexer-folder per entity and key with symlinks which point to respective documents which contain +// the link-filename as value. +// +// Directory Layout +// +// /var/data/index.disk/UniqueUserByEmail/ +// ├── jacky@example.com -> /var/data/users/ewf4ofk-555 +// ├── jones@example.com -> /var/data/users/rulan54-777 +// └── mikey@example.com -> /var/data/users/abcdefg-123 +// +// Example user +// +// { +// "Id": "ewf4ofk-555", +// "UserName": "jacky", +// "Email": "jacky@example.com" +// } +// +type Unique struct { + caseInsensitive bool + indexBy string + typeName string + filesDir string + indexBaseDir string + indexRootDir string +} + +func init() { + registry.IndexConstructorRegistry["disk"]["unique"] = NewUniqueIndexWithOptions +} + +// NewUniqueIndexWithOptions instantiates a new UniqueIndex instance. Init() should be +// called afterward to ensure correct on-disk structure. +func NewUniqueIndexWithOptions(o ...option.Option) index.Index { + opts := &option.Options{} + for _, opt := range o { + opt(opts) + } + + return &Unique{ + caseInsensitive: opts.CaseInsensitive, + indexBy: opts.IndexBy, + typeName: opts.TypeName, + filesDir: opts.FilesDir, + indexBaseDir: path.Join(opts.DataDir, "index.disk"), + indexRootDir: path.Join(path.Join(opts.DataDir, "index.disk"), strings.Join([]string{"unique", opts.TypeName, opts.IndexBy}, ".")), + } +} + +// Init initializes a unique index. +func (idx *Unique) Init() error { + if _, err := os.Stat(idx.filesDir); err != nil { + return err + } + + if err := os.MkdirAll(idx.indexRootDir, 0777); err != nil { + return err + } + + return nil +} + +// Add adds a value to the index, returns the path to the root-document +func (idx *Unique) Add(id, v string) (string, error) { + if v == "" { + return "", nil + } + if idx.caseInsensitive { + v = strings.ToLower(v) + } + oldName := path.Join(idx.filesDir, id) + newName := path.Join(idx.indexRootDir, v) + err := os.Symlink(oldName, newName) + if errors.Is(err, os.ErrExist) { + return "", &idxerrs.AlreadyExistsErr{TypeName: idx.typeName, Key: idx.indexBy, Value: v} + } + + return newName, err +} + +// Remove a value v from an index. +func (idx *Unique) Remove(id string, v string) (err error) { + if v == "" { + return nil + } + if idx.caseInsensitive { + v = strings.ToLower(v) + } + searchPath := path.Join(idx.indexRootDir, v) + return os.Remove(searchPath) +} + +// Lookup exact lookup by value. +func (idx *Unique) Lookup(v string) (resultPath []string, err error) { + if idx.caseInsensitive { + v = strings.ToLower(v) + } + searchPath := path.Join(idx.indexRootDir, v) + if err = isValidSymlink(searchPath); err != nil { + if os.IsNotExist(err) { + err = &idxerrs.NotFoundErr{TypeName: idx.typeName, Key: idx.indexBy, Value: v} + } + return + } + + p, err := os.Readlink(searchPath) + if err != nil { + return []string{}, nil + } + + return []string{p}, err +} + +// Update index from to . +func (idx *Unique) Update(id, oldV, newV string) (err error) { + if idx.caseInsensitive { + oldV = strings.ToLower(oldV) + newV = strings.ToLower(newV) + } + oldPath := path.Join(idx.indexRootDir, oldV) + if err = isValidSymlink(oldPath); err != nil { + if os.IsNotExist(err) { + return &idxerrs.NotFoundErr{TypeName: idx.TypeName(), Key: idx.IndexBy(), Value: oldV} + } + + return + } + + newPath := path.Join(idx.indexRootDir, newV) + if err = isValidSymlink(newPath); err == nil { + return &idxerrs.AlreadyExistsErr{TypeName: idx.typeName, Key: idx.indexBy, Value: newV} + } + + if os.IsNotExist(err) { + err = os.Rename(oldPath, newPath) + } + + return +} + +// Search allows for glob search on the index. +func (idx *Unique) Search(pattern string) ([]string, error) { + if idx.caseInsensitive { + pattern = strings.ToLower(pattern) + } + paths, err := filepath.Glob(path.Join(idx.indexRootDir, pattern)) + if err != nil { + return nil, err + } + + if len(paths) == 0 { + return nil, &idxerrs.NotFoundErr{TypeName: idx.typeName, Key: idx.indexBy, Value: pattern} + } + + res := make([]string, 0) + for _, p := range paths { + if err := isValidSymlink(p); err != nil { + return nil, err + } + + src, err := os.Readlink(p) + if err != nil { + return nil, err + } + + res = append(res, src) + } + + return res, nil +} + +// CaseInsensitive undocumented. +func (idx *Unique) CaseInsensitive() bool { + return idx.caseInsensitive +} + +// IndexBy undocumented. +func (idx *Unique) IndexBy() string { + return idx.indexBy +} + +// TypeName undocumented. +func (idx *Unique) TypeName() string { + return idx.typeName +} + +// FilesDir undocumented. +func (idx *Unique) FilesDir() string { + return idx.filesDir +} + +func isValidSymlink(path string) (err error) { + var symInfo os.FileInfo + if symInfo, err = os.Lstat(path); err != nil { + return + } + + if symInfo.Mode()&os.ModeSymlink == 0 { + err = fmt.Errorf("%s is not a valid symlink (bug/corruption?)", path) + return + } + + return +} diff --git a/accounts/pkg/indexer/index/disk/unique_test.go b/accounts/pkg/indexer/index/disk/unique_test.go new file mode 100644 index 000000000..8d88f5d9b --- /dev/null +++ b/accounts/pkg/indexer/index/disk/unique_test.go @@ -0,0 +1,133 @@ +package disk + +import ( + "os" + "path" + "testing" + + "github.com/owncloud/ocis/accounts/pkg/config" + "github.com/owncloud/ocis/accounts/pkg/indexer/errors" + "github.com/owncloud/ocis/accounts/pkg/indexer/index" + "github.com/owncloud/ocis/accounts/pkg/indexer/option" + . "github.com/owncloud/ocis/accounts/pkg/indexer/test" + "github.com/stretchr/testify/assert" +) + +func TestUniqueLookupSingleEntry(t *testing.T) { + uniq, dataDir := getUniqueIdxSut(t, "Email", User{}) + filesDir := path.Join(dataDir, "users") + + t.Log("existing lookup") + resultPath, err := uniq.Lookup("mikey@example.com") + assert.NoError(t, err) + + assert.Equal(t, []string{path.Join(filesDir, "abcdefg-123")}, resultPath) + + t.Log("non-existing lookup") + resultPath, err = uniq.Lookup("doesnotExists@example.com") + assert.Error(t, err) + assert.IsType(t, &errors.NotFoundErr{}, err) + assert.Empty(t, resultPath) + + _ = os.RemoveAll(dataDir) + +} + +func TestUniqueUniqueConstraint(t *testing.T) { + uniq, dataDir := getUniqueIdxSut(t, "Email", User{}) + + _, err := uniq.Add("abcdefg-123", "mikey@example.com") + assert.Error(t, err) + assert.IsType(t, &errors.AlreadyExistsErr{}, err) + + _ = os.RemoveAll(dataDir) +} + +func TestUniqueRemove(t *testing.T) { + uniq, dataDir := getUniqueIdxSut(t, "Email", User{}) + + err := uniq.Remove("", "mikey@example.com") + assert.NoError(t, err) + + _, err = uniq.Lookup("mikey@example.com") + assert.Error(t, err) + assert.IsType(t, &errors.NotFoundErr{}, err) + + _ = os.RemoveAll(dataDir) +} + +func TestUniqueUpdate(t *testing.T) { + uniq, dataDir := getUniqueIdxSut(t, "Email", User{}) + + t.Log("successful update") + err := uniq.Update("", "mikey@example.com", "mikey2@example.com") + assert.NoError(t, err) + + t.Log("failed update because already exists") + err = uniq.Update("", "mikey2@example.com", "mikey2@example.com") + assert.Error(t, err) + assert.IsType(t, &errors.AlreadyExistsErr{}, err) + + t.Log("failed update because not found") + err = uniq.Update("", "nonexisting@example.com", "something2@example.com") + assert.Error(t, err) + assert.IsType(t, &errors.NotFoundErr{}, err) + + _ = os.RemoveAll(dataDir) +} + +func TestUniqueIndexSearch(t *testing.T) { + sut, dataDir := getUniqueIdxSut(t, "Email", User{}) + + res, err := sut.Search("j*@example.com") + + assert.NoError(t, err) + assert.Len(t, res, 2) + + assert.Equal(t, "ewf4ofk-555", path.Base(res[0])) + assert.Equal(t, "rulan54-777", path.Base(res[1])) + + _, err = sut.Search("does-not-exist@example.com") + assert.Error(t, err) + assert.IsType(t, &errors.NotFoundErr{}, err) + + _ = os.RemoveAll(dataDir) +} + +func TestErrors(t *testing.T) { + assert.True(t, errors.IsAlreadyExistsErr(&errors.AlreadyExistsErr{})) + assert.True(t, errors.IsNotFoundErr(&errors.NotFoundErr{})) +} + +func getUniqueIdxSut(t *testing.T, indexBy string, entityType interface{}) (index.Index, string) { + dataPath, _ := WriteIndexTestData(Data, "ID", "") + cfg := config.Config{ + Repo: config.Repo{ + Disk: config.Disk{ + Path: dataPath, + }, + }, + } + + sut := NewUniqueIndexWithOptions( + option.WithTypeName(GetTypeFQN(entityType)), + option.WithIndexBy(indexBy), + option.WithFilesDir(path.Join(cfg.Repo.Disk.Path, "users")), + option.WithDataDir(cfg.Repo.Disk.Path), + ) + err := sut.Init() + if err != nil { + t.Fatal(err) + } + + for _, u := range Data["users"] { + pkVal := ValueOf(u, "ID") + idxByVal := ValueOf(u, "Email") + _, err := sut.Add(pkVal, idxByVal) + if err != nil { + t.Fatal(err) + } + } + + return sut, dataPath +} diff --git a/accounts/pkg/indexer/index/index.go b/accounts/pkg/indexer/index/index.go new file mode 100644 index 000000000..e6f62862e --- /dev/null +++ b/accounts/pkg/indexer/index/index.go @@ -0,0 +1,16 @@ +package index + +// Index can be implemented to create new indexer-strategies. See Unique for example. +// Each indexer implementation is bound to one data-column (IndexBy) and a data-type (TypeName) +type Index interface { + Init() error + Lookup(v string) ([]string, error) + Add(id, v string) (string, error) + Remove(id string, v string) error + Update(id, oldV, newV string) error + Search(pattern string) ([]string, error) + CaseInsensitive() bool + IndexBy() string + TypeName() string + FilesDir() string +} diff --git a/accounts/pkg/indexer/indexer.go b/accounts/pkg/indexer/indexer.go new file mode 100644 index 000000000..2731af413 --- /dev/null +++ b/accounts/pkg/indexer/indexer.go @@ -0,0 +1,222 @@ +// Package indexer provides symlink-based indexer for on-disk document-directories. +package indexer + +import ( + "fmt" + "path" + + "github.com/iancoleman/strcase" + "github.com/owncloud/ocis/accounts/pkg/config" + "github.com/owncloud/ocis/accounts/pkg/indexer/errors" + "github.com/owncloud/ocis/accounts/pkg/indexer/index" + _ "github.com/owncloud/ocis/accounts/pkg/indexer/index/cs3" // to populate index + _ "github.com/owncloud/ocis/accounts/pkg/indexer/index/disk" // to populate index + "github.com/owncloud/ocis/accounts/pkg/indexer/option" + "github.com/owncloud/ocis/accounts/pkg/indexer/registry" +) + +// Indexer is a facade to configure and query over multiple indices. +type Indexer struct { + config *config.Config + indices typeMap +} + +// IdxAddResult represents the result of an Add call on an index +type IdxAddResult struct { + Field, Value string +} + +// CreateIndexer creates a new Indexer. +func CreateIndexer(cfg *config.Config) *Indexer { + return &Indexer{ + config: cfg, + indices: typeMap{}, + } +} + +func getRegistryStrategy(cfg *config.Config) string { + if cfg.Repo.Disk.Path != "" { + return "disk" + } + + return "cs3" +} + +// AddIndex adds a new index to the indexer receiver. +func (i Indexer) AddIndex(t interface{}, indexBy, pkName, entityDirName, indexType string, bound *option.Bound, caseInsensitive bool) error { + strategy := getRegistryStrategy(i.config) + f := registry.IndexConstructorRegistry[strategy][indexType] + var idx index.Index + + if strategy == "disk" { + idx = f( + option.CaseInsensitive(caseInsensitive), + option.WithEntity(t), + option.WithBounds(bound), + option.WithTypeName(getTypeFQN(t)), + option.WithIndexBy(indexBy), + option.WithFilesDir(path.Join(i.config.Repo.Disk.Path, entityDirName)), + option.WithDataDir(i.config.Repo.Disk.Path), + ) + } else if strategy == "cs3" { + idx = f( + option.CaseInsensitive(caseInsensitive), + option.WithEntity(t), + option.WithBounds(bound), + option.WithTypeName(getTypeFQN(t)), + option.WithIndexBy(indexBy), + option.WithDataURL(i.config.Repo.CS3.DataURL), + option.WithDataPrefix(i.config.Repo.CS3.DataPrefix), + option.WithJWTSecret(i.config.Repo.CS3.JWTSecret), + option.WithProviderAddr(i.config.Repo.CS3.ProviderAddr), + option.WithServiceUserUUID(i.config.ServiceUser.UUID), + option.WithServiceUserName(i.config.ServiceUser.Username), + ) + } + + i.indices.addIndex(getTypeFQN(t), pkName, idx) + return idx.Init() +} + +// Add a new entry to the indexer +func (i Indexer) Add(t interface{}) ([]IdxAddResult, error) { + typeName := getTypeFQN(t) + var results []IdxAddResult + if fields, ok := i.indices[typeName]; ok { + for _, indices := range fields.IndicesByField { + for _, idx := range indices { + pkVal := valueOf(t, fields.PKFieldName) + idxByVal := valueOf(t, idx.IndexBy()) + value, err := idx.Add(pkVal, idxByVal) + if err != nil { + return []IdxAddResult{}, err + } + if value == "" { + continue + } + results = append(results, IdxAddResult{Field: idx.IndexBy(), Value: value}) + } + } + } + + return results, nil +} + +// FindBy finds a value on an index by field and value. +func (i Indexer) FindBy(t interface{}, field string, val string) ([]string, error) { + typeName := getTypeFQN(t) + resultPaths := make([]string, 0) + if fields, ok := i.indices[typeName]; ok { + for _, idx := range fields.IndicesByField[strcase.ToCamel(field)] { + idxVal := val + res, err := idx.Lookup(idxVal) + if err != nil { + if errors.IsNotFoundErr(err) { + continue + } + + if err != nil { + return nil, err + } + } + + resultPaths = append(resultPaths, res...) + + } + } + + result := make([]string, 0, len(resultPaths)) + for _, v := range resultPaths { + result = append(result, path.Base(v)) + } + + return result, nil +} + +// Delete deletes all indexed fields of a given type t on the Indexer. +func (i Indexer) Delete(t interface{}) error { + typeName := getTypeFQN(t) + if fields, ok := i.indices[typeName]; ok { + for _, indices := range fields.IndicesByField { + for _, idx := range indices { + pkVal := valueOf(t, fields.PKFieldName) + idxByVal := valueOf(t, idx.IndexBy()) + if err := idx.Remove(pkVal, idxByVal); err != nil { + return err + } + } + } + } + + return nil +} + +// FindByPartial allows for glob search across all indexes. +func (i Indexer) FindByPartial(t interface{}, field string, pattern string) ([]string, error) { + typeName := getTypeFQN(t) + resultPaths := make([]string, 0) + if fields, ok := i.indices[typeName]; ok { + for _, idx := range fields.IndicesByField[strcase.ToCamel(field)] { + res, err := idx.Search(pattern) + if err != nil { + if errors.IsNotFoundErr(err) { + continue + } + + if err != nil { + return nil, err + } + } + + resultPaths = append(resultPaths, res...) + + } + } + + result := make([]string, 0, len(resultPaths)) + for _, v := range resultPaths { + result = append(result, path.Base(v)) + } + + return result, nil + +} + +// Update updates all indexes on a value to a value . +func (i Indexer) Update(from, to interface{}) error { + typeNameFrom := getTypeFQN(from) + typeNameTo := getTypeFQN(to) + if typeNameFrom != typeNameTo { + return fmt.Errorf("update types do not match: from %v to %v", typeNameFrom, typeNameTo) + } + + if fields, ok := i.indices[typeNameFrom]; ok { + for fName, indices := range fields.IndicesByField { + oldV := valueOf(from, fName) + newV := valueOf(to, fName) + pkVal := valueOf(from, fields.PKFieldName) + for _, idx := range indices { + if oldV == newV { + continue + } + if oldV == "" { + if _, err := idx.Add(pkVal, newV); err != nil { + return err + } + continue + } + if newV == "" { + if err := idx.Remove(pkVal, oldV); err != nil { + return err + } + continue + } + if err := idx.Update(pkVal, oldV, newV); err != nil { + return err + } + } + } + } + + return nil +} diff --git a/accounts/pkg/indexer/indexer_test.go b/accounts/pkg/indexer/indexer_test.go new file mode 100644 index 000000000..276aec14c --- /dev/null +++ b/accounts/pkg/indexer/indexer_test.go @@ -0,0 +1,275 @@ +package indexer + +import ( + "os" + "path" + "testing" + + "github.com/owncloud/ocis/accounts/pkg/indexer/option" + + "github.com/owncloud/ocis/accounts/pkg/config" + _ "github.com/owncloud/ocis/accounts/pkg/indexer/index/cs3" + _ "github.com/owncloud/ocis/accounts/pkg/indexer/index/disk" + . "github.com/owncloud/ocis/accounts/pkg/indexer/test" + "github.com/stretchr/testify/assert" +) + +const cs3RootFolder = "/var/tmp/ocis/storage/users/data" + +func TestIndexer_CS3_AddWithUniqueIndex(t *testing.T) { + dataDir, err := WriteIndexTestData(Data, "ID", cs3RootFolder) + assert.NoError(t, err) + indexer := createCs3Indexer() + + err = indexer.AddIndex(&User{}, "UserName", "ID", "users", "unique", nil, false) + assert.NoError(t, err) + + u := &User{ID: "abcdefg-123", UserName: "mikey", Email: "mikey@example.com"} + _, err = indexer.Add(u) + assert.NoError(t, err) + + _ = os.RemoveAll(dataDir) +} + +func TestIndexer_CS3_AddWithNonUniqueIndex(t *testing.T) { + dataDir, err := WriteIndexTestData(Data, "ID", cs3RootFolder) + assert.NoError(t, err) + indexer := createCs3Indexer() + + err = indexer.AddIndex(&User{}, "UserName", "ID", "users", "non_unique", nil, false) + assert.NoError(t, err) + + u := &User{ID: "abcdefg-123", UserName: "mikey", Email: "mikey@example.com"} + _, err = indexer.Add(u) + assert.NoError(t, err) + + _ = os.RemoveAll(dataDir) +} + +func TestIndexer_Disk_FindByWithUniqueIndex(t *testing.T) { + dataDir, err := WriteIndexTestData(Data, "ID", "") + assert.NoError(t, err) + indexer := createDiskIndexer(dataDir) + + err = indexer.AddIndex(&User{}, "UserName", "ID", "users", "unique", nil, false) + assert.NoError(t, err) + + u := &User{ID: "abcdefg-123", UserName: "mikey", Email: "mikey@example.com"} + _, err = indexer.Add(u) + assert.NoError(t, err) + + res, err := indexer.FindBy(User{}, "UserName", "mikey") + assert.NoError(t, err) + t.Log(res) + + _ = os.RemoveAll(dataDir) +} + +func TestIndexer_Disk_AddWithUniqueIndex(t *testing.T) { + dataDir, err := WriteIndexTestData(Data, "ID", "") + assert.NoError(t, err) + indexer := createDiskIndexer(dataDir) + + err = indexer.AddIndex(&User{}, "UserName", "ID", "users", "unique", nil, false) + assert.NoError(t, err) + + u := &User{ID: "abcdefg-123", UserName: "mikey", Email: "mikey@example.com"} + _, err = indexer.Add(u) + assert.NoError(t, err) + + _ = os.RemoveAll(dataDir) +} + +func TestIndexer_Disk_AddWithNonUniqueIndex(t *testing.T) { + dataDir, err := WriteIndexTestData(Data, "ID", "") + assert.NoError(t, err) + indexer := createDiskIndexer(dataDir) + + err = indexer.AddIndex(&Pet{}, "Kind", "ID", "pets", "non_unique", nil, false) + assert.NoError(t, err) + + pet1 := Pet{ID: "goefe-789", Kind: "Hog", Color: "Green", Name: "Dicky"} + pet2 := Pet{ID: "xadaf-189", Kind: "Hog", Color: "Green", Name: "Ricky"} + + _, err = indexer.Add(pet1) + assert.NoError(t, err) + + _, err = indexer.Add(pet2) + assert.NoError(t, err) + + res, err := indexer.FindBy(Pet{}, "Kind", "Hog") + assert.NoError(t, err) + + t.Log(res) + + _ = os.RemoveAll(dataDir) +} + +func TestIndexer_Disk_AddWithAutoincrementIndex(t *testing.T) { + dataDir, err := WriteIndexTestData(Data, "ID", "") + assert.NoError(t, err) + indexer := createDiskIndexer(dataDir) + + err = indexer.AddIndex(&User{}, "UID", "ID", "users", "autoincrement", &option.Bound{Lower: 5}, false) + assert.NoError(t, err) + + res1, err := indexer.Add(Data["users"][0]) + assert.NoError(t, err) + assert.Equal(t, "UID", res1[0].Field) + assert.Equal(t, "5", path.Base(res1[0].Value)) + + res2, err := indexer.Add(Data["users"][1]) + assert.NoError(t, err) + assert.Equal(t, "UID", res2[0].Field) + assert.Equal(t, "6", path.Base(res2[0].Value)) + + resFindBy, err := indexer.FindBy(User{}, "UID", "6") + assert.NoError(t, err) + assert.Equal(t, "hijklmn-456", resFindBy[0]) + t.Log(resFindBy) + + _ = os.RemoveAll(dataDir) +} + +func TestIndexer_Disk_DeleteWithNonUniqueIndex(t *testing.T) { + dataDir, err := WriteIndexTestData(Data, "ID", "") + assert.NoError(t, err) + indexer := createDiskIndexer(dataDir) + + err = indexer.AddIndex(&Pet{}, "Kind", "ID", "pets", "non_unique", nil, false) + assert.NoError(t, err) + + pet1 := Pet{ID: "goefe-789", Kind: "Hog", Color: "Green", Name: "Dicky"} + pet2 := Pet{ID: "xadaf-189", Kind: "Hog", Color: "Green", Name: "Ricky"} + + _, err = indexer.Add(pet1) + assert.NoError(t, err) + + _, err = indexer.Add(pet2) + assert.NoError(t, err) + + err = indexer.Delete(pet2) + assert.NoError(t, err) + + _ = os.RemoveAll(dataDir) +} + +func TestIndexer_Disk_SearchWithNonUniqueIndex(t *testing.T) { + dataDir, err := WriteIndexTestData(Data, "ID", "") + assert.NoError(t, err) + indexer := createDiskIndexer(dataDir) + + err = indexer.AddIndex(&Pet{}, "Name", "ID", "pets", "non_unique", nil, false) + assert.NoError(t, err) + + pet1 := Pet{ID: "goefe-789", Kind: "Hog", Color: "Green", Name: "Dicky"} + pet2 := Pet{ID: "xadaf-189", Kind: "Hog", Color: "Green", Name: "Ricky"} + + _, err = indexer.Add(pet1) + assert.NoError(t, err) + + _, err = indexer.Add(pet2) + assert.NoError(t, err) + + res, err := indexer.FindByPartial(pet2, "Name", "*ky") + assert.NoError(t, err) + + t.Log(res) + _ = os.RemoveAll(dataDir) +} + +func TestIndexer_Disk_UpdateWithUniqueIndex(t *testing.T) { + dataDir, err := WriteIndexTestData(Data, "ID", "") + assert.NoError(t, err) + indexer := createDiskIndexer(dataDir) + + err = indexer.AddIndex(&User{}, "UserName", "ID", "users", "unique", nil, false) + assert.NoError(t, err) + + err = indexer.AddIndex(&User{}, "Email", "ID", "users", "unique", nil, false) + assert.NoError(t, err) + + user1 := &User{ID: "abcdefg-123", UserName: "mikey", Email: "mikey@example.com"} + user2 := &User{ID: "hijklmn-456", UserName: "frank", Email: "frank@example.com"} + + _, err = indexer.Add(user1) + assert.NoError(t, err) + + _, err = indexer.Add(user2) + assert.NoError(t, err) + + err = indexer.Update(user1, &User{ + ID: "abcdefg-123", + UserName: "mikey-new", + Email: "mikey@example.com", + }) + assert.NoError(t, err) + v, err1 := indexer.FindBy(&User{}, "UserName", "mikey-new") + assert.NoError(t, err1) + assert.Len(t, v, 1) + v, err2 := indexer.FindBy(&User{}, "UserName", "mikey") + assert.NoError(t, err2) + assert.Len(t, v, 0) + + err1 = indexer.Update(&User{ + ID: "abcdefg-123", + UserName: "mikey-new", + Email: "mikey@example.com", + }, &User{ + ID: "abcdefg-123", + UserName: "mikey-newest", + Email: "mikey-new@example.com", + }) + assert.NoError(t, err1) + fbUserName, err2 := indexer.FindBy(&User{}, "UserName", "mikey-newest") + assert.NoError(t, err2) + assert.Len(t, fbUserName, 1) + fbEmail, err3 := indexer.FindBy(&User{}, "Email", "mikey-new@example.com") + assert.NoError(t, err3) + assert.Len(t, fbEmail, 1) + + _ = os.RemoveAll(dataDir) +} + +func TestIndexer_Disk_UpdateWithNonUniqueIndex(t *testing.T) { + dataDir, err := WriteIndexTestData(Data, "ID", "") + assert.NoError(t, err) + indexer := createDiskIndexer(dataDir) + + err = indexer.AddIndex(&Pet{}, "Name", "ID", "pets", "non_unique", nil, false) + assert.NoError(t, err) + + pet1 := Pet{ID: "goefe-789", Kind: "Hog", Color: "Green", Name: "Dicky"} + pet2 := Pet{ID: "xadaf-189", Kind: "Hog", Color: "Green", Name: "Ricky"} + + _, err = indexer.Add(pet1) + assert.NoError(t, err) + + _, err = indexer.Add(pet2) + assert.NoError(t, err) + + _ = os.RemoveAll(dataDir) +} + +func createCs3Indexer() *Indexer { + return CreateIndexer(&config.Config{ + Repo: config.Repo{ + CS3: config.CS3{ + ProviderAddr: "0.0.0.0:9215", + DataURL: "http://localhost:9216", + DataPrefix: "data", + JWTSecret: "Pive-Fumkiu4", + }, + }, + }) +} + +func createDiskIndexer(dataDir string) *Indexer { + return CreateIndexer(&config.Config{ + Repo: config.Repo{ + Disk: config.Disk{ + Path: dataDir, + }, + }, + }) +} diff --git a/accounts/pkg/indexer/map.go b/accounts/pkg/indexer/map.go new file mode 100644 index 000000000..77e8e0777 --- /dev/null +++ b/accounts/pkg/indexer/map.go @@ -0,0 +1,27 @@ +package indexer + +import "github.com/owncloud/ocis/accounts/pkg/indexer/index" + +// typeMap stores the indexer layout at runtime. + +type typeMap map[tName]typeMapping +type tName = string +type fieldName = string + +type typeMapping struct { + PKFieldName string + IndicesByField map[fieldName][]index.Index +} + +func (m typeMap) addIndex(typeName string, pkName string, idx index.Index) { + if val, ok := m[typeName]; ok { + val.IndicesByField[idx.IndexBy()] = append(val.IndicesByField[idx.IndexBy()], idx) + return + } + m[typeName] = typeMapping{ + PKFieldName: pkName, + IndicesByField: map[string][]index.Index{ + idx.IndexBy(): {idx}, + }, + } +} diff --git a/accounts/pkg/indexer/option/option.go b/accounts/pkg/indexer/option/option.go new file mode 100644 index 000000000..df365d1ab --- /dev/null +++ b/accounts/pkg/indexer/option/option.go @@ -0,0 +1,132 @@ +package option + +// Option defines a single option function. +type Option func(o *Options) + +// Bound represents a lower and upper bound range for an index. +// todo: if we would like to provide an upper bound then we would need to deal with ranges, in which case this is why the +// upper bound attribute is here. +type Bound struct { + Lower, Upper int64 +} + +// Options defines the available options for this package. +type Options struct { + CaseInsensitive bool + Bound *Bound + + // Disk Options + TypeName string + IndexBy string + FilesDir string + IndexBaseDir string + DataDir string + EntityDirName string + Entity interface{} + + // CS3 options + DataURL string + DataPrefix string + JWTSecret string + ProviderAddr string + ServiceUserUUID string + ServiceUserName string +} + +// CaseInsensitive sets the CaseInsensitive field. +func CaseInsensitive(val bool) Option { + return func(o *Options) { + o.CaseInsensitive = val + } +} + +// WithBounds sets the Bounds field. +func WithBounds(val *Bound) Option { + return func(o *Options) { + o.Bound = val + } +} + +// WithEntity sets the Entity field. +func WithEntity(val interface{}) Option { + return func(o *Options) { + o.Entity = val + } +} + +// WithJWTSecret sets the JWTSecret field. +func WithJWTSecret(val string) Option { + return func(o *Options) { + o.JWTSecret = val + } +} + +// WithDataURL sets the DataURl field. +func WithDataURL(val string) Option { + return func(o *Options) { + o.DataURL = val + } +} + +// WithDataPrefix sets the DataPrefix field. +func WithDataPrefix(val string) Option { + return func(o *Options) { + o.DataPrefix = val + } +} + +// WithEntityDirName sets the EntityDirName field. +func WithEntityDirName(val string) Option { + return func(o *Options) { + o.EntityDirName = val + } +} + +// WithDataDir sets the DataDir option. +func WithDataDir(val string) Option { + return func(o *Options) { + o.DataDir = val + } +} + +// WithTypeName sets the TypeName option. +func WithTypeName(val string) Option { + return func(o *Options) { + o.TypeName = val + } +} + +// WithIndexBy sets the option IndexBy. +func WithIndexBy(val string) Option { + return func(o *Options) { + o.IndexBy = val + } +} + +// WithFilesDir sets the option FilesDir. +func WithFilesDir(val string) Option { + return func(o *Options) { + o.FilesDir = val + } +} + +// WithProviderAddr sets the option ProviderAddr. +func WithProviderAddr(val string) Option { + return func(o *Options) { + o.ProviderAddr = val + } +} + +// WithServiceUserUUID sets the option ServiceUserUUID. +func WithServiceUserUUID(val string) Option { + return func(o *Options) { + o.ServiceUserUUID = val + } +} + +// WithServiceUserName sets the option ServiceUserName. +func WithServiceUserName(val string) Option { + return func(o *Options) { + o.ServiceUserName = val + } +} diff --git a/accounts/pkg/indexer/reflect.go b/accounts/pkg/indexer/reflect.go new file mode 100644 index 000000000..137175292 --- /dev/null +++ b/accounts/pkg/indexer/reflect.go @@ -0,0 +1,41 @@ +package indexer + +import ( + "errors" + "path" + "reflect" + "strconv" + "strings" +) + +func getType(v interface{}) (reflect.Value, error) { + rv := reflect.ValueOf(v) + for rv.Kind() == reflect.Ptr || rv.Kind() == reflect.Interface { + rv = rv.Elem() + } + if !rv.IsValid() { + return reflect.Value{}, errors.New("failed to read value via reflection") + } + + return rv, nil +} + +func getTypeFQN(t interface{}) string { + typ, _ := getType(t) + typeName := path.Join(typ.Type().PkgPath(), typ.Type().Name()) + typeName = strings.ReplaceAll(typeName, "/", ".") + return typeName +} + +func valueOf(v interface{}, field string) string { + r := reflect.ValueOf(v) + f := reflect.Indirect(r).FieldByName(field) + + if f.Kind() == reflect.String { + return f.String() + } + if f.IsZero() { + return "" + } + return strconv.Itoa(int(f.Int())) +} diff --git a/accounts/pkg/indexer/reflect_test.go b/accounts/pkg/indexer/reflect_test.go new file mode 100644 index 000000000..0da505d2e --- /dev/null +++ b/accounts/pkg/indexer/reflect_test.go @@ -0,0 +1,53 @@ +package indexer + +import ( + "testing" +) + +func Test_getTypeFQN(t *testing.T) { + type someT struct{} + + type args struct { + t interface{} + } + tests := []struct { + name string + args args + want string + }{ + {name: "ByValue", args: args{&someT{}}, want: "github.com.owncloud.ocis.accounts.pkg.indexer.someT"}, + {name: "ByRef", args: args{someT{}}, want: "github.com.owncloud.ocis.accounts.pkg.indexer.someT"}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := getTypeFQN(tt.args.t); got != tt.want { + t.Errorf("getTypeFQN() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_valueOf(t *testing.T) { + type someT struct { + val string + } + type args struct { + v interface{} + field string + } + tests := []struct { + name string + args args + want string + }{ + {name: "ByValue", args: args{v: someT{val: "hello"}, field: "val"}, want: "hello"}, + {name: "ByRef", args: args{v: &someT{val: "hello"}, field: "val"}, want: "hello"}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := valueOf(tt.args.v, tt.args.field); got != tt.want { + t.Errorf("valueOf() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/accounts/pkg/indexer/registry/registry.go b/accounts/pkg/indexer/registry/registry.go new file mode 100644 index 000000000..6729ed3c0 --- /dev/null +++ b/accounts/pkg/indexer/registry/registry.go @@ -0,0 +1,15 @@ +package registry + +import ( + "github.com/owncloud/ocis/accounts/pkg/indexer/index" + "github.com/owncloud/ocis/accounts/pkg/indexer/option" +) + +// IndexConstructor is a constructor function for creating index.Index. +type IndexConstructor func(o ...option.Option) index.Index + +// IndexConstructorRegistry undocumented. +var IndexConstructorRegistry = map[string]map[string]IndexConstructor{ + "disk": {}, + "cs3": {}, +} diff --git a/accounts/pkg/indexer/test/data.go b/accounts/pkg/indexer/test/data.go new file mode 100644 index 000000000..98b2916a3 --- /dev/null +++ b/accounts/pkg/indexer/test/data.go @@ -0,0 +1,92 @@ +package test + +import ( + "encoding/json" + "io/ioutil" + "os" + "path" +) + +// User is a user. +type User struct { + ID, UserName, Email string + UID int +} + +// Pet is a pet. +type Pet struct { + ID, Kind, Color, Name string + UID int +} + +// Data mock data. +var Data = map[string][]interface{}{ + "users": { + User{ID: "abcdefg-123", UserName: "mikey", Email: "mikey@example.com"}, + User{ID: "hijklmn-456", UserName: "frank", Email: "frank@example.com"}, + User{ID: "ewf4ofk-555", UserName: "jacky", Email: "jacky@example.com"}, + User{ID: "rulan54-777", UserName: "jones", Email: "jones@example.com"}, + }, + "pets": { + Pet{ID: "rebef-123", Kind: "Dog", Color: "Brown", Name: "Waldo"}, + Pet{ID: "wefwe-456", Kind: "Cat", Color: "White", Name: "Snowy"}, + Pet{ID: "goefe-789", Kind: "Hog", Color: "Green", Name: "Dicky"}, + Pet{ID: "xadaf-189", Kind: "Hog", Color: "Green", Name: "Ricky"}, + }, +} + +// WriteIndexTestData writes mock data to disk. +func WriteIndexTestData(m map[string][]interface{}, privateKey, dir string) (string, error) { + rootDir, err := getRootDir(dir) + if err != nil { + return "", err + } + + err = writeData(m, privateKey, rootDir) + if err != nil { + return "", err + } + + return rootDir, nil +} + +// getRootDir allows for some minimal behavior on destination on disk. Testing the cs3 api behavior locally means +// keeping track of where the cs3 data lives on disk, this function allows for multiplexing whether or not to use a +// temporary folder or an already defined one. +func getRootDir(dir string) (string, error) { + var rootDir string + var err error + + if dir != "" { + rootDir = dir + } else { + rootDir, err = CreateTmpDir() + if err != nil { + return "", err + } + } + return rootDir, nil +} + +// writeData writes test data to disk on rootDir location Marshaled as json. +func writeData(m map[string][]interface{}, privateKey string, rootDir string) error { + for dirName := range m { + fileTypePath := path.Join(rootDir, dirName) + + if err := os.MkdirAll(fileTypePath, 0777); err != nil { + return err + } + for _, u := range m[dirName] { + data, err := json.Marshal(u) + if err != nil { + return err + } + + pkVal := ValueOf(u, privateKey) + if err := ioutil.WriteFile(path.Join(fileTypePath, pkVal), data, 0777); err != nil { + return err + } + } + } + return nil +} diff --git a/accounts/pkg/indexer/test/helpers.go b/accounts/pkg/indexer/test/helpers.go new file mode 100644 index 000000000..eaad855ec --- /dev/null +++ b/accounts/pkg/indexer/test/helpers.go @@ -0,0 +1,48 @@ +package test + +import ( + "errors" + "io/ioutil" + "path" + "reflect" + "strings" +) + +// CreateTmpDir creates a temporary dir for tests data. +func CreateTmpDir() (string, error) { + name, err := ioutil.TempDir("/var/tmp", "testfiles-") + if err != nil { + return "", err + } + + return name, nil +} + +// ValueOf gets the value of a type v on a given field . +func ValueOf(v interface{}, field string) string { + r := reflect.ValueOf(v) + f := reflect.Indirect(r).FieldByName(field) + + return f.String() +} + +func getType(v interface{}) (reflect.Value, error) { + rv := reflect.ValueOf(v) + for rv.Kind() == reflect.Ptr || rv.Kind() == reflect.Interface { + rv = rv.Elem() + } + if !rv.IsValid() { + return reflect.Value{}, errors.New("failed to read value via reflection") + } + + return rv, nil +} + +// GetTypeFQN formats a valid name from a type . This is a duplication of the already existing function in the +// indexer package, but since there is a circular dependency we chose to duplicate it. +func GetTypeFQN(t interface{}) string { + typ, _ := getType(t) + typeName := path.Join(typ.Type().PkgPath(), typ.Type().Name()) + typeName = strings.ReplaceAll(typeName, "/", ".") + return typeName +} diff --git a/accounts/pkg/indexer/test/test.go b/accounts/pkg/indexer/test/test.go new file mode 100644 index 000000000..5a131e71d --- /dev/null +++ b/accounts/pkg/indexer/test/test.go @@ -0,0 +1,23 @@ +package test + +import ( + "context" + "flag" + "github.com/micro/cli/v2" + "github.com/owncloud/ocis/storage/pkg/command" + mcfg "github.com/owncloud/ocis/storage/pkg/config" +) + +func init() { + go setupMetadataStorage() +} + +func setupMetadataStorage() { + cfg := mcfg.New() + app := cli.App{ + Name: "storage-metadata-for-tests", + Commands: []*cli.Command{command.StorageMetadata(cfg)}, + } + + _ = app.Command("storage-metadata").Run(cli.NewContext(&app, &flag.FlagSet{}, &cli.Context{Context: context.Background()})) +} diff --git a/accounts/pkg/proto/v0/accounts.pb.micro_test.go b/accounts/pkg/proto/v0/accounts.pb.micro_test.go index 223cfd439..f3e09a515 100644 --- a/accounts/pkg/proto/v0/accounts.pb.micro_test.go +++ b/accounts/pkg/proto/v0/accounts.pb.micro_test.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "io/ioutil" "log" "os" "path/filepath" @@ -25,51 +26,88 @@ import ( var service = grpc.Service{} -const dataPath = "./accounts-store" +var dataPath = createTmpDir() var newCreatedAccounts = []string{} var newCreatedGroups = []string{} var mockedRoleAssignment = map[string]string{} +var ( + user1 = proto.Account{ + Id: "f9149a32-2b8e-4f04-9e8d-937d81712b9a", + AccountEnabled: true, + IsResourceAccount: true, + CreationType: "", + DisplayName: "User One", + PreferredName: "user1", + OnPremisesSamAccountName: "user1", + UidNumber: 20009, + GidNumber: 30000, + Mail: "user1@example.com", + Identities: []*proto.Identities{nil}, + PasswordProfile: &proto.PasswordProfile{Password: "heysdjfsdlk"}, + MemberOf: []*proto.Group{ + {Id: "509a9dcd-bb37-4f4f-a01a-19dca27d9cfa"}, // users + }, + } + user2 = proto.Account{ + Id: "e9149a32-2b8e-4f04-9e8d-937d81712b9a", + AccountEnabled: true, + IsResourceAccount: true, + CreationType: "", + DisplayName: "User Two", + PreferredName: "user2", + OnPremisesSamAccountName: "user2", + UidNumber: 20010, + GidNumber: 30000, + Mail: "user2@example.com", + Identities: []*proto.Identities{nil}, + PasswordProfile: &proto.PasswordProfile{Password: "hello123"}, + MemberOf: []*proto.Group{ + {Id: "509a9dcd-bb37-4f4f-a01a-19dca27d9cfa"}, // users + }, + } +) + +func init() { + service = grpc.NewService( + grpc.Namespace("com.owncloud.api"), + grpc.Name("accounts"), + grpc.Address("localhost:9180"), + ) + + cfg := config.New() + cfg.Server.AccountsDataPath = dataPath + cfg.Repo.Disk.Path = dataPath + var hdlr *svc.Service + var err error + + if hdlr, err = svc.New(svc.Logger(command.NewLogger(cfg)), svc.Config(cfg), svc.RoleService(buildRoleServiceMock())); err != nil { + log.Fatalf("Could not create new service") + } + + err = proto.RegisterAccountsServiceHandler(service.Server(), hdlr) + if err != nil { + log.Fatal("could not register the Accounts handler") + } + err = proto.RegisterGroupsServiceHandler(service.Server(), hdlr) + if err != nil { + log.Fatal("could not register the Groups handler") + } + + err = service.Server().Start() + if err != nil { + log.Fatal(err) + } +} + func getAccount(user string) *proto.Account { switch user { case "user1": - return &proto.Account{ - Id: "f9149a32-2b8e-4f04-9e8d-937d81712b9a", - AccountEnabled: true, - IsResourceAccount: true, - CreationType: "", - DisplayName: "User One", - PreferredName: "user1", - OnPremisesSamAccountName: "user1", - UidNumber: 20009, - GidNumber: 30000, - Mail: "user1@example.com", - Identities: []*proto.Identities{nil}, - PasswordProfile: &proto.PasswordProfile{Password: "heysdjfsdlk"}, - MemberOf: []*proto.Group{ - {Id: "509a9dcd-bb37-4f4f-a01a-19dca27d9cfa"}, // users - }, - } + return &user1 case "user2": - return &proto.Account{ - Id: "e9149a32-2b8e-4f04-9e8d-937d81712b9a", - AccountEnabled: true, - IsResourceAccount: true, - CreationType: "", - DisplayName: "User Two", - PreferredName: "user2", - OnPremisesSamAccountName: "user2", - UidNumber: 20009, - GidNumber: 30000, - Mail: "user2@example.com", - Identities: []*proto.Identities{nil}, - PasswordProfile: &proto.PasswordProfile{Password: "hello123"}, - MemberOf: []*proto.Group{ - {Id: "509a9dcd-bb37-4f4f-a01a-19dca27d9cfa"}, // users - }, - } + return &user2 default: return &proto.Account{ Id: fmt.Sprintf("new-id-%s", user), @@ -154,38 +192,6 @@ func getTestGroups(group string) *proto.Group { return nil } -func init() { - service = grpc.NewService( - grpc.Namespace("com.owncloud.api"), - grpc.Name("accounts"), - grpc.Address("localhost:9180"), - ) - - cfg := config.New() - cfg.Server.AccountsDataPath = dataPath - cfg.Repo.Disk.Path = dataPath - var hdlr *svc.Service - var err error - - if hdlr, err = svc.New(svc.Logger(command.NewLogger(cfg)), svc.Config(cfg), svc.RoleService(buildRoleServiceMock())); err != nil { - log.Fatalf("Could not create new service") - } - - err = proto.RegisterAccountsServiceHandler(service.Server(), hdlr) - if err != nil { - log.Fatal("could not register the Accounts handler") - } - err = proto.RegisterGroupsServiceHandler(service.Server(), hdlr) - if err != nil { - log.Fatal("could not register the Groups handler") - } - - err = service.Server().Start() - if err != nil { - log.Fatal(err) - } -} - func buildRoleServiceMock() settings.RoleService { return settings.MockRoleService{ AssignRoleToUserFunc: func(ctx context.Context, req *settings.AssignRoleToUserRequest, opts ...client.CallOption) (res *settings.AssignRoleToUserResponse, err error) { @@ -208,8 +214,7 @@ func cleanUp(t *testing.T) { if _, err := os.Stat(path); os.IsNotExist(err) { continue } - _, err := deleteAccount(t, id) - checkError(t, err) + _, _ = deleteAccount(t, id) } datastore = filepath.Join(dataPath, "groups") @@ -219,8 +224,7 @@ func cleanUp(t *testing.T) { if _, err := os.Stat(path); os.IsNotExist(err) { continue } - _, err := deleteGroup(t, id) - checkError(t, err) + _, _ = deleteGroup(t, id) } newCreatedAccounts = []string{} @@ -309,12 +313,6 @@ func assertGroupHasMember(t *testing.T, grp *proto.Group, memberId string) { t.Fatalf("Member with id %s expected to be in group '%s', but not found", memberId, grp.DisplayName) } -func checkError(t *testing.T, err error) { - if err != nil { - t.Fatalf("Expected Error to be nil but got %s", err) - } -} - func createAccount(t *testing.T, user string) (*proto.Account, error) { client := service.Client() cl := proto.NewAccountsService("com.owncloud.api.accounts", client) @@ -368,7 +366,7 @@ func listGroups(t *testing.T) *proto.ListGroupsResponse { cl := proto.NewGroupsService("com.owncloud.api.accounts", client) response, err := cl.ListGroups(context.Background(), request) - checkError(t, err) + assert.NoError(t, err) return response } @@ -390,18 +388,27 @@ func deleteGroup(t *testing.T, id string) (*empty.Empty, error) { return res, err } +// createTmpDir creates a temporary dir for tests data. +func createTmpDir() string { + name, err := ioutil.TempDir("/var/tmp", "ocis-accounts-store-") + if err != nil { + panic(err) + } + + return name +} + // https://github.com/owncloud/ocis/accounts/issues/61 func TestCreateAccount(t *testing.T) { - resp, err := createAccount(t, "user1") - checkError(t, err) + assert.NoError(t, err) assertUserExists(t, getAccount("user1")) assert.IsType(t, &proto.Account{}, resp) // Account is not returned in response // assertAccountsSame(t, getAccount("user1"), resp) resp, err = createAccount(t, "user2") - checkError(t, err) + assert.NoError(t, err) assertUserExists(t, getAccount("user2")) assert.IsType(t, &proto.Account{}, resp) // Account is not returned in response @@ -410,24 +417,25 @@ func TestCreateAccount(t *testing.T) { cleanUp(t) } -// https://github.com/owncloud/ocis/accounts/issues/62 +// https://github.com/owncloud/ocis-accounts/issues/62 func TestCreateExistingUser(t *testing.T) { - createAccount(t, "user1") - _, err := createAccount(t, "user1") + var err error + _, err = createAccount(t, "user1") + assert.NoError(t, err) - // Should give error but it does not - checkError(t, err) + _, err = createAccount(t, "user1") + assert.Error(t, err) assertUserExists(t, getAccount("user1")) cleanUp(t) } // All tests fail after running this -// https://github.com/owncloud/ocis/accounts/issues/62 +// https://github.com/owncloud/ocis/accounts-issues/62 func TestCreateAccountInvalidUserName(t *testing.T) { resp, err := listAccounts(t) - checkError(t, err) + assert.NoError(t, err) numAccounts := len(resp.GetAccounts()) testData := []string{ @@ -448,7 +456,7 @@ func TestCreateAccountInvalidUserName(t *testing.T) { // resp should have the same number of accounts resp, err = listAccounts(t) - checkError(t, err) + assert.NoError(t, err) assert.Equal(t, numAccounts, len(resp.GetAccounts())) @@ -456,57 +464,58 @@ func TestCreateAccountInvalidUserName(t *testing.T) { } func TestUpdateAccount(t *testing.T) { - _, _ = createAccount(t, "user1") - tests := []struct { - name string - userAccount *proto.Account + name string + userAccount *proto.Account + expectedErrOnUpdate error }{ { "Update user (demonstration of updatable fields)", &proto.Account{ - DisplayName: "Alice Hansen", - PreferredName: "Wonderful Alice", - OnPremisesDistinguishedName: "Alice", - UidNumber: 20010, - GidNumber: 30001, - Mail: "alice@example.com", + DisplayName: "Alice Hansen", + PreferredName: "Wonderful-Alice", + OnPremisesSamAccountName: "Alice", + UidNumber: 20010, + GidNumber: 30001, + Mail: "alice@example.com", }, + nil, }, { "Update user with unicode data", &proto.Account{ - DisplayName: "एलिस हेन्सेन", - PreferredName: "अद्भुत एलिस", - OnPremisesDistinguishedName: "एलिस", - UidNumber: 20010, - GidNumber: 30001, - Mail: "एलिस@उदाहरण.com", + DisplayName: "एलिस हेन्सेन", + PreferredName: "अद्भुत-एलिस", + OnPremisesSamAccountName: "एलिस", + UidNumber: 20010, + GidNumber: 30001, + Mail: "एलिस@उदाहरण.com", }, + merrors.BadRequest(".", "preferred_name 'अद्भुत-एलिस' must be at least the local part of an email"), }, { "Update user with empty data values", &proto.Account{ - DisplayName: "", - PreferredName: "", - OnPremisesDistinguishedName: "", - UidNumber: 0, - GidNumber: 0, - Mail: "", + DisplayName: "", + PreferredName: "", + OnPremisesSamAccountName: "", + UidNumber: 0, + GidNumber: 0, + Mail: "", }, + merrors.BadRequest(".", "preferred_name '' must be at least the local part of an email"), }, { "Update user with strange data", &proto.Account{ - DisplayName: "12345", - PreferredName: "12345", - OnPremisesDistinguishedName: "54321", - UidNumber: 1000, - GidNumber: 1000, - // No email validation - // https://github.com/owncloud/ocis/accounts/issues/77 - Mail: "1.2@3.c_@", + DisplayName: "12345", + PreferredName: "a12345", + OnPremisesSamAccountName: "a54321", + UidNumber: 1000, + GidNumber: 1000, + Mail: "1.2@3.c_@", }, + merrors.BadRequest(".", "mail '1.2@3.c_@' must be a valid email"), }, } @@ -524,20 +533,25 @@ func TestUpdateAccount(t *testing.T) { } t.Run(tt.name, func(t *testing.T) { - tt.userAccount.Id = "f9149a32-2b8e-4f04-9e8d-937d81712b9a" + acc, err := createAccount(t, "user1") + assert.NoError(t, err) + + tt.userAccount.Id = acc.Id tt.userAccount.AccountEnabled = false tt.userAccount.IsResourceAccount = false resp, err := updateAccount(t, tt.userAccount, updateMask) - - checkError(t, err) - - assert.IsType(t, &proto.Account{}, resp) - assertAccountsSame(t, tt.userAccount, resp) - assertUserExists(t, tt.userAccount) + if tt.expectedErrOnUpdate != nil { + assert.Error(t, err) + assert.Equal(t, tt.expectedErrOnUpdate, err) + } else { + assert.NoError(t, err) + assert.IsType(t, &proto.Account{}, resp) + assertAccountsSame(t, tt.userAccount, resp) + assertUserExists(t, tt.userAccount) + } + cleanUp(t) }) } - - cleanUp(t) } func TestUpdateNonUpdatableFieldsInAccount(t *testing.T) { @@ -554,6 +568,7 @@ func TestUpdateNonUpdatableFieldsInAccount(t *testing.T) { "CreationType", }, &proto.Account{ + Id: user1.Id, CreationType: "Type Test", }, }, @@ -563,6 +578,7 @@ func TestUpdateNonUpdatableFieldsInAccount(t *testing.T) { "PasswordProfile", }, &proto.Account{ + Id: user1.Id, PasswordProfile: &proto.PasswordProfile{Password: "new password"}, }, }, @@ -572,6 +588,7 @@ func TestUpdateNonUpdatableFieldsInAccount(t *testing.T) { "MemberOf", }, &proto.Account{ + Id: user1.Id, MemberOf: []*proto.Group{ {Id: "509a9dcd-bb37-4f4f-a01a-19dca27d9cfa"}, }, @@ -580,7 +597,6 @@ func TestUpdateNonUpdatableFieldsInAccount(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - tt.userAccount.Id = "f9149a32-2b8e-4f04-9e8d-937d81712b9a" res, err := updateAccount(t, tt.userAccount, tt.updateMask) if err == nil { t.Fatalf("Expected error while updating non updatable field, but found none.") @@ -602,6 +618,8 @@ func TestUpdateNonUpdatableFieldsInAccount(t *testing.T) { } }) } + + cleanUp(t) } func TestListAccounts(t *testing.T) { @@ -609,7 +627,7 @@ func TestListAccounts(t *testing.T) { createAccount(t, "user2") resp, err := listAccounts(t) - checkError(t, err) + assert.NoError(t, err) assert.IsType(t, &proto.ListAccountsResponse{}, resp) assert.Equal(t, 8, len(resp.Accounts)) @@ -622,31 +640,128 @@ func TestListAccounts(t *testing.T) { func TestListWithoutUserCreation(t *testing.T) { resp, err := listAccounts(t) - - checkError(t, err) + assert.NoError(t, err) // Only 5 default users assert.Equal(t, 6, len(resp.Accounts)) cleanUp(t) } +func TestListAccountsWithFilterQuery(t *testing.T) { + scenarios := []struct { + name string + query string + expectedIDs []string + }{ + // FIXME: disabled test scenarios need to be supported when implementing OData support + // OData implementation tracked in https://github.com/owncloud/ocis/issues/716 + //{ + // name: "ListAccounts with exact match on preferred_name", + // query: "preferred_name eq 'user1'", + // expectedIDs: []string{user1.Id}, + //}, + { + name: "ListAccounts with exact match on on_premises_sam_account_name", + query: "on_premises_sam_account_name eq 'user1'", + expectedIDs: []string{user1.Id}, + }, + { + name: "ListAccounts with exact match on mail", + query: "mail eq 'user1@example.com'", + expectedIDs: []string{user1.Id}, + }, + //{ + // name: "ListAccounts with exact match on id", + // query: "id eq 'f9149a32-2b8e-4f04-9e8d-937d81712b9a'", + // expectedIDs: []string{user1.Id}, + //}, + //{ + // name: "ListAccounts without match on preferred_name", + // query: "preferred_name eq 'wololo'", + // expectedIDs: []string{}, + //}, + //{ + // name: "ListAccounts with exact match on preferred_name AND mail", + // query: "preferred_name eq 'user1' and mail eq 'user1@example.com'", + // expectedIDs: []string{user1.Id}, + //}, + //{ + // name: "ListAccounts without match on preferred_name AND mail", + // query: "preferred_name eq 'user1' and mail eq 'wololo@example.com'", + // expectedIDs: []string{}, + //}, + //{ + // name: "ListAccounts with exact match on preferred_name OR mail, preferred_name exists, mail exists", + // query: "preferred_name eq 'user1' or mail eq 'user1@example.com'", + // expectedIDs: []string{user1.Id}, + //}, + //{ + // name: "ListAccounts with exact match on preferred_name OR mail, preferred_name exists, mail does not exist", + // query: "preferred_name eq 'user1' or mail eq 'wololo@example.com'", + // expectedIDs: []string{user1.Id}, + //}, + //{ + // name: "ListAccounts with exact match on preferred_name OR mail, preferred_name does not exists, mail exists", + // query: "preferred_name eq 'wololo' or mail eq 'user1@example.com'", + // expectedIDs: []string{user1.Id}, + //}, + //{ + // name: "ListAccounts without match on preferred_name OR mail, preferred_name and mail do not exist", + // query: "preferred_name eq 'wololo' or mail eq 'wololo@example.com'", + // expectedIDs: []string{}, + //}, + //{ + // name: "ListAccounts with multiple matches on preferred_name", + // query: "startswith(preferred_name,'user*')", + // expectedIDs: []string{user1.Id, user2.Id}, + //}, + //{ + // name: "ListAccounts with multiple matches on on_premises_sam_account_name", + // query: "startswith(on_premises_sam_account_name,'user*')", + // expectedIDs: []string{user1.Id, user2.Id}, + //}, + } + + cl := proto.NewAccountsService("com.owncloud.api.accounts", service.Client()) + + for _, scenario := range scenarios { + t.Run(scenario.name, func(t *testing.T) { + _, err := createAccount(t, "user1") + assert.NoError(t, err) + _, err = createAccount(t, "user2") + assert.NoError(t, err) + + req := &proto.ListAccountsRequest{Query: scenario.query} + res, err := cl.ListAccounts(context.Background(), req) + assert.NoError(t, err) + ids := make([]string, 0) + for _, acc := range res.Accounts { + ids = append(ids, acc.Id) + } + assert.Equal(t, scenario.expectedIDs, ids) + cleanUp(t) + }) + } +} + func TestGetAccount(t *testing.T) { createAccount(t, "user1") req := &proto.GetAccountRequest{Id: getAccount("user1").Id} - client := service.Client() - cl := proto.NewAccountsService("com.owncloud.api.accounts", client) + cl := proto.NewAccountsService("com.owncloud.api.accounts", service.Client()) resp, err := cl.GetAccount(context.Background(), req) - checkError(t, err) + assert.NoError(t, err) assert.IsType(t, &proto.Account{}, resp) assertAccountsSame(t, getAccount("user1"), resp) cleanUp(t) } +//TODO: This segfaults! WIP + func TestDeleteAccount(t *testing.T) { createAccount(t, "user1") createAccount(t, "user2") @@ -657,7 +772,7 @@ func TestDeleteAccount(t *testing.T) { cl := proto.NewAccountsService("com.owncloud.api.accounts", client) resp, err := cl.DeleteAccount(context.Background(), req) - checkError(t, err) + assert.NoError(t, err) assert.IsType(t, resp, &empty.Empty{}) // Check the account doesn't exists anymore @@ -675,9 +790,9 @@ func TestListGroups(t *testing.T) { cl := proto.NewGroupsService("com.owncloud.api.accounts", client) resp, err := cl.ListGroups(context.Background(), req) - checkError(t, err) + assert.NoError(t, err) assert.IsType(t, &proto.ListGroupsResponse{}, resp) - assert.Equal(t, len(resp.Groups), 9) + assert.Equal(t, 9, len(resp.Groups)) groups := []string{ "sysusers", @@ -718,7 +833,7 @@ func TestGetGroups(t *testing.T) { req := &proto.GetGroupRequest{Id: group.Id} resp, err := cl.GetGroup(context.Background(), req) - checkError(t, err) + assert.NoError(t, err) assert.IsType(t, &proto.Group{}, resp) assertGroupsSame(t, group, resp) } @@ -733,7 +848,7 @@ func TestCreateGroup(t *testing.T) { }} res, err := createGroup(t, group) - checkError(t, err) + assert.NoError(t, err) assert.IsType(t, &proto.Group{}, res) @@ -772,12 +887,12 @@ func TestDeleteGroup(t *testing.T) { req := &proto.DeleteGroupRequest{Id: grp1.Id} res, err := cl.DeleteGroup(context.Background(), req) assert.IsType(t, res, &empty.Empty{}) - checkError(t, err) + assert.NoError(t, err) req = &proto.DeleteGroupRequest{Id: grp2.Id} res, err = cl.DeleteGroup(context.Background(), req) assert.IsType(t, res, &empty.Empty{}) - checkError(t, err) + assert.NoError(t, err) groupsResponse := listGroups(t) assertResponseNotContainsGroup(t, groupsResponse, grp1) @@ -870,7 +985,7 @@ func TestAddMember(t *testing.T) { req := &proto.AddMemberRequest{GroupId: grp1.Id, AccountId: account.Id} res, err := cl.AddMember(context.Background(), req) - checkError(t, err) + assert.NoError(t, err) assert.IsType(t, &proto.Group{}, res) @@ -904,7 +1019,7 @@ func TestAddMemberAlreadyInGroup(t *testing.T) { res, err := cl.AddMember(context.Background(), req) // Should Give Error - checkError(t, err) + assert.NoError(t, err) assert.IsType(t, &proto.Group{}, res) //assert.Equal(t, proto.Group{}, *res) //assertGroupsSame(t, updatedGroup, res) @@ -975,7 +1090,7 @@ func TestRemoveMember(t *testing.T) { req := &proto.RemoveMemberRequest{GroupId: grp1.Id, AccountId: account.Id} res, err := cl.RemoveMember(context.Background(), req) - checkError(t, err) + assert.NoError(t, err) assert.IsType(t, &proto.Group{}, res) //assert.Equal(t, proto.Group{}, *res) @@ -1034,7 +1149,7 @@ func TestRemoveMemberNotInGroup(t *testing.T) { res, err := cl.RemoveMember(context.Background(), req) // Should give an error - checkError(t, err) + assert.NoError(t, err) assert.IsType(t, &proto.Group{}, res) //assert.Error(t, err) @@ -1071,7 +1186,7 @@ func TestListMembers(t *testing.T) { req := &proto.ListMembersRequest{Id: expectedGroup.Id} res, err := cl.ListMembers(context.Background(), req) - checkError(t, err) + assert.NoError(t, err) assert.Equal(t, len(res.Members), len(expectedGroup.Members)) @@ -1104,7 +1219,7 @@ func TestListMembersEmptyGroup(t *testing.T) { res, err := cl.ListMembers(context.Background(), req) - checkError(t, err) + assert.NoError(t, err) assert.Empty(t, res.Members) cleanUp(t) @@ -1120,12 +1235,12 @@ func TestAccountUpdateMask(t *testing.T) { Account: &proto.Account{ Id: user1.Id, DisplayName: "ShouldBeUpdated", - PreferredName: "ShouldStaySame", + PreferredName: "ShouldStaySame And Is Invalid Anyway", }} cl := proto.NewAccountsService("com.owncloud.api.accounts", client) res, err := cl.UpdateAccount(context.Background(), req) - checkError(t, err) + assert.NoError(t, err) assert.Equal(t, "ShouldBeUpdated", res.DisplayName) assert.Equal(t, user1.PreferredName, res.PreferredName) diff --git a/accounts/pkg/proto/v0/bleve.go b/accounts/pkg/proto/v0/bleve.go deleted file mode 100644 index 66188d84b..000000000 --- a/accounts/pkg/proto/v0/bleve.go +++ /dev/null @@ -1,13 +0,0 @@ -package proto - -// BleveAccount wraps the generated Account and adds a bleve type that is used to distinguish documents in the index -type BleveAccount struct { - Account - BleveType string `json:"bleve_type"` -} - -// BleveGroup wraps the generated Group and adds a bleve type that is used to distinguish documents in the index -type BleveGroup struct { - Group - BleveType string `json:"bleve_type"` -} diff --git a/accounts/pkg/provider/bleve.go b/accounts/pkg/provider/bleve.go deleted file mode 100644 index 5657e9409..000000000 --- a/accounts/pkg/provider/bleve.go +++ /dev/null @@ -1,128 +0,0 @@ -package provider - -import ( - "errors" - "fmt" - "strconv" - "strings" - - "github.com/CiscoM31/godata" - "github.com/blevesearch/bleve" - "github.com/blevesearch/bleve/search/query" -) - -func init() { - // add (ap)prox filter - godata.GlobalFilterTokenizer = FilterTokenizer() - godata.GlobalFilterParser.DefineOperator("ap", 2, godata.OpAssociationLeft, 4, false) -} - -// BuildBleveQuery converts a GoDataFilterQuery into a bleve query -func BuildBleveQuery(r *godata.GoDataFilterQuery) (query.Query, error) { - return recursiveBuildQuery(r.Tree) -} - -// Builds the filter recursively using DFS -func recursiveBuildQuery(n *godata.ParseNode) (query.Query, error) { - if n.Token.Type == godata.FilterTokenFunc { - switch n.Token.Value { - case "startswith": - if len(n.Children) != 2 { - return nil, errors.New("startswith match must have two children") - } - if n.Children[0].Token.Type != godata.FilterTokenLiteral { - return nil, errors.New("startswith expected a literal as the first param") - } - if n.Children[1].Token.Type != godata.FilterTokenString { - return nil, errors.New("startswith expected a string as the second param") - } // remove enclosing ' of string tokens (looks like 'some ol'' string') - value := n.Children[1].Token.Value[1 : len(n.Children[1].Token.Value)-1] - // unescape '' as ' - unescaped := strings.ReplaceAll(value, "''", "'") - q := bleve.NewPrefixQuery(unescaped) - q.SetField(n.Children[0].Token.Value) - return q, nil - // TODO contains as regex? - // TODO endswith as regex? - default: - return nil, godata.NotImplementedError(n.Token.Value + " is not implemented.") - } - } - if n.Token.Type == godata.FilterTokenLogical { - switch n.Token.Value { - case "eq": - if len(n.Children) != 2 { - return nil, errors.New("equality match must have two children") - } - if n.Children[0].Token.Type != godata.FilterTokenLiteral { - return nil, errors.New("equality expected a literal on the lhs") - } - if n.Children[1].Token.Type == godata.FilterTokenString { - // for escape rules see http://docs.oasis-open.org/odata/odata/v4.01/cs01/part2-url-conventions/odata-v4.01-cs01-part2-url-conventions.html#sec_URLComponents - // remove enclosing ' of string tokens (looks like 'some ol'' string') - value := n.Children[1].Token.Value[1 : len(n.Children[1].Token.Value)-1] - // unescape '' as ' - unescaped := strings.ReplaceAll(value, "''", "'") - // use a match query, so the field mapping, e.g. lowercase is applied to the value - // remember we defined the field mapping for `preferred_name` to be lowercase - // a term query like `preferred_name eq 'Artur'` would use `Artur` to search in the index and come up empty - // a match query will apply the field mapping (lowercasing `Artur` to `artur`) before doing the search - // TODO there is a mismatch between the LDAP and odata filters: - // - LDAP matching rules depend on the attribute: see https://ldapwiki.com/wiki/MatchingRule - // - odata has functions like `startswith`, `contains`, `tolower`, `toupper`, `matchesPattern` andy more: see http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_BuiltinQueryFunctions - // - ocis-glauth should do the mapping between LDAP and odata filter - q := bleve.NewMatchQuery(unescaped) - q.SetField(n.Children[0].Token.Value) - return q, nil - } else if n.Children[1].Token.Type == godata.FilterTokenInteger { - v, err := strconv.ParseFloat(n.Children[1].Token.Value, 64) - if err != nil { - return nil, err - } - incl := true - q := bleve.NewNumericRangeInclusiveQuery(&v, &v, &incl, &incl) - q.SetField(n.Children[0].Token.Value) - return q, nil - } - return nil, fmt.Errorf("equality expected a string or int on the rhs, got %d", n.Children[1].Token.Type) - case "and": - q := query.NewConjunctionQuery([]query.Query{}) - for _, child := range n.Children { - subQuery, err := recursiveBuildQuery(child) - if err != nil { - return nil, err - } - if subQuery != nil { - q.AddQuery(subQuery) - } - } - return q, nil - case "or": - q := query.NewDisjunctionQuery([]query.Query{}) - for _, child := range n.Children { - subQuery, err := recursiveBuildQuery(child) - if err != nil { - return nil, err - } - if subQuery != nil { - q.AddQuery(subQuery) - } - } - return q, nil - case "Not": - if len(n.Children) != 1 { - return nil, errors.New("not filter must have only one child") - } - subQuery, err := recursiveBuildQuery(n.Children[0]) - if err != nil { - return nil, err - } - q := query.NewBooleanQuery(nil, nil, []query.Query{subQuery}) - return q, nil - default: - return nil, godata.NotImplementedError(n.Token.Value + " is not implemented.") - } - } - - return nil, godata.NotImplementedError(n.Token.Value + " is not implemented.") -} diff --git a/accounts/pkg/provider/tokenizer.go b/accounts/pkg/provider/tokenizer.go deleted file mode 100644 index f4d7c59ef..000000000 --- a/accounts/pkg/provider/tokenizer.go +++ /dev/null @@ -1,38 +0,0 @@ -package provider - -import "github.com/CiscoM31/godata" - -// FilterTokenizer creates a tokenizer capable of tokenizing filter statements -// TODO disable tokens we don't handle anyway -func FilterTokenizer() *godata.Tokenizer { - t := godata.Tokenizer{} - t.Add("^[0-9]{4,4}-[0-9]{2,2}-[0-9]{2,2}T[0-9]{2,2}:[0-9]{2,2}(:[0-9]{2,2}(.[0-9]+)?)?(Z|[+-][0-9]{2,2}:[0-9]{2,2})", godata.FilterTokenDateTime) - t.Add("^-?[0-9]{4,4}-[0-9]{2,2}-[0-9]{2,2}", godata.FilterTokenDate) - t.Add("^[0-9]{2,2}:[0-9]{2,2}(:[0-9]{2,2}(.[0-9]+)?)?", godata.FilterTokenTime) - t.Add("^\\(", godata.FilterTokenOpenParen) - t.Add("^\\)", godata.FilterTokenCloseParen) - t.Add("^/", godata.FilterTokenNav) - t.Add("^:", godata.FilterTokenColon) - t.Add("^,", godata.FilterTokenComma) - t.Add("^(geo.distance|geo.intersects|geo.length)", godata.FilterTokenFunc) - t.Add("^(substringof|substring|length|indexof)", godata.FilterTokenFunc) - // only change from the global tokenizer is the added ap - t.Add("^(eq|ne|gt|ge|lt|le|and|or|not|has|in|ap)", godata.FilterTokenLogical) - t.Add("^(add|sub|mul|divby|div|mod)", godata.FilterTokenOp) - t.Add("^(contains|endswith|startswith|tolower|toupper|"+ - "trim|concat|year|month|day|hour|minute|second|fractionalseconds|date|"+ - "time|totaloffsetminutes|now|maxdatetime|mindatetime|totalseconds|round|"+ - "floor|ceiling|isof|cast)", godata.FilterTokenFunc) - t.Add("^(any|all)", godata.FilterTokenLambda) - t.Add("^null", godata.FilterTokenNull) - t.Add("^\\$it", godata.FilterTokenIt) - t.Add("^\\$root", godata.FilterTokenRoot) - t.Add("^-?[0-9]+\\.[0-9]+", godata.FilterTokenFloat) - t.Add("^-?[0-9]+", godata.FilterTokenInteger) - t.Add("^'(''|[^'])*'", godata.FilterTokenString) - t.Add("^(true|false)", godata.FilterTokenBoolean) - t.Add("^@*[a-zA-Z][a-zA-Z0-9_.]*", godata.FilterTokenLiteral) // The optional '@' character is used to identify parameter aliases - t.Ignore("^ ", godata.FilterTokenWhitespace) - - return &t -} diff --git a/accounts/pkg/service/v0/accounts.go b/accounts/pkg/service/v0/accounts.go index acdd1c855..b6a0ccec8 100644 --- a/accounts/pkg/service/v0/accounts.go +++ b/accounts/pkg/service/v0/accounts.go @@ -3,19 +3,19 @@ package service import ( "context" "fmt" - "path/filepath" + "path" "regexp" + "strconv" "sync" "time" - "github.com/CiscoM31/godata" - "github.com/blevesearch/bleve" + "github.com/owncloud/ocis/ocis-pkg/log" + "github.com/gofrs/uuid" "github.com/golang/protobuf/ptypes/empty" fieldmask_utils "github.com/mennanov/fieldmask-utils" merrors "github.com/micro/go-micro/v2/errors" "github.com/owncloud/ocis/accounts/pkg/proto/v0" - "github.com/owncloud/ocis/accounts/pkg/provider" "github.com/owncloud/ocis/accounts/pkg/storage" "github.com/owncloud/ocis/ocis-pkg/roles" settings "github.com/owncloud/ocis/settings/pkg/proto/v0" @@ -35,22 +35,6 @@ import ( // accLock mutually exclude readers from writers on account files var accLock sync.Mutex -func (s Service) indexAccount(id string) error { - a := &proto.BleveAccount{ - BleveType: "account", - } - if err := s.repo.LoadAccount(context.Background(), id, &a.Account); err != nil { - s.log.Error().Err(err).Str("account", id).Msg("could not load account") - return err - } - s.log.Debug().Interface("account", a).Msg("found account") - if err := s.index.Index(a.Id, a); err != nil { - s.log.Error().Err(err).Interface("account", a).Msg("could not index account") - return err - } - return nil -} - // an auth request is currently hardcoded and has to match this regex // login eq \"teddy\" and password eq \"F&1!b90t111!\" var authQuery = regexp.MustCompile(`^login eq '(.*)' and password eq '(.*)'$`) // TODO how is ' escaped in the password? @@ -74,17 +58,6 @@ func (s Service) expandMemberOf(a *proto.Account) { a.MemberOf = expanded } -func (s Service) passwordIsValid(hash string, pwd string) (ok bool) { - defer func() { - if r := recover(); r != nil { - s.log.Error().Err(fmt.Errorf("%s", r)).Str("hash", hash).Msg("password lib panicked") - } - }() - - c := crypt.NewFromHash(hash) - return c.Verify(hash, []byte(pwd)) == nil -} - func (s Service) hasAccountManagementPermissions(ctx context.Context) bool { // get roles from context roleIDs, ok := roles.ReadRoleIDsFromContext(ctx) @@ -104,15 +77,12 @@ func (s Service) hasAccountManagementPermissions(ctx context.Context) bool { // serviceUserToIndex temporarily adds a service user to the index, which is supposed to be removed before the lock on the handler function is released func (s Service) serviceUserToIndex() (teardownServiceUser func()) { if s.Config.ServiceUser.Username != "" && s.Config.ServiceUser.UUID != "" { - err := s.index.Index(s.Config.ServiceUser.UUID, &proto.BleveAccount{ - BleveType: "account", - Account: s.getInMemoryServiceUser(), - }) + _, err := s.index.Add(s.getInMemoryServiceUser()) if err != nil { s.log.Logger.Err(err).Msg("service user was configured but failed to be added to the index") } else { return func() { - _ = s.index.Delete(s.Config.ServiceUser.UUID) + _ = s.index.Delete(s.getInMemoryServiceUser()) } } } @@ -140,83 +110,56 @@ func (s Service) ListAccounts(ctx context.Context, in *proto.ListAccountsRequest accLock.Lock() defer accLock.Unlock() - var password string teardownServiceUser := s.serviceUserToIndex() defer teardownServiceUser() - // check if this looks like an auth request - match := authQuery.FindStringSubmatch(in.Query) - if len(match) == 3 { - in.Query = fmt.Sprintf("on_premises_sam_account_name eq '%s'", match[1]) // todo fetch email? make query configurable - password = match[2] - if password == "" { - return merrors.Unauthorized(s.id, "password must not be empty") - } - } - - // only search for accounts - tq := bleve.NewTermQuery("account") - tq.SetField("bleve_type") - - query := bleve.NewConjunctionQuery(tq) - - if in.Query != "" { - // parse the query like an odata filter - var q *godata.GoDataFilterQuery - if q, err = godata.ParseFilterString(in.Query); err != nil { - s.log.Error().Err(err).Msg("could not parse query") - return merrors.InternalServerError(s.id, "could not parse query: %v", err.Error()) + match, authRequest := getAuthQueryMatch(in.Query) + if authRequest { + password := match[2] + if len(password) == 0 { + return merrors.Unauthorized(s.id, "account not found or invalid credentials") } - // convert to bleve query - bq, err := provider.BuildBleveQuery(q) - if err != nil { - s.log.Error().Err(err).Msg("could not build bleve query") - return merrors.InternalServerError(s.id, "could not build bleve query: %v", err.Error()) + ids, err := s.index.FindBy(&proto.Account{}, "OnPremisesSamAccountName", match[1]) + if err != nil || len(ids) > 1 { + return merrors.Unauthorized(s.id, "account not found or invalid credentials") + } + if len(ids) == 0 { + ids, err = s.index.FindBy(&proto.Account{}, "Mail", match[1]) + if err != nil || len(ids) != 1 { + return merrors.Unauthorized(s.id, "account not found or invalid credentials") + } } - query.AddQuery(bq) - } - s.log.Debug().Interface("query", query).Msg("using query") - - searchRequest := bleve.NewSearchRequest(query) - var searchResult *bleve.SearchResult - searchResult, err = s.index.Search(searchRequest) - if err != nil { - s.log.Error().Err(err).Msg("could not execute bleve search") - return merrors.InternalServerError(s.id, "could not execute bleve search: %v", err.Error()) - } - - s.log.Debug().Interface("result", searchResult).Msg("result") - - out.Accounts = make([]*proto.Account, 0) - - for _, hit := range searchResult.Hits { a := &proto.Account{} - if hit.ID == s.Config.ServiceUser.UUID { + err = s.repo.LoadAccount(ctx, ids[0], a) + if err != nil || a.PasswordProfile == nil || len(a.PasswordProfile.Password) == 0 { + return merrors.Unauthorized(s.id, "account not found or invalid credentials") + } + if !isPasswordValid(s.log, a.PasswordProfile.Password, password) { + return merrors.Unauthorized(s.id, "account not found or invalid credentials") + } + a.PasswordProfile.Password = "" + out.Accounts = []*proto.Account{a} + return nil + } + + searchResults, err := s.findAccountsByQuery(ctx, in.Query) + out.Accounts = make([]*proto.Account, 0, len(searchResults)) + + for _, hit := range searchResults { + a := &proto.Account{} + if hit == s.Config.ServiceUser.UUID { acc := s.getInMemoryServiceUser() a = &acc - } else if err = s.repo.LoadAccount(ctx, hit.ID, a); err != nil { - s.log.Error().Err(err).Str("account", hit.ID).Msg("could not load account, skipping") + } else if err = s.repo.LoadAccount(ctx, hit, a); err != nil { + s.log.Error().Err(err).Str("account", hit).Msg("could not load account, skipping") continue } - var currentHash string - if a.PasswordProfile != nil { - currentHash = a.PasswordProfile.Password - } s.debugLogAccount(a).Msg("found account") - if password != "" { - if a.PasswordProfile == nil { - s.debugLogAccount(a).Msg("no password profile") - return merrors.Unauthorized(s.id, "invalid password") - } - if !s.passwordIsValid(currentHash, password) { - return merrors.Unauthorized(s.id, "invalid password") - } - } // TODO add groups if requested // if in.FieldMask ... s.expandMemberOf(a) @@ -232,6 +175,90 @@ func (s Service) ListAccounts(ctx context.Context, in *proto.ListAccountsRequest return } +func (s Service) findAccountsByQuery(ctx context.Context, query string) ([]string, error) { + var searchResults []string + var err error + + if query == "" { + return s.index.FindByPartial(&proto.Account{}, "Mail", "*") + } + + // TODO: more explicit queries have to be on top + var onPremOrMailQuery = regexp.MustCompile(`^on_premises_sam_account_name eq '(.*)' or mail eq '(.*)'$`) + match := onPremOrMailQuery.FindStringSubmatch(query) + if len(match) == 3 { + resSam, err := s.index.FindByPartial(&proto.Account{}, "OnPremisesSamAccountName", match[1]+"*") + if err != nil { + return nil, err + } + resMail, err := s.index.FindByPartial(&proto.Account{}, "Mail", match[2]+"*") + if err != nil { + return nil, err + } + + searchResults = append(resSam, resMail...) + return unique(searchResults), nil + } + + // startswith(on_premises_sam_account_name,'mar') or startswith(display_name,'mar') or startswith(mail,'mar') + var searchQuery = regexp.MustCompile(`^startswith\(on_premises_sam_account_name,'(.*)'\) or startswith\(display_name,'(.*)'\) or startswith\(mail,'(.*)'\)$`) + match = searchQuery.FindStringSubmatch(query) + if len(match) == 4 { + resSam, err := s.index.FindByPartial(&proto.Account{}, "OnPremisesSamAccountName", match[1]+"*") + if err != nil { + return nil, err + } + resDisp, err := s.index.FindByPartial(&proto.Account{}, "DisplayName", match[2]+"*") + if err != nil { + return nil, err + } + resMail, err := s.index.FindByPartial(&proto.Account{}, "Mail", match[3]+"*") + if err != nil { + return nil, err + } + + searchResults = append(resSam, append(resDisp, resMail...)...) + return unique(searchResults), nil + } + + // id eq 'marie' or on_premises_sam_account_name eq 'marie' + // id eq 'f7fbf8c8-139b-4376-b307-cf0a8c2d0d9c' or on_premises_sam_account_name eq 'f7fbf8c8-139b-4376-b307-cf0a8c2d0d9c' + var idOrQuery = regexp.MustCompile(`^id eq '(.*)' or on_premises_sam_account_name eq '(.*)'$`) + match = idOrQuery.FindStringSubmatch(query) + if len(match) == 3 { + qID, qSam := match[1], match[2] + tmp := &proto.Account{} + err = s.repo.LoadAccount(ctx, qID, tmp) + if err != nil { + return nil, err + } + searchResults, err = s.index.FindBy(&proto.Account{}, "OnPremisesSamAccountName", qSam) + if err != nil { + return nil, err + } + + if tmp.Id != "" { + searchResults = append(searchResults, tmp.Id) + } + + return unique(searchResults), nil + } + + var onPremQuery = regexp.MustCompile(`^on_premises_sam_account_name eq '(.*)'$`) // TODO how is ' escaped in the password? + match = onPremQuery.FindStringSubmatch(query) + if len(match) == 2 { + return s.index.FindBy(&proto.Account{}, "OnPremisesSamAccountName", match[1]) + } + + var mailQuery = regexp.MustCompile(`^mail eq '(.*)'$`) + match = mailQuery.FindStringSubmatch(query) + if len(match) == 2 { + return s.index.FindBy(&proto.Account{}, "Mail", match[1]) + } + + return nil, merrors.BadRequest(s.id, "unsupported query %s", query) +} + // GetAccount implements the AccountsServiceHandler interface func (s Service) GetAccount(ctx context.Context, in *proto.GetAccountRequest, out *proto.Account) (err error) { if !s.hasAccountManagementPermissions(ctx) { @@ -277,35 +304,43 @@ func (s Service) CreateAccount(ctx context.Context, in *proto.CreateAccountReque accLock.Lock() defer accLock.Unlock() var id string - var acc = in.Account - if acc == nil { - return merrors.BadRequest(s.id, "account missing") - } - if acc.Id == "" { - acc.Id = uuid.Must(uuid.NewV4()).String() - } - if !s.isValidUsername(acc.PreferredName) { - return merrors.BadRequest(s.id, "preferred_name '%s' must be at least the local part of an email", acc.PreferredName) - } - if !s.isValidEmail(acc.Mail) { - return merrors.BadRequest(s.id, "mail '%s' must be a valid email", acc.Mail) + + if in.Account == nil { + return merrors.InternalServerError(s.id, "invalid account: empty") } - if id, err = cleanupID(acc.Id); err != nil { + out.XXX_Merge(in.Account) + + if out.Id == "" { + out.Id = uuid.Must(uuid.NewV4()).String() + } + if err = validateAccount(s.id, out); err != nil { + return err + } + + if id, err = cleanupID(out.Id); err != nil { return merrors.InternalServerError(s.id, "could not clean up account id: %v", err.Error()) } - if acc.PasswordProfile != nil { - if acc.PasswordProfile.Password != "" { + exists, err := s.accountExists(ctx, out.PreferredName, out.Mail, out.Id) + if err != nil { + return merrors.InternalServerError(s.id, "could not check if account exists: %v", err.Error()) + } + if exists { + return merrors.BadRequest(s.id, "account already exists") + } + + if out.PasswordProfile != nil { + if out.PasswordProfile.Password != "" { // encrypt password c := crypt.New(crypt.SHA512) - if acc.PasswordProfile.Password, err = c.Generate([]byte(acc.PasswordProfile.Password), nil); err != nil { + if out.PasswordProfile.Password, err = c.Generate([]byte(out.PasswordProfile.Password), nil); err != nil { s.log.Error().Err(err).Str("id", id).Msg("could not hash password") return merrors.InternalServerError(s.id, "could not hash password: %v", err.Error()) } } - if err := passwordPoliciesValid(acc.PasswordProfile.PasswordPolicies); err != nil { + if err := passwordPoliciesValid(out.PasswordProfile.PasswordPolicies); err != nil { return merrors.BadRequest(s.id, "%s", err) } } @@ -314,27 +349,54 @@ func (s Service) CreateAccount(ctx context.Context, in *proto.CreateAccountReque // TODO groups should be ignored during create, use groups.AddMember? return error? // write and index account - note: don't do anything else in between! - if err = s.repo.WriteAccount(ctx, acc); err != nil { + if err = s.repo.WriteAccount(ctx, out); err != nil { s.log.Error().Err(err).Str("id", id).Msg("could not persist new account") - s.debugLogAccount(acc).Msg("could not persist new account") + s.debugLogAccount(out).Msg("could not persist new account") return merrors.InternalServerError(s.id, "could not persist new account: %v", err.Error()) } - if err = s.indexAccount(acc.Id); err != nil { - return merrors.InternalServerError(s.id, "could not index new account: %v", err.Error()) - } - s.log.Debug().Interface("account", acc).Msg("account after indexing") + indexResults, err := s.index.Add(out) + if err != nil { + s.rollbackCreateAccount(ctx, out) + return merrors.BadRequest(s.id, "Account already exists %v", err.Error()) - if acc.PasswordProfile != nil { - acc.PasswordProfile.Password = "" + } + s.log.Debug().Interface("account", out).Msg("account after indexing") + + for _, r := range indexResults { + if r.Field == "UidNumber" { + id, err := strconv.Atoi(path.Base(r.Value)) + if err != nil { + s.rollbackCreateAccount(ctx, out) + return err + } + out.UidNumber = int64(id) + break + } } - { - out.Id = acc.Id - out.Mail = acc.Mail - out.PreferredName = acc.PreferredName - out.AccountEnabled = acc.AccountEnabled - out.DisplayName = acc.DisplayName - out.OnPremisesSamAccountName = acc.OnPremisesSamAccountName + if out.GidNumber == 0 { + out.GidNumber = userDefaultGID + } + + r := proto.ListGroupsResponse{} + err = s.ListGroups(ctx, &proto.ListGroupsRequest{}, &r) + if err != nil { + // rollback account creation + return err + } + + for _, group := range r.Groups { + if group.GidNumber == out.GidNumber { + out.MemberOf = append(out.MemberOf, group) + } + } + //acc.MemberOf = append(acc.MemberOf, &group) + if err := s.repo.WriteAccount(context.Background(), out); err != nil { + return err + } + + if out.PasswordProfile != nil { + out.PasswordProfile.Password = "" } // TODO: assign user role to all new users for now, as create Account request does not have any role field @@ -342,7 +404,7 @@ func (s Service) CreateAccount(ctx context.Context, in *proto.CreateAccountReque return merrors.InternalServerError(s.id, "could not assign role to account: roleService not configured") } if _, err = s.RoleService.AssignRoleToUser(ctx, &settings.AssignRoleToUserRequest{ - AccountUuid: acc.Id, + AccountUuid: out.Id, RoleId: settings_svc.BundleUUIDRoleUser, }); err != nil { return merrors.InternalServerError(s.id, "could not assign role to account: %v", err.Error()) @@ -351,6 +413,18 @@ func (s Service) CreateAccount(ctx context.Context, in *proto.CreateAccountReque return } +// rollbackCreateAccount tries to rollback changes made by `CreateAccount` if parts of it failed. +func (s Service) rollbackCreateAccount(ctx context.Context, acc *proto.Account) { + err := s.index.Delete(acc) + if err != nil { + s.log.Err(err).Msg("failed to rollback account from indices") + } + err = s.repo.DeleteAccount(ctx, acc.Id) + if err != nil { + s.log.Err(err).Msg("failed to rollback account from repo") + } +} + // UpdateAccount implements the AccountsServiceHandler interface // read only fields are ignored // TODO how can we unset specific values? using the update mask @@ -373,8 +447,6 @@ func (s Service) UpdateAccount(ctx context.Context, in *proto.UpdateAccountReque return merrors.InternalServerError(s.id, "could not clean up account id: %v", err.Error()) } - path := filepath.Join(s.Config.Server.AccountsDataPath, "accounts", id) - if err = s.repo.LoadAccount(ctx, id, out); err != nil { if storage.IsNotFoundErr(err) { return merrors.NotFound(s.id, "account not found: %v", err.Error()) @@ -396,6 +468,24 @@ func (s Service) UpdateAccount(ctx context.Context, in *proto.UpdateAccountReque return merrors.BadRequest(s.id, "%s", err) } + if _, exists := validMask.Filter("PreferredName"); exists { + if err = validateAccountPreferredName(s.id, in.Account); err != nil { + return err + } + } + if _, exists := validMask.Filter("OnPremisesSamAccountName"); exists { + if err = validateAccountOnPremisesSamAccountName(s.id, in.Account); err != nil { + return err + } + } + if _, exists := validMask.Filter("Mail"); exists { + if in.Account.Mail != "" { + if err = validateAccountEmail(s.id, in.Account); err != nil { + return err + } + } + } + if err := fieldmask_utils.StructToStruct(validMask, in.Account, out); err != nil { return merrors.InternalServerError(s.id, "%s", err) } @@ -434,13 +524,20 @@ func (s Service) UpdateAccount(ctx context.Context, in *proto.UpdateAccountReque out.ExternalUserStateChangeDateTime = tsnow } + // We need to reload the old account state to be able to compute the update + old := &proto.Account{} + if err = s.repo.LoadAccount(ctx, id, old); err != nil { + s.log.Error().Err(err).Str("id", out.Id).Msg("could not load old account representation during update, maybe the account got deleted meanwhile?") + return merrors.InternalServerError(s.id, "could not load current account for update: %v", err.Error()) + } + if err = s.repo.WriteAccount(ctx, out); err != nil { s.log.Error().Err(err).Str("id", out.Id).Msg("could not persist updated account") return merrors.InternalServerError(s.id, "could not persist updated account: %v", err.Error()) } - if err = s.indexAccount(id); err != nil { - s.log.Error().Err(err).Str("id", id).Str("path", path).Msg("could not index new account") + if err = s.index.Update(old, out); err != nil { + s.log.Error().Err(err).Str("id", id).Msg("could not index new account") return merrors.InternalServerError(s.id, "could not index updated account: %v", err.Error()) } @@ -514,7 +611,7 @@ func (s Service) DeleteAccount(ctx context.Context, in *proto.DeleteAccountReque return merrors.InternalServerError(s.id, "could not remove account: %v", err.Error()) } - if err = s.index.Delete(id); err != nil { + if err = s.index.Delete(a); err != nil { s.log.Error().Err(err).Str("id", id).Str("accountId", id).Msg("could not remove account from index") return merrors.InternalServerError(s.id, "could not remove account from index: %v", err.Error()) } @@ -523,12 +620,46 @@ func (s Service) DeleteAccount(ctx context.Context, in *proto.DeleteAccountReque return } +func validateAccount(serviceID string, a *proto.Account) error { + if err := validateAccountPreferredName(serviceID, a); err != nil { + return err + } + if err := validateAccountOnPremisesSamAccountName(serviceID, a); err != nil { + return err + } + if err := validateAccountEmail(serviceID, a); err != nil { + return err + } + return nil +} + +func validateAccountPreferredName(serviceID string, a *proto.Account) error { + if !isValidUsername(a.PreferredName) { + return merrors.BadRequest(serviceID, "preferred_name '%s' must be at least the local part of an email", a.PreferredName) + } + return nil +} + +func validateAccountOnPremisesSamAccountName(serviceID string, a *proto.Account) error { + if !isValidUsername(a.OnPremisesSamAccountName) { + return merrors.BadRequest(serviceID, "on_premises_sam_account_name '%s' must be at least the local part of an email", a.OnPremisesSamAccountName) + } + return nil +} + +func validateAccountEmail(serviceID string, a *proto.Account) error { + if !isValidEmail(a.Mail) { + return merrors.BadRequest(serviceID, "mail '%s' must be a valid email", a.Mail) + } + return nil +} + // We want to allow email addresses as usernames so they show up when using them in ACLs on storages that allow intergration with our glauth LDAP service // so we are adding a few restrictions from https://stackoverflow.com/questions/6949667/what-are-the-real-rules-for-linux-usernames-on-centos-6-and-rhel-6 // names should not start with numbers var usernameRegex = regexp.MustCompile("^[a-zA-Z_][a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]*(@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*)*$") -func (s Service) isValidUsername(e string) bool { +func isValidUsername(e string) bool { if len(e) < 1 && len(e) > 254 { return false } @@ -538,7 +669,7 @@ func (s Service) isValidUsername(e string) bool { // regex from https://www.w3.org/TR/2016/REC-html51-20161101/sec-forms.html#valid-e-mail-address var emailRegex = regexp.MustCompile("^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$") -func (s Service) isValidEmail(e string) bool { +func isValidEmail(e string) bool { if len(e) < 3 && len(e) > 254 { return false } @@ -613,3 +744,68 @@ func (s Service) debugLogAccount(a *proto.Account) *zerolog.Event { "DeletedDateTime": a.DeletedDateTime, }) } + +func unique(strSlice []string) []string { + keys := make(map[string]bool) + list := []string{} + for _, entry := range strSlice { + if _, value := keys[entry]; !value { + keys[entry] = true + list = append(list, entry) + } + } + return list +} + +func (s Service) accountExists(ctx context.Context, username, mail, id string) (exists bool, err error) { + var ids []string + ids, err = s.index.FindBy(&proto.Account{}, "preferred_name", username) + if err != nil { + return false, err + } + if len(ids) > 0 { + return true, nil + } + + ids, err = s.index.FindBy(&proto.Account{}, "on_premises_sam_account_name", username) + if err != nil { + return false, err + } + if len(ids) > 0 { + return true, nil + } + + ids, err = s.index.FindBy(&proto.Account{}, "mail", mail) + if err != nil { + return false, err + } + if len(ids) > 0 { + return true, nil + } + + a := &proto.Account{} + err = s.repo.LoadAccount(ctx, id, a) + if err == nil { + return true, nil + } + if !storage.IsNotFoundErr(err) { + return true, err + } + return false, nil +} + +func getAuthQueryMatch(query string) (match []string, authRequest bool) { + match = authQuery.FindStringSubmatch(query) + return match, len(match) == 3 +} + +func isPasswordValid(logger log.Logger, hash string, pwd string) (ok bool) { + defer func() { + if r := recover(); r != nil { + logger.Error().Err(fmt.Errorf("%s", r)).Str("hash", hash).Msg("password lib panicked") + } + }() + + c := crypt.NewFromHash(hash) + return c.Verify(hash, []byte(pwd)) == nil +} diff --git a/accounts/pkg/service/v0/groups.go b/accounts/pkg/service/v0/groups.go index d6d8302d0..c65f04b8b 100644 --- a/accounts/pkg/service/v0/groups.go +++ b/accounts/pkg/service/v0/groups.go @@ -2,34 +2,18 @@ package service import ( "context" - "github.com/owncloud/ocis/accounts/pkg/storage" + "path" "path/filepath" + "strconv" + + "github.com/owncloud/ocis/accounts/pkg/storage" - "github.com/CiscoM31/godata" - "github.com/blevesearch/bleve" "github.com/gofrs/uuid" "github.com/golang/protobuf/ptypes/empty" merrors "github.com/micro/go-micro/v2/errors" "github.com/owncloud/ocis/accounts/pkg/proto/v0" - "github.com/owncloud/ocis/accounts/pkg/provider" ) -func (s Service) indexGroup(id string) error { - g := &proto.BleveGroup{ - BleveType: "group", - } - if err := s.repo.LoadGroup(context.Background(), id, &g.Group); err != nil { - s.log.Error().Err(err).Str("group", id).Msg("could not load group") - return err - } - s.log.Debug().Interface("group", g).Msg("found group") - if err := s.index.Index(g.Id, g); err != nil { - s.log.Error().Err(err).Interface("group", g).Msg("could not index group") - return err - } - return nil -} - func (s Service) expandMembers(g *proto.Group) { if g == nil { return @@ -41,7 +25,7 @@ func (s Service) expandMembers(g *proto.Group) { if err := s.repo.LoadAccount(context.Background(), g.Members[i].Id, a); err == nil { expanded = append(expanded, a) } else { - // log errors but continue execution for now + // log errors but con/var/tmp/ocis-accounts-store-408341811tinue execution for now s.log.Error().Err(err).Str("id", g.Members[i].Id).Msg("could not load account") } } @@ -67,49 +51,25 @@ func (s Service) deflateMembers(g *proto.Group) { // ListGroups implements the GroupsServiceHandler interface func (s Service) ListGroups(c context.Context, in *proto.ListGroupsRequest, out *proto.ListGroupsResponse) (err error) { - - // only search for groups - tq := bleve.NewTermQuery("group") - tq.SetField("bleve_type") - - query := bleve.NewConjunctionQuery(tq) - - if in.Query != "" { - // parse the query like an odata filter - var q *godata.GoDataFilterQuery - if q, err = godata.ParseFilterString(in.Query); err != nil { - s.log.Error().Err(err).Msg("could not parse query") - return merrors.InternalServerError(s.id, "could not parse query: %v", err.Error()) - } - - // convert to bleve query - bq, err := provider.BuildBleveQuery(q) - if err != nil { - s.log.Error().Err(err).Msg("could not build bleve query") - return merrors.InternalServerError(s.id, "could not build bleve query: %v", err.Error()) - } - query.AddQuery(bq) - } - - s.log.Debug().Interface("query", query).Msg("using query") - - searchRequest := bleve.NewSearchRequest(query) - var searchResult *bleve.SearchResult - searchResult, err = s.index.Search(searchRequest) - if err != nil { - s.log.Error().Err(err).Msg("could not execute bleve search") - return merrors.InternalServerError(s.id, "could not execute bleve search: %v", err.Error()) - } - - s.log.Debug().Interface("result", searchResult).Msg("result") + var searchResults []string out.Groups = make([]*proto.Group, 0) + if in.Query == "" { + searchResults, _ = s.index.FindByPartial(&proto.Group{}, "DisplayName", "*") + } - for _, hit := range searchResult.Hits { + /* + var startsWithIDQuery = regexp.MustCompile(`^startswith\(id,'(.*)'\)$`) + match := startsWithIDQuery.FindStringSubmatch(in.Query) + if len(match) == 2 { + searchResults = []string{match[1]} + } + */ + for _, hit := range searchResults { g := &proto.Group{} - if err = s.repo.LoadGroup(c, hit.ID, g); err != nil { - s.log.Error().Err(err).Str("group", hit.ID).Msg("could not load group, skipping") + if err = s.repo.LoadGroup(c, hit, g); err != nil { + s.log.Error().Err(err).Str("group", hit).Msg("could not load group, skipping") continue } s.log.Debug().Interface("group", g).Msg("found group") @@ -150,33 +110,59 @@ func (s Service) GetGroup(c context.Context, in *proto.GetGroupRequest, out *pro // CreateGroup implements the GroupsServiceHandler interface func (s Service) CreateGroup(c context.Context, in *proto.CreateGroupRequest, out *proto.Group) (err error) { - var id string if in.Group == nil { - return merrors.BadRequest(s.id, "account missing") + return merrors.InternalServerError(s.id, "invalid group: empty") } - if in.Group.Id == "" { - in.Group.Id = uuid.Must(uuid.NewV4()).String() + out.XXX_Merge(in.Group) + + if out.Id == "" { + out.Id = uuid.Must(uuid.NewV4()).String() } - if id, err = cleanupID(in.Group.Id); err != nil { + if _, err = cleanupID(out.Id); err != nil { return merrors.InternalServerError(s.id, "could not clean up account id: %v", err.Error()) } - // extract member id - s.deflateMembers(in.Group) + s.deflateMembers(out) - if err = s.repo.WriteGroup(c, in.Group); err != nil { - s.log.Error().Err(err).Interface("group", in.Group).Msg("could not persist new group") + if err = s.repo.WriteGroup(c, out); err != nil { + s.log.Error().Err(err).Interface("group", out).Msg("could not persist new group") return merrors.InternalServerError(s.id, "could not persist new group: %v", err.Error()) } - if err = s.indexGroup(id); err != nil { + indexResults, err := s.index.Add(out) + if err != nil { + s.rollbackCreateGroup(c, out) return merrors.InternalServerError(s.id, "could not index new group: %v", err.Error()) } + for _, r := range indexResults { + if r.Field == "GidNumber" { + gid, err := strconv.Atoi(path.Base(r.Value)) + if err != nil { + s.rollbackCreateGroup(c, out) + return err + } + out.GidNumber = int64(gid) + return s.repo.WriteGroup(context.Background(), out) + } + } + return } +// rollbackCreateGroup tries to rollback changes made by `CreateGroup` if parts of it failed. +func (s Service) rollbackCreateGroup(ctx context.Context, group *proto.Group) { + err := s.index.Delete(group) + if err != nil { + s.log.Err(err).Msg("failed to rollback group from indices") + } + err = s.repo.DeleteGroup(ctx, group.Id) + if err != nil { + s.log.Err(err).Msg("failed to rollback group from repo") + } +} + // UpdateGroup implements the GroupsServiceHandler interface func (s Service) UpdateGroup(c context.Context, in *proto.UpdateGroupRequest, out *proto.Group) (err error) { return merrors.InternalServerError(s.id, "not implemented") @@ -217,7 +203,7 @@ func (s Service) DeleteGroup(c context.Context, in *proto.DeleteGroupRequest, ou return merrors.InternalServerError(s.id, "could not load group: %v", err.Error()) } - if err = s.index.Delete(id); err != nil { + if err = s.index.Delete(g); err != nil { s.log.Error().Err(err).Str("id", id).Str("path", path).Msg("could not remove group from index") return merrors.InternalServerError(s.id, "could not remove group from index: %v", err.Error()) } diff --git a/accounts/pkg/service/v0/service.go b/accounts/pkg/service/v0/service.go index 772210253..99152690e 100644 --- a/accounts/pkg/service/v0/service.go +++ b/accounts/pkg/service/v0/service.go @@ -3,21 +3,20 @@ package service import ( "context" "errors" - "github.com/owncloud/ocis/accounts/pkg/storage" - "os" + "path" "path/filepath" + "strconv" "strings" "time" - "github.com/blevesearch/bleve" - "github.com/blevesearch/bleve/analysis/analyzer/custom" - "github.com/blevesearch/bleve/analysis/analyzer/keyword" - "github.com/blevesearch/bleve/analysis/analyzer/simple" - "github.com/blevesearch/bleve/analysis/analyzer/standard" - "github.com/blevesearch/bleve/analysis/token/lowercase" - "github.com/blevesearch/bleve/analysis/tokenizer/unicode" + "github.com/owncloud/ocis/accounts/pkg/indexer/option" + + "github.com/owncloud/ocis/accounts/pkg/indexer" + "github.com/owncloud/ocis/accounts/pkg/storage" + mclient "github.com/micro/go-micro/v2/client" "github.com/owncloud/ocis/accounts/pkg/config" + idxerrs "github.com/owncloud/ocis/accounts/pkg/indexer/errors" "github.com/owncloud/ocis/accounts/pkg/proto/v0" "github.com/owncloud/ocis/ocis-pkg/log" "github.com/owncloud/ocis/ocis-pkg/roles" @@ -25,6 +24,9 @@ import ( settings_svc "github.com/owncloud/ocis/settings/pkg/service/v0" ) +// userDefaultGID is the default integer representing the "users" group. +const userDefaultGID = 30000 + // New returns a new instance of Service func New(opts ...Option) (s *Service, err error) { options := newOptions(opts...) @@ -72,92 +74,50 @@ func New(opts ...Option) (s *Service, err error) { return } -func (s Service) buildIndex() (index bleve.Index, err error) { - indexDir := filepath.Join(s.Config.Server.AccountsDataPath, "index.bleve") - if index, err = bleve.Open(indexDir); err != nil { - if err != bleve.ErrorIndexPathDoesNotExist { - s.log.Error().Err(err).Msg("failed to read index") - return - } +func (s Service) buildIndex() (*indexer.Indexer, error) { + idx := indexer.CreateIndexer(s.Config) - indexMapping := bleve.NewIndexMapping() - // keep all symbols in terms to allow exact maching, eg. emails - indexMapping.DefaultAnalyzer = keyword.Name - // TODO don't bother to store fields as we will load the account from disk - - // Reusable mapping for text - standardTextFieldMapping := bleve.NewTextFieldMapping() - standardTextFieldMapping.Analyzer = standard.Name - standardTextFieldMapping.Store = false - - // Reusable mapping for text, uses english stop word removal - simpleTextFieldMapping := bleve.NewTextFieldMapping() - simpleTextFieldMapping.Analyzer = simple.Name - simpleTextFieldMapping.Store = false - - // Reusable mapping for keyword text - keywordFieldMapping := bleve.NewTextFieldMapping() - keywordFieldMapping.Analyzer = keyword.Name - keywordFieldMapping.Store = false - - // Reusable mapping for lowercase text - err = indexMapping.AddCustomAnalyzer("lowercase", - map[string]interface{}{ - "type": custom.Name, - "tokenizer": unicode.Name, - "token_filters": []string{ - lowercase.Name, - }, - }) - if err != nil { - return - } - lowercaseTextFieldMapping := bleve.NewTextFieldMapping() - lowercaseTextFieldMapping.Analyzer = "lowercase" - lowercaseTextFieldMapping.Store = true - - // accounts - accountMapping := bleve.NewDocumentMapping() - indexMapping.AddDocumentMapping("account", accountMapping) - - // Text - accountMapping.AddFieldMappingsAt("display_name", standardTextFieldMapping) - accountMapping.AddFieldMappingsAt("description", standardTextFieldMapping) - - // Lowercase - accountMapping.AddFieldMappingsAt("on_premises_sam_account_name", lowercaseTextFieldMapping) - accountMapping.AddFieldMappingsAt("preferred_name", lowercaseTextFieldMapping) - - // Keywords - accountMapping.AddFieldMappingsAt("mail", keywordFieldMapping) - - // groups - groupMapping := bleve.NewDocumentMapping() - indexMapping.AddDocumentMapping("group", groupMapping) - - // Text - groupMapping.AddFieldMappingsAt("display_name", standardTextFieldMapping) - groupMapping.AddFieldMappingsAt("description", standardTextFieldMapping) - - // Lowercase - groupMapping.AddFieldMappingsAt("on_premises_sam_account_name", lowercaseTextFieldMapping) - - // Tell blevesearch how to determine the type of the structs that are indexed. - // The referenced field needs to match the struct field exactly and it must be public. - // See pkg/proto/v0/bleve.go how we wrap the generated Account and Group to add a - // BleveType property which is indexed as `bleve_type` so we can also distinguish the - // documents in the index by querying for that property. - indexMapping.TypeField = "BleveType" - - // for now recreate index on every start - if err = os.RemoveAll(indexDir); err != nil { - return - } - if index, err = bleve.New(indexDir, indexMapping); err != nil { - return nil, err - } + // Accounts + if err := idx.AddIndex(&proto.Account{}, "DisplayName", "Id", "accounts", "non_unique", nil, true); err != nil { + return nil, err } - return + if err := idx.AddIndex(&proto.Account{}, "Mail", "Id", "accounts", "unique", nil, true); err != nil { + return nil, err + } + + if err := idx.AddIndex(&proto.Account{}, "OnPremisesSamAccountName", "Id", "accounts", "unique", nil, true); err != nil { + return nil, err + } + + if err := idx.AddIndex(&proto.Account{}, "PreferredName", "Id", "accounts", "unique", nil, true); err != nil { + return nil, err + } + + if err := idx.AddIndex(&proto.Account{}, "UidNumber", "Id", "accounts", "autoincrement", &option.Bound{ + Lower: s.Config.Index.UID.Lower, + Upper: s.Config.Index.UID.Upper, + }, false); err != nil { + return nil, err + } + + // Groups + if err := idx.AddIndex(&proto.Group{}, "OnPremisesSamAccountName", "Id", "groups", "unique", nil, true); err != nil { + return nil, err + } + + if err := idx.AddIndex(&proto.Group{}, "DisplayName", "Id", "groups", "non_unique", nil, true); err != nil { + return nil, err + } + + if err := idx.AddIndex(&proto.Group{}, "GidNumber", "Id", "groups", "autoincrement", &option.Bound{ + Lower: s.Config.Index.GID.Lower, + Upper: s.Config.Index.GID.Upper, + }, false); err != nil { + return nil, err + } + + return idx, nil + } func (s Service) createDefaultAccounts() (err error) { @@ -275,8 +235,35 @@ func (s Service) createDefaultAccounts() (err error) { return err } - if err := s.indexAccount(accounts[i].Id); err != nil { - return err + results, err := s.index.Add(&accounts[i]) + if err != nil { + if idxerrs.IsAlreadyExistsErr(err) { + continue + } else { + return err + } + } + + // TODO: can be removed again as soon as we respect the predefined UIDs and GIDs from the account. Then no autoincrement is happening, therefore we don't need to update accounts. + changed := false + for _, r := range results { + if r.Field == "UidNumber" || r.Field == "GidNumber" { + id, err := strconv.ParseInt(path.Base(r.Value), 10, 0) + if err != nil { + return err + } + if r.Field == "UidNumber" { + accounts[i].UidNumber = id + } else { + accounts[i].GidNumber = id + } + changed = true + } + } + if changed { + if err := s.repo.WriteAccount(context.Background(), &accounts[i]); err != nil { + return err + } } } @@ -337,8 +324,28 @@ func (s Service) createDefaultGroups() (err error) { return err } - if err := s.indexGroup(groups[i].Id); err != nil { - return err + results, err := s.index.Add(&groups[i]) + if err != nil { + if idxerrs.IsAlreadyExistsErr(err) { + continue + } else { + return err + } + } + + // TODO: can be removed again as soon as we respect the predefined GIDs from the group. Then no autoincrement is happening, therefore we don't need to update groups. + for _, r := range results { + if r.Field == "GidNumber" { + gid, err := strconv.ParseInt(path.Base(r.Value), 10, 0) + if err != nil { + return err + } + groups[i].GidNumber = gid + if err := s.repo.WriteGroup(context.Background(), &groups[i]); err != nil { + return err + } + break + } } } return nil @@ -374,7 +381,7 @@ type Service struct { id string log log.Logger Config *config.Config - index bleve.Index + index *indexer.Indexer RoleService settings.RoleService RoleManager *roles.Manager repo storage.Repo diff --git a/accounts/pkg/storage/cs3.go b/accounts/pkg/storage/cs3.go index 6a80972c9..930144c87 100644 --- a/accounts/pkg/storage/cs3.go +++ b/accounts/pkg/storage/cs3.go @@ -73,8 +73,14 @@ func (r CS3Repo) WriteAccount(ctx context.Context, a *proto.Account) (err error) return err } - _, err = r.dataProvider.put(r.accountURL(a.Id), bytes.NewReader(by), t) - return err + resp, err := r.dataProvider.put(r.accountURL(a.Id), bytes.NewReader(by), t) + if err != nil { + return err + } + if err = resp.Body.Close(); err != nil { + return err + } + return nil } // LoadAccount loads an account via cs3 by id and writes it to the provided account @@ -147,8 +153,14 @@ func (r CS3Repo) WriteGroup(ctx context.Context, g *proto.Group) (err error) { return err } - _, err = r.dataProvider.put(r.groupURL(g.Id), bytes.NewReader(by), t) - return err + resp, err := r.dataProvider.put(r.groupURL(g.Id), bytes.NewReader(by), t) + if err != nil { + return err + } + if err = resp.Body.Close(); err != nil { + return err + } + return nil } // LoadGroup loads a group via cs3 by id and writes it to the provided group diff --git a/accounts/pkg/storage/disk.go b/accounts/pkg/storage/disk.go index 3490424a6..fddfb6a0e 100644 --- a/accounts/pkg/storage/disk.go +++ b/accounts/pkg/storage/disk.go @@ -127,7 +127,7 @@ func (r DiskRepo) DeleteGroup(ctx context.Context, id string) (err error) { } } - return nil + return //r.log.Error().Err(err).Str("id", id).Str("path", path).Msg("could not remove group") //return merrors.InternalServerError(r.serviceID, "could not remove group: %v", err.Error()) diff --git a/accounts/ui/tests/config/drone/identifier-registration.yml b/accounts/ui/tests/config/drone/identifier-registration.yml deleted file mode 100644 index a45a83f78..000000000 --- a/accounts/ui/tests/config/drone/identifier-registration.yml +++ /dev/null @@ -1,16 +0,0 @@ ---- - -# OpenID Connect client registry. -clients: - - id: phoenix - name: OCIS - application_type: web - insecure: yes - trusted: yes - redirect_uris: - - https://ocis-server:9200/oidc-callback.html - - https://ocis-server:9200/ - origins: - - https://ocis-server:9200 - -authorities: diff --git a/accounts/ui/tests/config/drone/ocis-config.json b/accounts/ui/tests/config/drone/ocis-config.json deleted file mode 100644 index b68b4a087..000000000 --- a/accounts/ui/tests/config/drone/ocis-config.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "server": "https://ocis-server:9200", - "theme": "owncloud", - "version": "0.1.0", - "openIdConnect": { - "metadata_url": "https://ocis-server:9200/.well-known/openid-configuration", - "authority": "https://ocis-server:9200", - "client_id": "phoenix", - "response_type": "code", - "scope": "openid profile email" - }, - "apps": [ - "files", - "draw-io", - "markdown-editor", - "media-viewer" - ], - "external_apps": [ - { - "id": "accounts", - "path": "https://ocis-server:9200/accounts.js", - "config": { - "url": "https://ocis-server:9200" - } - } - ] -} diff --git a/changelog/unreleased/accounts-fs-based-index.md b/changelog/unreleased/accounts-fs-based-index.md new file mode 100644 index 000000000..c6ba4e15d --- /dev/null +++ b/changelog/unreleased/accounts-fs-based-index.md @@ -0,0 +1,15 @@ +Change: Filesystem based index + +Tags: accounts, storage + +We replaced `bleve` with a new filesystem based index implementation. There is an `indexer` which is capable of +orchestrating different index types to build indices on documents by field. You can choose from the index types `unique`, +`non-unique` or `autoincrement`. Indices can be utilized to run search queries (full matches or globbing) on document +fields. The accounts service is using this index internally to run the search queries coming in via `ListAccounts` and +`ListGroups` and to generate UIDs for new accounts as well as GIDs for new groups. + +The accounts service can be configured to store the index on the local FS / a NFS (`disk` implementation of the index) +or to use an arbitrary storage ( `cs3` implementation of the index). `cs3` is the new default, which is configured to +use the `metadata` storage. + +https://github.com/owncloud/ocis/pull/709 diff --git a/changelog/unreleased/ocs-map-userid-to-username.md b/changelog/unreleased/ocs-map-userid-to-username.md new file mode 100644 index 000000000..4b3aa07a2 --- /dev/null +++ b/changelog/unreleased/ocs-map-userid-to-username.md @@ -0,0 +1,9 @@ +Change: Remove username field in OCS + +Tags: ocs + +We use the incoming userid as both the `id` and the `on_premises_sam_account_name` for new accounts in the accounts +service. The userid in OCS requests is in fact the username, not our internal account id. We need to enforce the userid +as our internal account id though, because the account id is part of various `path` formats. + +https://github.com/owncloud/ocis/pull/709 diff --git a/changelog/unreleased/prevent-duplicate-accounts.md b/changelog/unreleased/prevent-duplicate-accounts.md new file mode 100644 index 000000000..1bc9fb448 --- /dev/null +++ b/changelog/unreleased/prevent-duplicate-accounts.md @@ -0,0 +1,7 @@ +Bugfix: Don't create account if id/mail/username already taken + +Tags: accounts + +We don't allow anymore to create a new account if the provided id/mail/username is already taken. + +https://github.com/owncloud/ocis/pull/709 diff --git a/docs/extensions/ocis-phoenix/getting-started.md b/docs/extensions/ocis-phoenix/getting-started.md index 31eb7bab3..95d57baa9 100644 --- a/docs/extensions/ocis-phoenix/getting-started.md +++ b/docs/extensions/ocis-phoenix/getting-started.md @@ -190,7 +190,7 @@ If you prefer to configure the service with commandline flags you can see the av : Version, defaults to `0.1.0` --web-config-app -: Provide multiple apps, defaults to `""`. Usage: `phoenix --web-config-app files --web-config-app pdf-viewer` +: Provide multiple apps, defaults to `""`. Usage: `phoenix --web-config-app files --web-config-app media-viewer` --oidc-metadata-url : OpenID Connect metadata URL, defaults to `http://localhost:9130/.well-known/openid-configuration` diff --git a/glauth/go.sum b/glauth/go.sum index b9bfedc0b..41a4c9923 100644 --- a/glauth/go.sum +++ b/glauth/go.sum @@ -110,12 +110,14 @@ github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:l github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496 h1:zV3ejI06GQ59hwDQAvmK1qxOQGB3WuVTRoY0okPTAv0= github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg= +github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg= github.com/ascarter/requestid v0.0.0-20170313220838-5b76ab3d4aee h1:3T/l+vMotQ7cDSLWNAn2Vg1SAQ3mdyLgBWWBitSS3uU= github.com/ascarter/requestid v0.0.0-20170313220838-5b76ab3d4aee/go.mod h1:u7Wtt4WATGGgae9mURNGQQqxAudPKrxfsbSDSGOso+g= github.com/aws/aws-sdk-go v1.20.1/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.23.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.23.19/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.33.19/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= +github.com/aws/aws-sdk-go v1.34.12/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= github.com/aws/aws-xray-sdk-go v0.9.4/go.mod h1:XtMKdBQfpVut+tJEwI7+dJFRxxRdxHDyVNp2tHXRq04= github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f/go.mod h1:AuiFmCCPBSrqvVMvuqFuk0qogytodnVFVSN5CeJB8Gc= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= @@ -155,6 +157,7 @@ github.com/bmizerany/pat v0.0.0-20170815010413-6226ea591a40/go.mod h1:8rLXio+Wji github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc h1:biVzkmvwrH8WK8raXaxBx6fRVTlJILwEwQGL1I/ByEI= github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= github.com/bwmarrin/discordgo v0.20.2/go.mod h1:O9S4p+ofTFwB02em7jkpkV8M3R0/PUVOwN61zSZ0r4Q= +github.com/c-bata/go-prompt v0.2.3/go.mod h1:VzqtzE2ksDBcdln8G7mk2RX9QyGjH+OVqOCSiVIqS34= github.com/caddyserver/certmagic v0.10.6/go.mod h1:Y8jcUBctgk/IhpAzlHKfimZNyXCkfGgRTC0orl8gROQ= github.com/cenkalti/backoff v2.1.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/cenkalti/backoff/v4 v4.0.0/go.mod h1:eEew/i+1Q6OrCDZh3WiXYv3+nJwBASZ8Bog/87DQnVg= @@ -170,6 +173,7 @@ github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL github.com/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitfE= github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ= github.com/cheggaaa/pb v1.0.28/go.mod h1:pQciLPpbU0oxA0h+VJYYLxO+XeDQb5pZijXscXHm81s= +github.com/cheggaaa/pb v1.0.29/go.mod h1:W40334L7FMC5JKWldsTWbdGjLo0RxUKK73K+TuPxX30= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= @@ -220,10 +224,13 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/cs3org/cato v0.0.0-20200626150132-28a40e643719/go.mod h1:XJEZ3/EQuI3BXTp/6DUzFr850vlxq11I6satRtz0YQ4= +github.com/cs3org/cato v0.0.0-20200828125504-e418fc54dd5e/go.mod h1:XJEZ3/EQuI3BXTp/6DUzFr850vlxq11I6satRtz0YQ4= github.com/cs3org/go-cs3apis v0.0.0-20200730121022-c4f3d4f7ddfd h1:uMaudkC7znaiIKT9rxIhoRYzrhTg1Nc78X7XEqhmjSk= github.com/cs3org/go-cs3apis v0.0.0-20200730121022-c4f3d4f7ddfd/go.mod h1:UXha4TguuB52H14EMoSsCqDj7k8a/t7g4gVP+bgY5LY= +github.com/cs3org/go-cs3apis v0.0.0-20200810113633-b00aca449666/go.mod h1:UXha4TguuB52H14EMoSsCqDj7k8a/t7g4gVP+bgY5LY= github.com/cs3org/reva v1.1.0 h1:Gih6ECHvMMGSx523SFluFlDmNMuhYelXYShdWvjvW38= github.com/cs3org/reva v1.1.0/go.mod h1:fBzTrNuAKdQ62ybjpdu8nyhBin90/3/3s6DGQDCdBp4= +github.com/cs3org/reva v1.2.2-0.20200924071957-e6676516e61e/go.mod h1:DOV5SjpOBKN+aWfOHLdA4KiLQkpyC786PQaXEdRAZ0M= github.com/cznic/b v0.0.0-20181122101859-a26611c4d92d h1:SwD98825d6bdB+pEuTxWOXiSjBrHdOl/UVp75eI7JT8= github.com/cznic/b v0.0.0-20181122101859-a26611c4d92d/go.mod h1:URriBxXwVq5ijiJ12C7iIZqlA69nTlI+LgI6/pwftG8= github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548 h1:iwZdTE0PVqJCos1vaoKsclOGD3ADKpshg3SRtYBbwso= @@ -233,8 +240,12 @@ github.com/cznic/strutil v0.0.0-20181122101858-275e90344537/go.mod h1:AHHPPPXTw0 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgraph-io/ristretto v0.0.1/go.mod h1:T40EBc7CJke8TkpiYfGGKAeFjSaxuFXhuXRyumBd6RE= +github.com/dgraph-io/ristretto v0.0.2/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= +github.com/dgraph-io/ristretto v0.0.3/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8= github.com/dnaeon/go-vcr v0.0.0-20180814043457-aafff18a5cc2/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= @@ -267,6 +278,7 @@ github.com/facebookgo/stack v0.0.0-20160209184415-751773369052/go.mod h1:UbMTZqL github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4 h1:7HZCaLC5+BZpmbhCOZJ293Lz68O7PYrF2EzeiFMwCLk= github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4/go.mod h1:5tD+neXqOorC30/tWg0LCSkrqj/AR6gu8yY8/fpw1q0= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fatih/structs v1.0.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ= @@ -274,6 +286,7 @@ github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI github.com/forestgiant/sliceutil v0.0.0-20160425183142-94783f95db6c/go.mod h1:pFdJbAhRf7rh6YYMUdIQGyzne6zYL1tCUW8QV2B3UfY= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsouza/go-dockerclient v1.6.0/go.mod h1:YWwtNPuL4XTX1SKJQk86cWPmmqwx+4np9qfPbb+znGc= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= @@ -568,11 +581,13 @@ github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/google/uuid v1.1.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/gophercloud/gophercloud v0.3.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= github.com/gopherjs/gopherjs v0.0.0-20181004151105-1babbf986f6f/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99 h1:twflg0XRTjwKpxb/jFExr4HGq6on2dEOmnL6FV+fgPw= github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= @@ -597,6 +612,7 @@ github.com/grpc-ecosystem/go-grpc-middleware v1.1.0 h1:THDBEeQ9xZ8JEaCLyLQqXMMdR github.com/grpc-ecosystem/go-grpc-middleware v1.1.0/go.mod h1:f5nM7jw/oeRSadq3xCzHAvxcr8HZnzsqU6ILg/0NiiE= github.com/grpc-ecosystem/go-grpc-middleware v1.2.0 h1:0IKlLyQ3Hs9nDaiK5cSHAGmcQEIC8l2Ts1u6x5Dfrqg= github.com/grpc-ecosystem/go-grpc-middleware v1.2.0/go.mod h1:mJzapYve32yjrKlk9GbyCZHuPgZsrbyIbyKhSzOpg6s= +github.com/grpc-ecosystem/go-grpc-middleware v1.2.2/go.mod h1:EaizFBKfUKtMIF5iaDEhniwNedqGo9FuLFzppDr3uwI= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.8.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= @@ -699,6 +715,7 @@ github.com/konsorten/go-windows-terminal-sequences v0.0.0-20180402223658-b729f26 github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= @@ -751,11 +768,14 @@ github.com/marten-seemann/qtls v0.4.1 h1:YlT8QP3WCCvvok7MGEZkMldXbyqgr8oFg5/n8Gt github.com/marten-seemann/qtls v0.4.1/go.mod h1:pxVXcHHw1pNIt8Qo0pwSYQEoZ8yYOOPXTCZLQQunvRc= github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.7 h1:UvyT9uN+3r7yLEYSlJsbQGdsaB/a0DlgWP3pql6iwOc= github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y= @@ -769,6 +789,7 @@ github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW github.com/mattn/go-tty v0.0.0-20180219170247-931426f7535a/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE= github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw= github.com/mattn/goveralls v0.0.5/go.mod h1:Xg2LHi51faXLyKXwsndxiW6uxEEQT9+3sjGzzwU4xy0= +github.com/mattn/goveralls v0.0.6/go.mod h1:h8b4ow6FxSPMQHF6o2ve3qsclnffZjYTNEKmLesRwqw= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mennanov/fieldmask-utils v0.3.2 h1:AkHXYBEOoyvocl8YhzoStATRnto5OH1PY4Rj78I5Cuc= @@ -806,6 +827,7 @@ github.com/mitchellh/mapstructure v1.0.0/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.3.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.3.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.3.3 h1:SzB1nHZ2Xi+17FP0zVQBHIZqvwRN9408fJO8h+eeNA8= github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY= @@ -890,15 +912,19 @@ github.com/oracle/oci-go-sdk v7.0.0+incompatible/go.mod h1:VQb79nF8Z2cwLkLS35ukw github.com/ory/dockertest v3.3.5+incompatible/go.mod h1:1vX4m9wsvi00u5bseYwXaSnhNrne+V0E6LAcBILJdPs= github.com/ory/fosite v0.29.0/go.mod h1:0atSZmXO7CAcs6NPMI/Qtot8tmZYj04Nddoold4S2h0= github.com/ory/fosite v0.32.2/go.mod h1:UeBhRgW6nAjTcd8S7kAo0IFsY/rTPyOXPq/t8N20Q8I= +github.com/ory/fosite v0.33.0/go.mod h1:h+ize9gk0GvRyGjabriqSEmTkMhny+O95cijb8DVqPE= github.com/ory/go-acc v0.0.0-20181118080137-ddc355013f90/go.mod h1:sxnvPCxChFuSmTJGj8FdMupeq1BezCiEpDjTUXQ4hf4= github.com/ory/go-acc v0.2.1/go.mod h1:0omgy2aa3nDBJ45VAKeLHH8ccPBudxLeic4xiDRtug0= +github.com/ory/go-acc v0.2.5/go.mod h1:4Kb/UnPcT8qRAk3IAxta+hvVapdxTLWtrr7bFLlEgpw= github.com/ory/go-convenience v0.1.0/go.mod h1:uEY/a60PL5c12nYz4V5cHY03IBmwIAEm8TWB0yn9KNs= github.com/ory/gojsonreference v0.0.0-20190720135523-6b606c2d8ee8/go.mod h1:wsH1C4nIeeQClDtD5AH7kF1uTS6zWyqfjVDTmB0Em7A= github.com/ory/gojsonschema v1.1.1-0.20190919112458-f254ca73d5e9/go.mod h1:BNZpdJgB74KOLSsWFvzw6roXg1I6O51WO8roMmW+T7Y= github.com/ory/herodot v0.6.2/go.mod h1:3BOneqcyBsVybCPAJoi92KN2BpJHcmDqAMcAAaJiJow= github.com/ory/viper v1.5.6/go.mod h1:TYmpFpKLxjQwvT4f0QPpkOn4sDXU1kDgAwJpgLYiQ28= +github.com/ory/viper v1.7.5/go.mod h1:ypOuyJmEUb3oENywQZRgeAMwqgOyDqwboO1tj3DjTaM= github.com/ory/x v0.0.85/go.mod h1:s44V8t3xyjWZREcU+mWlp4h302rTuM4aLXcW+y5FbQ8= github.com/ovh/go-ovh v0.0.0-20181109152953-ba5adb4cf014/go.mod h1:joRatxRJaZBsY3JAOEMcoOp05CnZzsx4scTxi95DHyQ= +github.com/owncloud/flaex v0.0.0-20200411150708-dce59891a203/go.mod h1:jip86t4OVURJTf8CM/0e2qcji/Y4NG3l2lR8kex4JWw= github.com/owncloud/ocis-pkg/v2 v2.4.0/go.mod h1:FSzIvhx9HcZcq4jgNaDowNvM7PTX/XCyoMvyfzidUpE= github.com/owncloud/ocis-pkg/v2 v2.4.1-0.20200902134813-1e87c6173ada h1:iVknnuT/z8QCAeBpHEcbI/AiQ9FOBvF5aOAFT3TIM+I= github.com/owncloud/ocis-pkg/v2 v2.4.1-0.20200902134813-1e87c6173ada/go.mod h1:WdcVM54z0X7aQzS8eyGl7S5sjEMVBtLpfpzsPX3Z+Pw= @@ -906,6 +932,7 @@ github.com/owncloud/ocis-settings v0.3.2-0.20200828091056-47af10a0e872 h1:VGWM/e github.com/owncloud/ocis-settings v0.3.2-0.20200828091056-47af10a0e872/go.mod h1:vRge9QDkOsc6j76gPBmZs1Z5uOPrV4DIkZCgZCEFwBA= github.com/owncloud/ocis/settings v0.0.0-20200918114005-1a0ddd2190ee h1:LaKP3hCTJ6WwB3W40m5UhddvokMRIbOXrjogxON4jAI= github.com/owncloud/ocis/settings v0.0.0-20200918114005-1a0ddd2190ee/go.mod h1:5w91idmyOd8LgYK3eGuqsFBOfVJnSDeEp7S6dHheW14= +github.com/owncloud/ocis/storage v0.0.0-20201015120921-38358ba4d4df/go.mod h1:s9kJvxtBlHEi5qc1TuPAdz2bprk9yGFe+FSOeC76Pbs= github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c h1:rp5dCmg/yLR3mgFuSOe4oEnDDmGLROTvMragMUXpTQw= github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c/go.mod h1:X07ZCGwUbLaax7L0S3Tw4hpejzu63ZrrQiUe6W0hcy0= github.com/parnurzeal/gorequest v0.2.15/go.mod h1:3Kh2QUMJoqw3icWAecsyzkpY7UzRfDhbRdTjtNwNiUE= @@ -917,6 +944,7 @@ github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181 github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.4.0 h1:u3Z1r+oOXJIkxqw34zVhyPgjBsm6X2wn21NWs/HfSeg= github.com/pelletier/go-toml v1.4.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo= +github.com/pelletier/go-toml v1.8.0/go.mod h1:D6yutnOGMveHEPV7VQOuvI/gXY61bv+9bAOTRnLElKs= github.com/philhofer/fwd v1.0.0 h1:UbZqGr5Y38ApvM/V/jEljVxwocdweyH+vmYvRPBnbqQ= github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= @@ -927,6 +955,8 @@ github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= +github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= +github.com/pkg/term v0.0.0-20200520122047-c3ffed290a03/go.mod h1:Z9+Ul5bCbBKnbCvdOWbLqTHhJiYV414CURZJba6L8qA= github.com/pkg/xattr v0.4.1/go.mod h1:W2cGD0TBEus7MkUgv0tNZ9JutLtVO3cXu+IBRuHqnFs= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -1043,6 +1073,7 @@ github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B github.com/spf13/afero v1.2.0/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= +github.com/spf13/afero v1.3.2/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= github.com/spf13/cast v1.2.0/go.mod h1:r2rcYCSwa1IExKTDiTfzaxqT2FNHs8hODu4LnUfgKEg= github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= @@ -1050,6 +1081,7 @@ github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= +github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= @@ -1061,6 +1093,7 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An github.com/spf13/viper v1.2.1/go.mod h1:P4AexN0a+C9tGAnUFNwDMYYZv3pjFuvmeiMyKRaNVlI= github.com/spf13/viper v1.3.1/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= +github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= github.com/spf13/viper v1.6.3 h1:pDDu1OyEDTKzpJwdq4TiuLyMsUgRa/BT5cn5O62NoHs= github.com/spf13/viper v1.6.3/go.mod h1:jUMtyi0/lB5yZH/FjyGAoH7IMNrIhlBf6pXZmbMDvzw= github.com/spf13/viper v1.7.0 h1:xVKxvI7ouOI5I+U9s2eeiUfMaWBVoXA3AWskkrqK0VM= @@ -1080,6 +1113,7 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/studio-b12/gowebdav v0.0.0-20200303150724-9380631c29a1/go.mod h1:gCcfDlA1Y7GqOaeEKw5l9dOGx1VLdc/HuQSlQAaZ30s= github.com/subosito/gotenv v1.1.1/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= @@ -1142,6 +1176,7 @@ github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c/go.mod h1:UrdRz5enIKZ63M github.com/yaegashi/msgraph.go v0.1.1-0.20200221123608-2d438cf2a7cc h1:ejaC8rvIvCWmsaFrvmGOxhBuMxxhBB1xRshuM98XQ7M= github.com/yaegashi/msgraph.go v0.1.1-0.20200221123608-2d438cf2a7cc/go.mod h1:tso14hwzqX4VbnWTNsxiL0DvMb2OwbGISFA7jDibdWc= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= @@ -1196,17 +1231,20 @@ golang.org/x/crypto v0.0.0-20190418165655-df01cb2cc480/go.mod h1:WFFai1msRO1wXaE golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190829043050-9756ffdc2472/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190927123631-a832865fa7ad/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200320181102-891825fb96df/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59 h1:3zb4D3T4G8jdExgVU/95+vQXfpEPiMdCaZgmGVxjNHM= golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de h1:ikNHVSjEfnvz6sxdSPCaPt572qowuyMDMJLLm3Db3ig= golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -1219,6 +1257,7 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= @@ -1287,6 +1326,7 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20200301022130-244492dfa37a h1:GuSPYbZzB5/dcLNCwLQLsg3obCJtX9IJhpXkvY7kzk0= golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200421231249-e086a090c8fd/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2 h1:eDrdRpKgkcCqKZQwyZRyeFZgfqt37SL7Kv3tok06cKE= golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= @@ -1361,7 +1401,9 @@ golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191105231009-c1f44814a5cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191113165036-4c7a9d0fe056/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1369,6 +1411,7 @@ golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200219091948-cb0a6d8edb6c h1:jceGD5YNJGgGMkJz79agzOln1K9TaZUjv5ird16qniQ= @@ -1378,15 +1421,19 @@ golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527 h1:uYVVQ9WP/Ds2ROhcaGPeIdVq0 golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200523222454-059865788121 h1:rITEj+UZHYC927n8GT97eC3zrpzXdb/voyeOuVKS46o= golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1 h1:ogLJMz+qpzav7lGMh10LMvAkM/fAoGlaiiHYiFYdm80= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200720211630-cb9d2d5c5666/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190921001708-c4c64cad1fd0 h1:xQwXv67TxFo9nC1GJFyab5eq/5B590r6RlnL/G8Sz7w= @@ -1456,6 +1503,8 @@ golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapK golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200522201501-cb1345f3a375/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200721223218-6123e77877b2/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200811215021-48a8ffc5b207 h1:8Kg+JssU1jBZs8GIrL5pl4nVyaqyyhdmHAR4D1zGErg= golang.org/x/tools v0.0.0-20200811215021-48a8ffc5b207/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -1515,6 +1564,7 @@ google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4 google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20200527145253-8367513e4ece h1:1YM0uhfumvoDu9sx8+RyWwTI63zoCQvI23IYFRlvte0= @@ -1562,6 +1612,7 @@ gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.44.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.57.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/mail.v2 v2.0.0-20180731213649-a0242b2233b4/go.mod h1:htwXN1Qh09vZJ1NVKxQqHPBaCBbzKhp5GzuJEA4VJWw= gopkg.in/ns1/ns1-go.v2 v2.0.0-20190730140822-b51389932cbc/go.mod h1:VV+3haRsgDiVLxyifmMBrBIuCWFBPYKbRssXB9z67Hw= gopkg.in/resty.v1 v1.9.1/go.mod h1:vo52Hzryw9PnPHcJfPsBiFW62XhNx5OczbV9y+IMpgc= @@ -1572,6 +1623,7 @@ gopkg.in/square/go-jose.v2 v2.3.1 h1:SK5KegNXmKmqE342YYN2qPHEnUYeoMiXXl1poUlI+o4 gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/square/go-jose.v2 v2.5.0 h1:OZ4sdq+Y+SHfYB7vfthi1Ei8b0vkP8ZPQgUfUwdUSqo= gopkg.in/square/go-jose.v2 v2.5.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/telegram-bot-api.v4 v4.6.4/go.mod h1:5DpGO5dbumb40px+dXcwCpcjmeHNYLpk0bp3XRNvWDM= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= @@ -1585,6 +1637,8 @@ gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5 h1:ymVxjfMaHvXD8RqPRmzHHsB3VvucivSkIAvJFDI5O3c= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c h1:grhR+C34yXImVGp7EzNk+DTIk+323eIUWOmEevy6bDo= gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/ocis-pkg/middleware/account.go b/ocis-pkg/middleware/account.go index 754db161f..af04acbce 100644 --- a/ocis-pkg/middleware/account.go +++ b/ocis-pkg/middleware/account.go @@ -22,8 +22,10 @@ func newAccountOptions(opts ...account.Option) account.Options { // AccountID serves as key for the account uuid in the context const AccountID string = "Account-Id" + // RoleIDs serves as key for the roles in the context const RoleIDs string = "Role-Ids" + // UUIDKey serves as key for the account uuid in the context // Deprecated: UUIDKey exists for compatibility reasons. Use AccountID instead. var UUIDKey struct{} diff --git a/ocis/go.mod b/ocis/go.mod index 11ac1aaa0..e9d6b2901 100644 --- a/ocis/go.mod +++ b/ocis/go.mod @@ -24,7 +24,7 @@ require ( github.com/owncloud/ocis/ocs v0.0.0-00010101000000-000000000000 github.com/owncloud/ocis/proxy v0.0.0-00010101000000-000000000000 github.com/owncloud/ocis/settings v0.0.0-20200918114005-1a0ddd2190ee - github.com/owncloud/ocis/storage v0.0.0-00010101000000-000000000000 + github.com/owncloud/ocis/storage v0.0.0-20201015120921-38358ba4d4df github.com/owncloud/ocis/store v0.0.0-20200918125107-fcca9faa81c8 github.com/owncloud/ocis/thumbnails v0.1.6 github.com/owncloud/ocis/webdav v0.0.0-00010101000000-000000000000 diff --git a/ocis/go.sum b/ocis/go.sum index 0061d8251..9e8e1b3ae 100644 --- a/ocis/go.sum +++ b/ocis/go.sum @@ -126,8 +126,10 @@ github.com/aws/aws-sdk-go v1.23.19/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpi github.com/aws/aws-sdk-go v1.28.2/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.33.19/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= github.com/aws/aws-sdk-go v1.34.2/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= +github.com/aws/aws-sdk-go v1.34.12/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= github.com/aws/aws-sdk-go v1.35.0 h1:Pxqn1MWNfBCNcX7jrXCCTfsKpg5ms2IMUMmmcGtYJuo= github.com/aws/aws-sdk-go v1.35.0/go.mod h1:H7NKnBqNVzoTJpGfLrQkkD+ytBA93eiDYi/+8rV9s48= +github.com/aws/aws-sdk-go v1.35.9 h1:b1HiUpdkFLJyoOQ7zas36YHzjNHH0ivHx/G5lWBeg+U= github.com/aws/aws-sdk-go v1.35.9/go.mod h1:tlPOdRjfxPBpNIwqDj61rmsnA85v9jc0Ps9+muhnW+k= github.com/aws/aws-xray-sdk-go v0.9.4/go.mod h1:XtMKdBQfpVut+tJEwI7+dJFRxxRdxHDyVNp2tHXRq04= github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f/go.mod h1:AuiFmCCPBSrqvVMvuqFuk0qogytodnVFVSN5CeJB8Gc= @@ -268,11 +270,13 @@ github.com/cs3org/go-cs3apis v0.0.0-20200929101248-821df597ec8d/go.mod h1:UXha4T github.com/cs3org/reva v0.0.2-0.20200115110931-4c7513415ec5/go.mod h1:Hk3eCcdhtv4eIhKvRK736fQuOyS1HuHnUcz0Dq6NK1A= github.com/cs3org/reva v1.1.0/go.mod h1:fBzTrNuAKdQ62ybjpdu8nyhBin90/3/3s6DGQDCdBp4= github.com/cs3org/reva v1.2.1-0.20200826162318-c0f54e1f37ea/go.mod h1:FvYB+UPpuPCw0hH42qHVR1R2atZyy69METZI/zEaST8= +github.com/cs3org/reva v1.2.2-0.20200924071957-e6676516e61e/go.mod h1:DOV5SjpOBKN+aWfOHLdA4KiLQkpyC786PQaXEdRAZ0M= github.com/cs3org/reva v1.2.2-0.20201006093611-4a9be347ac29 h1:bGdr8WQI3khh8/Uo7icnWOvIyGxnGUVvSSLvtEliuIE= github.com/cs3org/reva v1.2.2-0.20201006093611-4a9be347ac29/go.mod h1:c0MYy0goE5OGC8WPb5LLMZtCqymwSk2fiKVQANzy0zg= github.com/cs3org/reva v1.2.2-0.20201007135248-bccddc4b5a48 h1:ICRTh96BemJ+oOSgp8j4EM32Ye10jh+UWjMxKbVr30g= github.com/cs3org/reva v1.2.2-0.20201007135248-bccddc4b5a48/go.mod h1:A4Q/nQ8Vs+HeAduSFnM37fqxEM3uXVxhaHrNL+gWcBY= github.com/cs3org/reva v1.3.1-0.20201021065855-dc400f81ecbc/go.mod h1:rTJhfVoZggB5iSPH5oWqQSO+W1iTQIxNmaX/ueS9GAU= +github.com/cs3org/reva v1.3.1-0.20201021130722-dd3a8c0f3881 h1:xhpamvgyDr0jCtjXZCTk8qOdnslxhz8dHym5KLh7gl8= github.com/cs3org/reva v1.3.1-0.20201021130722-dd3a8c0f3881/go.mod h1:NplJavkhPZvy8/9K9m95g6uddq3pATO62bovvevpsBw= github.com/cznic/b v0.0.0-20181122101859-a26611c4d92d h1:SwD98825d6bdB+pEuTxWOXiSjBrHdOl/UVp75eI7JT8= github.com/cznic/b v0.0.0-20181122101859-a26611c4d92d/go.mod h1:URriBxXwVq5ijiJ12C7iIZqlA69nTlI+LgI6/pwftG8= @@ -393,6 +397,7 @@ github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2 github.com/go-ldap/ldap/v3 v3.1.7/go.mod h1:5Zun81jBTabRaI8lzN7E1JjyEl1g6zI6u9pd8luAK4Q= github.com/go-ldap/ldap/v3 v3.2.3 h1:FBt+5w3q/vPVPb4eYMQSn+pOiz4zewPamYhlGMmc7yM= github.com/go-ldap/ldap/v3 v3.2.3/go.mod h1:iYS1MdmrmceOJ1QOTnRXrIs7i3kloqtmGQjRvjKpyMg= +github.com/go-ldap/ldap/v3 v3.2.4 h1:PFavAq2xTgzo/loE8qNXcQaofAaqIpI4WgaLdv+1l3E= github.com/go-ldap/ldap/v3 v3.2.4/go.mod h1:iYS1MdmrmceOJ1QOTnRXrIs7i3kloqtmGQjRvjKpyMg= github.com/go-log/log v0.1.0/go.mod h1:4mBwpdRMFLiuXZDCwU2lKQFsoSCo72j3HqBK9d81N2M= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= @@ -636,6 +641,7 @@ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvq github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= @@ -1063,9 +1069,11 @@ github.com/ory/dockertest v3.3.5+incompatible/go.mod h1:1vX4m9wsvi00u5bseYwXaSnh github.com/ory/fosite v0.29.0/go.mod h1:0atSZmXO7CAcs6NPMI/Qtot8tmZYj04Nddoold4S2h0= github.com/ory/fosite v0.30.2/go.mod h1:Lq9qQ9Sl6mcea2Tt8J7PU+wUeFYPZ+vg7N3zPVKGbN8= github.com/ory/fosite v0.32.2/go.mod h1:UeBhRgW6nAjTcd8S7kAo0IFsY/rTPyOXPq/t8N20Q8I= +github.com/ory/fosite v0.33.0/go.mod h1:h+ize9gk0GvRyGjabriqSEmTkMhny+O95cijb8DVqPE= github.com/ory/fosite v0.34.0 h1:lCUX4f5BoiXBIRUYKeTne+aRl0v6StgWpyYKq+7ILw0= github.com/ory/fosite v0.34.0/go.mod h1:h+ize9gk0GvRyGjabriqSEmTkMhny+O95cijb8DVqPE= github.com/ory/fosite v0.35.0/go.mod h1:h+ize9gk0GvRyGjabriqSEmTkMhny+O95cijb8DVqPE= +github.com/ory/fosite v0.35.1 h1:mGPcwVGwHA7Yy9wr/7LDps6BEXyavL32NxizL9eH53Q= github.com/ory/fosite v0.35.1/go.mod h1:h+ize9gk0GvRyGjabriqSEmTkMhny+O95cijb8DVqPE= github.com/ory/go-acc v0.0.0-20181118080137-ddc355013f90/go.mod h1:sxnvPCxChFuSmTJGj8FdMupeq1BezCiEpDjTUXQ4hf4= github.com/ory/go-acc v0.2.1/go.mod h1:0omgy2aa3nDBJ45VAKeLHH8ccPBudxLeic4xiDRtug0= @@ -1385,6 +1393,7 @@ go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4 h1:LYy1Hy3MJdrCdMwwzxA/dRok4ejH+RwNGbuoD9fCjto= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.5 h1:dntmOdLpSpHlVqbW5Eay97DelsZHe+55D+xC6i0dDS0= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= diff --git a/ocis/tests/acceptance/expected-failures-on-OCIS-storage.txt b/ocis/tests/acceptance/expected-failures-on-OCIS-storage.txt index d74e62114..d811d975a 100644 --- a/ocis/tests/acceptance/expected-failures-on-OCIS-storage.txt +++ b/ocis/tests/acceptance/expected-failures-on-OCIS-storage.txt @@ -161,7 +161,6 @@ apiProvisioning-v2/enableUser.feature:31 apiProvisioning-v2/enableUser.feature:32 apiProvisioning-v2/getUser.feature:34 apiProvisioning-v2/getUser.feature:35 -apiProvisioning-v1/addUser.feature:118 # # https://github.com/owncloud/ocis-accounts/issues/80 # Creating an already existing user works @@ -177,29 +176,16 @@ apiProvisioning-v2/addUser.feature:39 apiProvisioning-v1/addUser.feature:69 apiProvisioning-v2/addUser.feature:69 # +# https://github.com/owncloud/product/issues/237 etags are not quoted in PROPFIND +# +apiSharePublicLink1/createPublicLinkShare.feature:793 +apiSharePublicLink1/createPublicLinkShare.feature:804 +# # https://github.com/owncloud/ocis-accounts/issues/128 # Username is case sensitive # -apiProvisioning-v1/addUser.feature:96 -apiProvisioning-v1/addUser.feature:97 -apiProvisioning-v1/addUser.feature:98 -apiProvisioning-v1/addUser.feature:99 -apiProvisioning-v1/addUser.feature:100 apiProvisioning-v1/addUser.feature:102 -apiProvisioning-v1/getUser.feature:37 -apiProvisioning-v1/getUser.feature:113 -apiProvisioning-v1/getUser.feature:124 -apiProvisioning-v2/addUser.feature:96 -apiProvisioning-v2/addUser.feature:97 -apiProvisioning-v2/addUser.feature:98 -apiProvisioning-v2/addUser.feature:99 -apiProvisioning-v2/addUser.feature:100 apiProvisioning-v2/addUser.feature:102 -apiProvisioning-v2/getUser.feature:37 -apiProvisioning-v2/getUser.feature:114 -apiProvisioning-v2/getUser.feature:125 -apiProvisioning-v1/deleteUser.feature:32 -apiProvisioning-v2/deleteUser.feature:32 # # https://github.com/owncloud/ocis/issues/197 # Client token generation not implemented @@ -494,6 +480,7 @@ apiShareOperationsToShares/getWebDAVSharePermissions.feature:143 # apiShareOperationsToShares/gettingShares.feature:167 apiShareOperationsToShares/gettingShares.feature:168 +# apiSharePublicLink1/createPublicLinkShare.feature:793 apiSharePublicLink1/createPublicLinkShare.feature:804 # @@ -1301,6 +1288,9 @@ apiWebdavProperties2/getFileProperties.feature:441 apiWebdavProperties2/getFileProperties.feature:442 apiWebdavProperties2/getFileProperties.feature:454 apiWebdavProperties2/getFileProperties.feature:455 +# +# https://github.com/owncloud/product/issues/237 etags are not quoted in PROPFIND +# apiWebdavUpload1/uploadFile.feature:144 apiWebdavUpload1/uploadFile.feature:145 apiWebdavUpload1/uploadFile.feature:157 @@ -1509,7 +1499,7 @@ apiWebdavEtagPropagation1/deleteFileFolder.feature:152 apiWebdavEtagPropagation1/deleteFileFolder.feature:182 apiWebdavEtagPropagation1/deleteFileFolder.feature:183 # -# https://github.com/owncloud/product/issues/239 moving a file from one folder to an other does not change the etag of the source folder +# https://github.com/owncloud/product/issues/243 etags don't change for a share receiver # apiWebdavEtagPropagation1/moveFileFolder.feature:235 apiWebdavEtagPropagation1/moveFileFolder.feature:236 diff --git a/ocis/tests/acceptance/expected-failures-on-OWNCLOUD-storage.txt b/ocis/tests/acceptance/expected-failures-on-OWNCLOUD-storage.txt index d9ff55251..60b994e4d 100644 --- a/ocis/tests/acceptance/expected-failures-on-OWNCLOUD-storage.txt +++ b/ocis/tests/acceptance/expected-failures-on-OWNCLOUD-storage.txt @@ -165,13 +165,11 @@ apiProvisioning-v2/enableUser.feature:31 apiProvisioning-v2/enableUser.feature:32 apiProvisioning-v2/getUser.feature:34 apiProvisioning-v2/getUser.feature:35 -apiProvisioning-v1/addUser.feature:118 # # https://github.com/owncloud/ocis-accounts/issues/80 # Creating an already existing user works # apiProvisioning-v1/addUser.feature:32 -apiProvisioning-v2/addUser.feature:32 apiProvisioning-v1/addUser.feature:39 apiProvisioning-v2/addUser.feature:39 # @@ -184,26 +182,7 @@ apiProvisioning-v2/addUser.feature:69 # https://github.com/owncloud/ocis-accounts/issues/128 # Username is case sensitive # -apiProvisioning-v1/addUser.feature:96 -apiProvisioning-v1/addUser.feature:97 -apiProvisioning-v1/addUser.feature:98 -apiProvisioning-v1/addUser.feature:99 -apiProvisioning-v1/addUser.feature:100 apiProvisioning-v1/addUser.feature:102 -apiProvisioning-v1/getUser.feature:37 -apiProvisioning-v1/getUser.feature:113 -apiProvisioning-v1/getUser.feature:124 -apiProvisioning-v2/addUser.feature:96 -apiProvisioning-v2/addUser.feature:97 -apiProvisioning-v2/addUser.feature:98 -apiProvisioning-v2/addUser.feature:99 -apiProvisioning-v2/addUser.feature:100 -apiProvisioning-v2/addUser.feature:102 -apiProvisioning-v2/getUser.feature:37 -apiProvisioning-v2/getUser.feature:114 -apiProvisioning-v2/getUser.feature:125 -apiProvisioning-v1/deleteUser.feature:32 -apiProvisioning-v2/deleteUser.feature:32 # # https://github.com/owncloud/ocis/issues/197 # Client token generation not implemented diff --git a/ocis/tests/acceptance/features/apiOcisSpecific/apiAuthOcs-ocsPUTAuth.feature b/ocis/tests/acceptance/features/apiOcisSpecific/apiAuthOcs-ocsPUTAuth.feature index d603acb69..5bdb30e8d 100644 --- a/ocis/tests/acceptance/features/apiOcisSpecific/apiAuthOcs-ocsPUTAuth.feature +++ b/ocis/tests/acceptance/features/apiOcisSpecific/apiAuthOcs-ocsPUTAuth.feature @@ -20,12 +20,12 @@ Feature: auth | endpoint | | /ocs/v1.php/cloud/users/%username% | Then the HTTP status code of responses on all endpoints should be "200" - And the OCS status code of responses on all endpoints should be "103" + And the OCS status code of responses on all endpoints should be "998" When the administrator requests these endpoints with "PUT" with body "doesnotmatter" using password "invalid" about user "Alice" | endpoint | | /ocs/v2.php/cloud/users/%username% | - Then the HTTP status code of responses on all endpoints should be "400" - And the OCS status code of responses on all endpoints should be "103" + Then the HTTP status code of responses on all endpoints should be "404" + And the OCS status code of responses on all endpoints should be "998" @issue-ocis-reva-30 @issue-ocis-ocs-28 diff --git a/ocis/tests/config/drone/ocis-config.json b/ocis/tests/config/drone/ocis-config.json index bf9e30f9f..b8c5b416c 100644 --- a/ocis/tests/config/drone/ocis-config.json +++ b/ocis/tests/config/drone/ocis-config.json @@ -12,8 +12,17 @@ "apps": [ "files", "draw-io", - "pdf-viewer", "markdown-editor", "media-viewer" + ], + "external_apps": [ + { + "id": "settings", + "path": "https://ocis-server:9200/settings.js" + }, + { + "id": "accounts", + "path": "https://ocis-server:9200/accounts.js" + } ] } diff --git a/ocs/go.mod b/ocs/go.mod index f9e941ae4..867e33352 100644 --- a/ocs/go.mod +++ b/ocs/go.mod @@ -7,11 +7,10 @@ require ( contrib.go.opencensus.io/exporter/ocagent v0.7.0 contrib.go.opencensus.io/exporter/zipkin v0.1.1 github.com/UnnoTed/fileb0x v1.1.4 - github.com/cs3org/reva v1.2.1-0.20200826162318-c0f54e1f37ea + github.com/cs3org/reva v1.3.1-0.20201021065855-dc400f81ecbc github.com/go-chi/chi v4.1.2+incompatible github.com/go-chi/render v1.0.1 - github.com/golang/protobuf v1.4.2 - github.com/google/uuid v1.1.2 // indirect + github.com/golang/protobuf v1.4.3 github.com/micro/cli/v2 v2.1.2 github.com/micro/go-micro/v2 v2.9.1 github.com/oklog/run v1.1.0 @@ -25,7 +24,7 @@ require ( github.com/restic/calens v0.2.0 github.com/spf13/viper v1.7.0 github.com/stretchr/testify v1.6.1 - go.opencensus.io v0.22.4 + go.opencensus.io v0.22.5 golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a // indirect google.golang.org/protobuf v1.25.0 ) diff --git a/ocs/go.sum b/ocs/go.sum index 9a7f7dac7..ab7e37ced 100644 --- a/ocs/go.sum +++ b/ocs/go.sum @@ -144,6 +144,8 @@ github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:l github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496 h1:zV3ejI06GQ59hwDQAvmK1qxOQGB3WuVTRoY0okPTAv0= github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg= +github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535 h1:4daAzAu0S6Vi7/lbWECcX0j45yZReDZ56BQsrVBOEEY= +github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg= github.com/ascarter/requestid v0.0.0-20170313220838-5b76ab3d4aee h1:3T/l+vMotQ7cDSLWNAn2Vg1SAQ3mdyLgBWWBitSS3uU= github.com/ascarter/requestid v0.0.0-20170313220838-5b76ab3d4aee/go.mod h1:u7Wtt4WATGGgae9mURNGQQqxAudPKrxfsbSDSGOso+g= github.com/asim/go-awsxray v0.0.0-20161209120537-0d8a60b6e205/go.mod h1:frVmN4PtXUuL1EbZn0uL4PHSTKNKFnbMpBIhngqMuNQ= @@ -159,6 +161,8 @@ github.com/aws/aws-sdk-go v1.28.2/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN github.com/aws/aws-sdk-go v1.29.26/go.mod h1:1KvfttTE3SPKMpo8g2c6jL3ZKfXtFvKscTgahTma5Xg= github.com/aws/aws-sdk-go v1.33.19/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= github.com/aws/aws-sdk-go v1.34.2/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= +github.com/aws/aws-sdk-go v1.34.12/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= +github.com/aws/aws-sdk-go v1.35.9/go.mod h1:tlPOdRjfxPBpNIwqDj61rmsnA85v9jc0Ps9+muhnW+k= github.com/aws/aws-xray-sdk-go v0.9.4/go.mod h1:XtMKdBQfpVut+tJEwI7+dJFRxxRdxHDyVNp2tHXRq04= github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f/go.mod h1:AuiFmCCPBSrqvVMvuqFuk0qogytodnVFVSN5CeJB8Gc= github.com/beevik/etree v1.1.0/go.mod h1:r8Aw8JqVegEf0w2fDnATrX9VpkMcyFeM0FhwO62wh+A= @@ -210,6 +214,7 @@ github.com/bwmarrin/discordgo v0.19.0/go.mod h1:O9S4p+ofTFwB02em7jkpkV8M3R0/PUVO github.com/bwmarrin/discordgo v0.20.1/go.mod h1:O9S4p+ofTFwB02em7jkpkV8M3R0/PUVOwN61zSZ0r4Q= github.com/bwmarrin/discordgo v0.20.2/go.mod h1:O9S4p+ofTFwB02em7jkpkV8M3R0/PUVOwN61zSZ0r4Q= github.com/c-bata/go-prompt v0.2.3/go.mod h1:VzqtzE2ksDBcdln8G7mk2RX9QyGjH+OVqOCSiVIqS34= +github.com/c-bata/go-prompt v0.2.5/go.mod h1:vFnjEGDIIA/Lib7giyE4E9c50Lvl8j0S+7FVlAwDAVw= github.com/caddyserver/certmagic v0.10.6/go.mod h1:Y8jcUBctgk/IhpAzlHKfimZNyXCkfGgRTC0orl8gROQ= github.com/cenkalti/backoff v2.1.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= @@ -293,6 +298,7 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3 github.com/crewjam/httperr v0.0.0-20190612203328-a946449404da/go.mod h1:+rmNIXRvYMqLQeR4DHyTvs6y0MEMymTz4vyFpFkKTPs= github.com/crewjam/saml v0.4.0/go.mod h1:geQUbAAwmTKNJFDzoXaTssZHY26O89PHIm3K3YWjWnI= github.com/cs3org/cato v0.0.0-20200626150132-28a40e643719/go.mod h1:XJEZ3/EQuI3BXTp/6DUzFr850vlxq11I6satRtz0YQ4= +github.com/cs3org/cato v0.0.0-20200828125504-e418fc54dd5e/go.mod h1:XJEZ3/EQuI3BXTp/6DUzFr850vlxq11I6satRtz0YQ4= github.com/cs3org/go-cs3apis v0.0.0-20191128165347-19746c015c83/go.mod h1:IsVGyZrOLUQD48JIhlM/xb3Vz6He5o2+W0ZTfUGY+IU= github.com/cs3org/go-cs3apis v0.0.0-20200306065539-29abc33f5be0 h1:jTKILSBtDm0GEw3FtXPxc5wxGpaw2pxzREg1GBV9LIQ= github.com/cs3org/go-cs3apis v0.0.0-20200306065539-29abc33f5be0/go.mod h1:UXha4TguuB52H14EMoSsCqDj7k8a/t7g4gVP+bgY5LY= @@ -301,6 +307,7 @@ github.com/cs3org/go-cs3apis v0.0.0-20200730121022-c4f3d4f7ddfd h1:uMaudkC7znaiI github.com/cs3org/go-cs3apis v0.0.0-20200730121022-c4f3d4f7ddfd/go.mod h1:UXha4TguuB52H14EMoSsCqDj7k8a/t7g4gVP+bgY5LY= github.com/cs3org/go-cs3apis v0.0.0-20200810113633-b00aca449666 h1:E7VsSSN/2YZLSwrDMJJdAWU11lP7W1qkcXbrslb0PM0= github.com/cs3org/go-cs3apis v0.0.0-20200810113633-b00aca449666/go.mod h1:UXha4TguuB52H14EMoSsCqDj7k8a/t7g4gVP+bgY5LY= +github.com/cs3org/go-cs3apis v0.0.0-20200929101248-821df597ec8d/go.mod h1:UXha4TguuB52H14EMoSsCqDj7k8a/t7g4gVP+bgY5LY= github.com/cs3org/reva v0.0.2-0.20200115110931-4c7513415ec5/go.mod h1:Hk3eCcdhtv4eIhKvRK736fQuOyS1HuHnUcz0Dq6NK1A= github.com/cs3org/reva v0.1.0 h1:PYzDejKm/+xG3OTS2WgzBxcksVogEGmPgjJVegwSR2c= github.com/cs3org/reva v0.1.0/go.mod h1:8j6QyyAq9Kjj7RPfJb7M1aEmw5DmsuCJKUULXxYOyRo= @@ -308,6 +315,10 @@ github.com/cs3org/reva v1.1.0 h1:Gih6ECHvMMGSx523SFluFlDmNMuhYelXYShdWvjvW38= github.com/cs3org/reva v1.1.0/go.mod h1:fBzTrNuAKdQ62ybjpdu8nyhBin90/3/3s6DGQDCdBp4= github.com/cs3org/reva v1.2.1-0.20200826162318-c0f54e1f37ea h1:xVyJvR+GoaBrRJV6GnDflgm9bzkmjchCBBg0nMiHu6M= github.com/cs3org/reva v1.2.1-0.20200826162318-c0f54e1f37ea/go.mod h1:FvYB+UPpuPCw0hH42qHVR1R2atZyy69METZI/zEaST8= +github.com/cs3org/reva v1.2.2-0.20200924071957-e6676516e61e h1:khITGSnfDXtByQsLezoXgocUgGHJBBn0BPsUihGvk7w= +github.com/cs3org/reva v1.2.2-0.20200924071957-e6676516e61e/go.mod h1:DOV5SjpOBKN+aWfOHLdA4KiLQkpyC786PQaXEdRAZ0M= +github.com/cs3org/reva v1.3.1-0.20201021065855-dc400f81ecbc h1:8y6N/bscmzdXd4yAv6bIJiqggter8iEmb4cYMhuZkE8= +github.com/cs3org/reva v1.3.1-0.20201021065855-dc400f81ecbc/go.mod h1:rTJhfVoZggB5iSPH5oWqQSO+W1iTQIxNmaX/ueS9GAU= github.com/cznic/b v0.0.0-20181122101859-a26611c4d92d h1:SwD98825d6bdB+pEuTxWOXiSjBrHdOl/UVp75eI7JT8= github.com/cznic/b v0.0.0-20181122101859-a26611c4d92d/go.mod h1:URriBxXwVq5ijiJ12C7iIZqlA69nTlI+LgI6/pwftG8= github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548 h1:iwZdTE0PVqJCos1vaoKsclOGD3ADKpshg3SRtYBbwso= @@ -323,8 +334,12 @@ github.com/deckarep/golang-set v1.7.1/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14y github.com/decker502/dnspod-go v0.2.0/go.mod h1:qsurYu1FgxcDwfSwXJdLt4kRsBLZeosEb9uq4Sy+08g= github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f/go.mod h1:xH/i4TFMt8koVQZ6WFms69WAsDWr2XsYL3Hkl7jkoLE= github.com/devigned/tab v0.1.1/go.mod h1:XG9mPq0dFghrYvoBF3xdRrJzSTX1b7IQrvaL9mzjeJY= +github.com/dgraph-io/ristretto v0.0.1/go.mod h1:T40EBc7CJke8TkpiYfGGKAeFjSaxuFXhuXRyumBd6RE= +github.com/dgraph-io/ristretto v0.0.2/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= +github.com/dgraph-io/ristretto v0.0.3/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8= github.com/dnaeon/go-vcr v0.0.0-20180814043457-aafff18a5cc2/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= @@ -431,6 +446,7 @@ github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2 github.com/go-ldap/ldap v3.0.2+incompatible/go.mod h1:qfd9rJvER9Q0/D/Sqn1DfHRoBp40uXYvFoEVrNEPqRc= github.com/go-ldap/ldap/v3 v3.1.7/go.mod h1:5Zun81jBTabRaI8lzN7E1JjyEl1g6zI6u9pd8luAK4Q= github.com/go-ldap/ldap/v3 v3.2.3/go.mod h1:iYS1MdmrmceOJ1QOTnRXrIs7i3kloqtmGQjRvjKpyMg= +github.com/go-ldap/ldap/v3 v3.2.4/go.mod h1:iYS1MdmrmceOJ1QOTnRXrIs7i3kloqtmGQjRvjKpyMg= github.com/go-log/log v0.1.0 h1:wudGTNsiGzrD5ZjgIkVZ517ugi2XRe9Q/xRCzwEO4/U= github.com/go-log/log v0.1.0/go.mod h1:4mBwpdRMFLiuXZDCwU2lKQFsoSCo72j3HqBK9d81N2M= github.com/go-log/log v0.2.0/go.mod h1:xzCnwajcues/6w7lne3yK2QU7DBPW7kqbgPGG5AF65U= @@ -683,6 +699,7 @@ github.com/golang/protobuf v1.4.1 h1:ZFgWrT+bLgsYPirOnRfKLYJLvssAegOj/hgyMFdJZe0 github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= @@ -759,6 +776,7 @@ github.com/grpc-ecosystem/go-grpc-middleware v1.1.0/go.mod h1:f5nM7jw/oeRSadq3xC github.com/grpc-ecosystem/go-grpc-middleware v1.2.0 h1:0IKlLyQ3Hs9nDaiK5cSHAGmcQEIC8l2Ts1u6x5Dfrqg= github.com/grpc-ecosystem/go-grpc-middleware v1.2.0/go.mod h1:mJzapYve32yjrKlk9GbyCZHuPgZsrbyIbyKhSzOpg6s= github.com/grpc-ecosystem/go-grpc-middleware v1.2.1/go.mod h1:EaizFBKfUKtMIF5iaDEhniwNedqGo9FuLFzppDr3uwI= +github.com/grpc-ecosystem/go-grpc-middleware v1.2.2/go.mod h1:EaizFBKfUKtMIF5iaDEhniwNedqGo9FuLFzppDr3uwI= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.8.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= @@ -827,6 +845,7 @@ github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw= github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= +github.com/iancoleman/strcase v0.1.2/go.mod h1:SK73tn/9oHe+/Y0h39VT4UCxmurVJkR5NA7kMEAOgSE= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df/go.mod h1:QMZY7/J/KSQEhKWFeDesPjMj+wCHReeknARU3wqlyN4= github.com/ijc/Gotty v0.0.0-20170406111628-a8b993ba6abd/go.mod h1:3LVOLeyx9XVvwPgrt2be44XgSqndprz1G18rSk8KD84= @@ -850,6 +869,8 @@ github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a/go.mod h1:yL958EeXv8 github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik= +github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/jmhodges/levigo v1.0.0 h1:q5EC36kV79HWeTBWsod3mG11EgStG3qArTKcvlksN1U= github.com/jmhodges/levigo v1.0.0/go.mod h1:Q6Qx+uH3RAqyK4rFQroq9RL7mdkABMcfhEI+nNuzMJQ= github.com/jmoiron/sqlx v0.0.0-20180614180643-0dae4fefe7c0/go.mod h1:IiEW3SEiiErVyFdH8NTuWjSifiEQKUoyK3LNqr2kCHU= @@ -899,6 +920,7 @@ github.com/konsorten/go-windows-terminal-sequences v0.0.0-20180402223658-b729f26 github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= @@ -980,6 +1002,7 @@ github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= +github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= @@ -997,8 +1020,10 @@ github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsO github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-tty v0.0.0-20180219170247-931426f7535a/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE= +github.com/mattn/go-tty v0.0.3/go.mod h1:ihxohKRERHTVzN+aSVRwACLCeqIoZAWpoICkkvrWyR0= github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw= github.com/mattn/goveralls v0.0.5/go.mod h1:Xg2LHi51faXLyKXwsndxiW6uxEEQT9+3sjGzzwU4xy0= +github.com/mattn/goveralls v0.0.6/go.mod h1:h8b4ow6FxSPMQHF6o2ve3qsclnffZjYTNEKmLesRwqw= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mendsley/gojwk v0.0.0-20141217222730-4d5ec6e58103/go.mod h1:o9YPB5aGP8ob35Vy6+vyq3P3bWe7NQWzf+JLiXCiMaE= @@ -1075,6 +1100,7 @@ github.com/mitchellh/mapstructure v1.0.0/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.3.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.3.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.3.3 h1:SzB1nHZ2Xi+17FP0zVQBHIZqvwRN9408fJO8h+eeNA8= github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY= @@ -1187,13 +1213,17 @@ github.com/ory/fosite v0.29.0/go.mod h1:0atSZmXO7CAcs6NPMI/Qtot8tmZYj04Nddoold4S github.com/ory/fosite v0.30.2/go.mod h1:Lq9qQ9Sl6mcea2Tt8J7PU+wUeFYPZ+vg7N3zPVKGbN8= github.com/ory/fosite v0.30.4/go.mod h1:Lq9qQ9Sl6mcea2Tt8J7PU+wUeFYPZ+vg7N3zPVKGbN8= github.com/ory/fosite v0.32.2/go.mod h1:UeBhRgW6nAjTcd8S7kAo0IFsY/rTPyOXPq/t8N20Q8I= +github.com/ory/fosite v0.33.0/go.mod h1:h+ize9gk0GvRyGjabriqSEmTkMhny+O95cijb8DVqPE= +github.com/ory/fosite v0.35.0/go.mod h1:h+ize9gk0GvRyGjabriqSEmTkMhny+O95cijb8DVqPE= github.com/ory/go-acc v0.0.0-20181118080137-ddc355013f90/go.mod h1:sxnvPCxChFuSmTJGj8FdMupeq1BezCiEpDjTUXQ4hf4= github.com/ory/go-acc v0.2.1/go.mod h1:0omgy2aa3nDBJ45VAKeLHH8ccPBudxLeic4xiDRtug0= +github.com/ory/go-acc v0.2.5/go.mod h1:4Kb/UnPcT8qRAk3IAxta+hvVapdxTLWtrr7bFLlEgpw= github.com/ory/go-convenience v0.1.0/go.mod h1:uEY/a60PL5c12nYz4V5cHY03IBmwIAEm8TWB0yn9KNs= github.com/ory/gojsonreference v0.0.0-20190720135523-6b606c2d8ee8/go.mod h1:wsH1C4nIeeQClDtD5AH7kF1uTS6zWyqfjVDTmB0Em7A= github.com/ory/gojsonschema v1.1.1-0.20190919112458-f254ca73d5e9/go.mod h1:BNZpdJgB74KOLSsWFvzw6roXg1I6O51WO8roMmW+T7Y= github.com/ory/herodot v0.6.2/go.mod h1:3BOneqcyBsVybCPAJoi92KN2BpJHcmDqAMcAAaJiJow= github.com/ory/viper v1.5.6/go.mod h1:TYmpFpKLxjQwvT4f0QPpkOn4sDXU1kDgAwJpgLYiQ28= +github.com/ory/viper v1.7.5/go.mod h1:ypOuyJmEUb3oENywQZRgeAMwqgOyDqwboO1tj3DjTaM= github.com/ory/x v0.0.85/go.mod h1:s44V8t3xyjWZREcU+mWlp4h302rTuM4aLXcW+y5FbQ8= github.com/ovh/go-ovh v0.0.0-20181109152953-ba5adb4cf014/go.mod h1:joRatxRJaZBsY3JAOEMcoOp05CnZzsx4scTxi95DHyQ= github.com/owncloud/flaex v0.0.0-20200411150708-dce59891a203/go.mod h1:jip86t4OVURJTf8CM/0e2qcji/Y4NG3l2lR8kex4JWw= @@ -1252,6 +1282,7 @@ github.com/owncloud/ocis/ocis-pkg v0.0.0-20200918114005-1a0ddd2190ee h1:P/bd6+zV github.com/owncloud/ocis/ocis-pkg v0.0.0-20200918114005-1a0ddd2190ee/go.mod h1:kukd3chd0ARpGNSLcIutgFywSvWIIdQcpyhPtVTyZbM= github.com/owncloud/ocis/settings v0.0.0-20200918114005-1a0ddd2190ee h1:LaKP3hCTJ6WwB3W40m5UhddvokMRIbOXrjogxON4jAI= github.com/owncloud/ocis/settings v0.0.0-20200918114005-1a0ddd2190ee/go.mod h1:5w91idmyOd8LgYK3eGuqsFBOfVJnSDeEp7S6dHheW14= +github.com/owncloud/ocis/storage v0.0.0-20201015120921-38358ba4d4df/go.mod h1:s9kJvxtBlHEi5qc1TuPAdz2bprk9yGFe+FSOeC76Pbs= github.com/owncloud/ocis/store v0.0.0-20200918125107-fcca9faa81c8 h1:yFhYoWTmRHopVO2HfSoJtq8n9T91HFMMBTRx+KAvMuw= github.com/owncloud/ocis/store v0.0.0-20200918125107-fcca9faa81c8/go.mod h1://2CHveV3rTfhJIAs/gK7o5/U5jB9Xx8IekM8NEE40s= github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c h1:rp5dCmg/yLR3mgFuSOe4oEnDDmGLROTvMragMUXpTQw= @@ -1269,6 +1300,8 @@ github.com/pelletier/go-toml v1.4.0 h1:u3Z1r+oOXJIkxqw34zVhyPgjBsm6X2wn21NWs/HfS github.com/pelletier/go-toml v1.4.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo= github.com/pelletier/go-toml v1.6.0 h1:aetoXYr0Tv7xRU/V4B4IZJ2QcbtMUFoNb3ORp7TzIK4= github.com/pelletier/go-toml v1.6.0/go.mod h1:5N711Q9dKgbdkxHL+MEfF31hpT7l0S0s/t2kKREewys= +github.com/pelletier/go-toml v1.8.0 h1:Keo9qb7iRJs2voHvunFtuuYFsbWeOBh8/P9v/kVMFtw= +github.com/pelletier/go-toml v1.8.0/go.mod h1:D6yutnOGMveHEPV7VQOuvI/gXY61bv+9bAOTRnLElKs= github.com/philhofer/fwd v1.0.0 h1:UbZqGr5Y38ApvM/V/jEljVxwocdweyH+vmYvRPBnbqQ= github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= @@ -1280,7 +1313,9 @@ github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= +github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= github.com/pkg/term v0.0.0-20200520122047-c3ffed290a03/go.mod h1:Z9+Ul5bCbBKnbCvdOWbLqTHhJiYV414CURZJba6L8qA= +github.com/pkg/term v1.1.0/go.mod h1:E25nymQcrSllhX42Ok8MRm1+hyBdHY0dCeiKZ9jpNGw= github.com/pkg/xattr v0.4.1/go.mod h1:W2cGD0TBEus7MkUgv0tNZ9JutLtVO3cXu+IBRuHqnFs= github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -1359,6 +1394,8 @@ github.com/rs/zerolog v1.18.0 h1:CbAm3kP2Tptby1i9sYy2MGRg0uxIN9cyDb59Ys7W8z8= github.com/rs/zerolog v1.18.0/go.mod h1:9nvC1axdVrAHcu/s9taAVfBuIdTZLVQmKQyvrUjF5+I= github.com/rs/zerolog v1.19.0 h1:hYz4ZVdUgjXTBUmrkrw55j1nHx68LfOKIQk5IYtyScg= github.com/rs/zerolog v1.19.0/go.mod h1:IzD0RJ65iWH0w97OQQebJEvTZYvsCUm9WVLWBQrJRjo= +github.com/rs/zerolog v1.20.0 h1:38k9hgtUBdxFwE34yS8rTHmHBa4eN16E4DJlv177LNs= +github.com/rs/zerolog v1.20.0/go.mod h1:IzD0RJ65iWH0w97OQQebJEvTZYvsCUm9WVLWBQrJRjo= github.com/rubenv/sql-migrate v0.0.0-20190212093014-1007f53448d7/go.mod h1:WS0rl9eEliYI8DPnr3TOwz4439pay+qNgzJoVya/DmY= github.com/russellhaering/goxmldsig v0.0.0-20180430223755-7acd5e4a6ef7/go.mod h1:Oz4y6ImuOQZxynhbSXk7btjEfNBtGlj2dcaOvXl2FSM= github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo= @@ -1420,6 +1457,8 @@ github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B github.com/spf13/afero v1.2.0/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= +github.com/spf13/afero v1.3.2 h1:GDarE4TJQI52kYSbSAmLiId1Elfj+xgSDqrUZxFhxlU= +github.com/spf13/afero v1.3.2/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= github.com/spf13/cast v1.2.0/go.mod h1:r2rcYCSwa1IExKTDiTfzaxqT2FNHs8hODu4LnUfgKEg= github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= @@ -1566,6 +1605,7 @@ go.opencensus.io v0.22.3 h1:8sGtKOrtQqkN1bp2AtX+misvLIlOmsEsNd+9NIcPEm8= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4 h1:LYy1Hy3MJdrCdMwwzxA/dRok4ejH+RwNGbuoD9fCjto= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0 h1:OI5t8sDa1Or+q8AeE+yKeB/SDYioSHAgcVljj9JIETY= @@ -1619,6 +1659,7 @@ golang.org/x/crypto v0.0.0-20190418165655-df01cb2cc480/go.mod h1:WFFai1msRO1wXaE golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190829043050-9756ffdc2472/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= @@ -1639,6 +1680,7 @@ golang.org/x/crypto v0.0.0-20200420201142-3c4aac89819a/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de h1:ikNHVSjEfnvz6sxdSPCaPt572qowuyMDMJLLm3Db3ig= golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a h1:vclmkQCjlDX5OydZ9wv8rBCcS0QyQY66Mpf/7BZbInM= @@ -1819,18 +1861,21 @@ golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191105231009-c1f44814a5cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191110163157-d32e6e3b99c4 h1:Hynbrlo6LbYI3H1IqXpkVDOcX/3HiPdhVEuyj5a59RM= golang.org/x/sys v0.0.0-20191110163157-d32e6e3b99c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191113165036-4c7a9d0fe056/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200219091948-cb0a6d8edb6c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1841,10 +1886,15 @@ golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1 h1:ogLJMz+qpzav7lGMh10LMvAkM/fAoGlaiiHYiFYdm80= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200720211630-cb9d2d5c5666 h1:gVCS+QOncANNPlmlO1AhlU3oxs4V9z+gTtPwIk3p2N8= +golang.org/x/sys v0.0.0-20200720211630-cb9d2d5c5666/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6 h1:DvY3Zkh7KabQE/kfzMvYvKirSiguP9Q/veMtkYyf0o8= golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200918174421-af09f7315aff/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1931,8 +1981,10 @@ golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4 h1:kDtqNkeBrZb8B+atrj50B5X golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= golang.org/x/tools v0.0.0-20200421042724-cfa8b22178d2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200427214658-4697a2867c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200522201501-cb1345f3a375/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200526224456-8b020aee10d2 h1:21BqcH/onxtGHn1A2GDOJjZnbt4Nlez629S3eaR+eYs= golang.org/x/tools v0.0.0-20200526224456-8b020aee10d2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200721223218-6123e77877b2/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200811215021-48a8ffc5b207 h1:8Kg+JssU1jBZs8GIrL5pl4nVyaqyyhdmHAR4D1zGErg= golang.org/x/tools v0.0.0-20200811215021-48a8ffc5b207/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200817023811-d00afeaade8f h1:33yHANSyO/TeglgY9rBhUpX43wtonTXoFOsMRtNB6qE= @@ -2057,6 +2109,8 @@ gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.51.1 h1:GyboHr4UqMiLUybYjd22ZjQIKEJEpgtLXtuGbR21Oho= gopkg.in/ini.v1 v1.51.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.57.0 h1:9unxIsFcTt4I55uWluz+UmL95q4kdJ0buvQ1ZIqVQww= +gopkg.in/ini.v1 v1.57.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/jcmturner/aescts.v1 v1.0.1/go.mod h1:nsR8qBOg+OucoIW+WMhB3GspUQXq9XorLnQb9XtvcOo= gopkg.in/jcmturner/dnsutils.v1 v1.0.1/go.mod h1:m3v+5svpVOhtFAP/wSz+yzh4Mc0Fg7eRhxkJMWSIz9Q= gopkg.in/jcmturner/goidentity.v3 v3.0.0/go.mod h1:oG2kH0IvSYNIu80dVAyu/yoefjq1mNfM5bm88whjWx4= @@ -2079,6 +2133,8 @@ gopkg.in/square/go-jose.v2 v2.4.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76 gopkg.in/square/go-jose.v2 v2.4.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/square/go-jose.v2 v2.5.0 h1:OZ4sdq+Y+SHfYB7vfthi1Ei8b0vkP8ZPQgUfUwdUSqo= gopkg.in/square/go-jose.v2 v2.5.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/square/go-jose.v2 v2.5.1 h1:7odma5RETjNHWJnR32wx8t+Io4djHE1PqxCFx3iiZ2w= +gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/src-d/go-billy.v4 v4.3.2/go.mod h1:nDjArDMp+XMs1aFAESLRjfGSgfvoYN0hDfzEk0GjC98= gopkg.in/src-d/go-git-fixtures.v3 v3.5.0/go.mod h1:dLBcvytrw/TYZsNTWCnkNF2DSIlzWYqTe3rJR56Ac7g= gopkg.in/src-d/go-git.v4 v4.13.1/go.mod h1:nx5NYcxdKxq5fpltdHnPa2Exj4Sx0EclMWZQbYDu2z8= diff --git a/ocs/pkg/server/http/svc_test.go b/ocs/pkg/server/http/svc_test.go index dbfcb5747..5d826c53a 100644 --- a/ocs/pkg/server/http/svc_test.go +++ b/ocs/pkg/server/http/svc_test.go @@ -32,32 +32,39 @@ import ( settings "github.com/owncloud/ocis/settings/pkg/proto/v0" ) -const ocsV1 string = "v1.php" -const ocsV2 string = "v2.php" - -const adminBasicAuth string = "admin:admin" - -const userProvisioningEndPoint string = "/v1.php/cloud/users?format=json" -const groupProvisioningEndPoint string = "/v1.php/cloud/groups?format=json" +const ( + ocsV1 string = "v1.php" + ocsV2 string = "v2.php" + adminBasicAuth string = "admin:admin" +) const unsuccessfulResponseText string = "The response was expected to be successful but was not" -const userIDEinstein string = "4c510ada-c86b-4815-8820-42cdf82c3d51" -const userIDMarie string = "f7fbf8c8-139b-4376-b307-cf0a8c2d0d9c" -const userIDFeynman string = "932b4540-8d16-481e-8ef4-588e4b6b151c" -const userIDKonnectd string = "820ba2a1-3f54-4538-80a4-2d73007e30bf" -const userIDReva string = "bc596f3c-c955-4328-80a0-60d018b4ad57" -const userIDMoss string = "058bff95-6708-4fe5-91e4-9ea3d377588b" +const ( + userProvisioningEndPoint string = "/v1.php/cloud/users?format=json" + groupProvisioningEndPoint string = "/v1.php/cloud/groups?format=json" +) -const groupPhilosophyHaters = "167cbee2-0518-455a-bfb2-031fe0621e5d" -const groupPhysicsLovers = "262982c1-2362-4afa-bfdf-8cbfef64a06e" -const groupPoloniumLovers = "cedc21aa-4072-4614-8676-fa9165f598ff" -const groupQuantumLovers = "a1726108-01f8-4c30-88df-2b1a9d1cba1a" -const groupRadiumLovers = "7b87fd49-286e-4a5f-bafd-c535d5dd997a" -const groupSailingLovers = "6040aa17-9c64-4fef-9bd0-77234d71bad0" -const groupViolinHaters = "dd58e5ec-842e-498b-8800-61f2ec6f911f" -const groupUsers = "509a9dcd-bb37-4f4f-a01a-19dca27d9cfa" -const groupSysUsers = "34f38767-c937-4eb6-b847-1c175829a2a0" +const ( + userIDEinstein string = "4c510ada-c86b-4815-8820-42cdf82c3d51" + userIDMarie string = "f7fbf8c8-139b-4376-b307-cf0a8c2d0d9c" + userIDFeynman string = "932b4540-8d16-481e-8ef4-588e4b6b151c" + userIDKonnectd string = "820ba2a1-3f54-4538-80a4-2d73007e30bf" + userIDReva string = "bc596f3c-c955-4328-80a0-60d018b4ad57" + userIDMoss string = "058bff95-6708-4fe5-91e4-9ea3d377588b" +) + +const ( + groupPhilosophyHaters = "167cbee2-0518-455a-bfb2-031fe0621e5d" + groupPhysicsLovers = "262982c1-2362-4afa-bfdf-8cbfef64a06e" + groupPoloniumLovers = "cedc21aa-4072-4614-8676-fa9165f598ff" + groupQuantumLovers = "a1726108-01f8-4c30-88df-2b1a9d1cba1a" + groupRadiumLovers = "7b87fd49-286e-4a5f-bafd-c535d5dd997a" + groupSailingLovers = "6040aa17-9c64-4fef-9bd0-77234d71bad0" + groupViolinHaters = "dd58e5ec-842e-498b-8800-61f2ec6f911f" + groupUsers = "509a9dcd-bb37-4f4f-a01a-19dca27d9cfa" + groupSysUsers = "34f38767-c937-4eb6-b847-1c175829a2a0" +) var service = grpc.Service{} @@ -67,169 +74,173 @@ var ocsVersions = []string{ocsV1, ocsV2} var formats = []string{"json", "xml"} -const dataPath = "./accounts-store" +var dataPath = createTmpDir() var DefaultUsers = []string{ - userIDEinstein, - userIDKonnectd, - userIDFeynman, - userIDReva, - userIDMarie, - userIDMoss, + userIDEinstein, + userIDKonnectd, + userIDFeynman, + userIDReva, + userIDMarie, + userIDMoss, } var DefaultGroups = []string{ - groupPhilosophyHaters, - groupPhysicsLovers, - groupSysUsers, - groupUsers, - groupSailingLovers, - groupRadiumLovers, - groupQuantumLovers, - groupPoloniumLovers, - groupViolinHaters, + groupPhilosophyHaters, + groupPhysicsLovers, + groupSysUsers, + groupUsers, + groupSailingLovers, + groupRadiumLovers, + groupQuantumLovers, + groupPoloniumLovers, + groupViolinHaters, } func getFormatString(format string) string { - if format == "json" { - return "?format=json" - } else if format == "xml" { - return "" - } else { - panic("Invalid format received") - } + if format == "json" { + return "?format=json" + } else if format == "xml" { + return "" + } else { + panic("Invalid format received") + } +} + +func createTmpDir() string { + name, err := ioutil.TempDir("/var/tmp", "ocis-accounts-store-") + if err != nil { + panic(err) + } + + return name } type Quota struct { - Free int64 `json:"free" xml:"free"` - Used int64 `json:"used" xml:"used"` - Total int64 `json:"total" xml:"total"` - Relative float32 `json:"relative" xml:"relative"` - Definition string `json:"definition" xml:"definition"` + Free int64 `json:"free" xml:"free"` + Used int64 `json:"used" xml:"used"` + Total int64 `json:"total" xml:"total"` + Relative float32 `json:"relative" xml:"relative"` + Definition string `json:"definition" xml:"definition"` } type User struct { - Enabled string `json:"enabled" xml:"enabled"` - ID string `json:"id" xml:"id"` - Username string `json:"username" xml:"username"` - Email string `json:"email" xml:"email"` - Password string `json:"-" xml:"-"` - Quota Quota `json:"quota" xml:"quota"` - UIDNumber int `json:"uidnumber" xml:"uidnumber"` - GIDNumber int `json:"gidnumber" xml:"gidnumber"` - Displayname string `json:"displayname" xml:"displayname"` + Enabled string `json:"enabled" xml:"enabled"` + ID string `json:"id" xml:"id"` + Email string `json:"email" xml:"email"` + Password string `json:"-" xml:"-"` + Quota Quota `json:"quota" xml:"quota"` + UIDNumber int `json:"uidnumber" xml:"uidnumber"` + GIDNumber int `json:"gidnumber" xml:"gidnumber"` + Displayname string `json:"displayname" xml:"displayname"` } func (u *User) getUserRequestString() string { - res := url.Values{} + res := url.Values{} - if u.Password != "" { - res.Add("password", u.Password) - } + if u.Password != "" { + res.Add("password", u.Password) + } - if u.ID != "" { - res.Add("userid", u.ID) - } + if u.ID != "" { + res.Add("userid", u.ID) + } - if u.Username != "" { - res.Add("username", u.Username) - } + if u.Email != "" { + res.Add("email", u.Email) + } - if u.Email != "" { - res.Add("email", u.Email) - } + if u.Displayname != "" { + res.Add("displayname", u.Displayname) + } - if u.Displayname != "" { - res.Add("displayname", u.Displayname) - } + if u.UIDNumber != 0 { + res.Add("uidnumber", fmt.Sprint(u.UIDNumber)) + } - if u.UIDNumber != 0 { - res.Add("uidnumber", fmt.Sprint(u.UIDNumber)) - } + if u.GIDNumber != 0 { + res.Add("gidnumber", fmt.Sprint(u.GIDNumber)) + } - if u.GIDNumber != 0 { - res.Add("gidnumber", fmt.Sprint(u.GIDNumber)) - } - - return res.Encode() + return res.Encode() } type Group struct { - ID string `json:"id" xml:"id"` - GIDNumber int `json:"gidnumber" xml:"gidnumber"` - Displayname string `json:"displayname" xml:"displayname"` + ID string `json:"id" xml:"id"` + GIDNumber int `json:"gidnumber" xml:"gidnumber"` + Displayname string `json:"displayname" xml:"displayname"` } func (g *Group) getGroupRequestString() string { - res := url.Values{} + res := url.Values{} - if g.ID != "" { - res.Add("groupid", g.ID) - } + if g.ID != "" { + res.Add("groupid", g.ID) + } - if g.Displayname != "" { - res.Add("displayname", g.Displayname) - } - if g.GIDNumber != 0 { - res.Add("gidnumber", fmt.Sprint(g.GIDNumber)) - } + if g.Displayname != "" { + res.Add("displayname", g.Displayname) + } + if g.GIDNumber != 0 { + res.Add("gidnumber", fmt.Sprint(g.GIDNumber)) + } - return res.Encode() + return res.Encode() } type Meta struct { - Status string `json:"status" xml:"status"` - StatusCode int `json:"statuscode" xml:"statuscode"` - Message string `json:"message" xml:"message"` + Status string `json:"status" xml:"status"` + StatusCode int `json:"statuscode" xml:"statuscode"` + Message string `json:"message" xml:"message"` } func (m *Meta) Success(ocsVersion string) bool { - if !(ocsVersion == ocsV1 || ocsVersion == ocsV2) { - return false - } - if m.Status != "ok" { - return false - } - if ocsVersion == ocsV1 && m.StatusCode != 100 { - return false - } else if ocsVersion == ocsV2 && m.StatusCode != 200 { - return false - } else { - return true - } + if !(ocsVersion == ocsV1 || ocsVersion == ocsV2) { + return false + } + if m.Status != "ok" { + return false + } + if ocsVersion == ocsV1 && m.StatusCode != 100 { + return false + } else if ocsVersion == ocsV2 && m.StatusCode != 200 { + return false + } else { + return true + } } type SingleUserResponse struct { - Ocs struct { - Meta Meta `json:"meta" xml:"meta"` - Data User `json:"data" xml:"data"` - } `json:"ocs" xml:"ocs"` + Ocs struct { + Meta Meta `json:"meta" xml:"meta"` + Data User `json:"data" xml:"data"` + } `json:"ocs" xml:"ocs"` } type GetUsersResponse struct { - Ocs struct { - Meta Meta `json:"meta" xml:"meta"` - Data struct { - Users []string `json:"users" xml:"users>element"` - } `json:"data" xml:"data"` - } `json:"ocs" xml:"ocs"` + Ocs struct { + Meta Meta `json:"meta" xml:"meta"` + Data struct { + Users []string `json:"users" xml:"users>element"` + } `json:"data" xml:"data"` + } `json:"ocs" xml:"ocs"` } type EmptyResponse struct { - Ocs struct { - Meta Meta `json:"meta" xml:"meta"` - Data struct { - } `json:"data" xml:"data"` - } `json:"ocs" xml:"ocs"` + Ocs struct { + Meta Meta `json:"meta" xml:"meta"` + Data struct { + } `json:"data" xml:"data"` + } `json:"ocs" xml:"ocs"` } type GetUsersGroupsResponse struct { - Ocs struct { - Meta Meta `json:"meta" xml:"meta"` - Data struct { - Groups []string `json:"groups" xml:"groups>element"` - } `json:"data" xml:"data"` - } `json:"ocs" xml:"ocs"` + Ocs struct { + Meta Meta `json:"meta" xml:"meta"` + Data struct { + Groups []string `json:"groups" xml:"groups>element"` + } `json:"data" xml:"data"` + } `json:"ocs" xml:"ocs"` } type OcsConfig struct { @@ -248,1568 +259,1476 @@ type GetConfigResponse struct { } func assertStatusCode(t *testing.T, statusCode int, res *httptest.ResponseRecorder, ocsVersion string) { - if ocsVersion == ocsV1 { - assert.Equal(t, 200, res.Code) - } else { - assert.Equal(t, statusCode, res.Code) - } + if ocsVersion == ocsV1 { + assert.Equal(t, 200, res.Code) + } else { + assert.Equal(t, statusCode, res.Code) + } } type GetGroupsResponse struct { - Ocs struct { - Meta Meta `json:"meta" xml:"meta"` - Data struct { - Groups []string `json:"groups" xml:"groups>element"` - } `json:"data" xml:"data"` - } `json:"ocs" xml:"ocs"` + Ocs struct { + Meta Meta `json:"meta" xml:"meta"` + Data struct { + Groups []string `json:"groups" xml:"groups>element"` + } `json:"data" xml:"data"` + } `json:"ocs" xml:"ocs"` } type GetGroupMembersResponse struct { - Ocs struct { - Meta Meta `json:"meta" xml:"meta"` - Data struct { - Users []string `json:"users" xml:"users>element"` - } `json:"data" xml:"data"` - } `json:"ocs" xml:"ocs"` + Ocs struct { + Meta Meta `json:"meta" xml:"meta"` + Data struct { + Users []string `json:"users" xml:"users>element"` + } `json:"data" xml:"data"` + } `json:"ocs" xml:"ocs"` } func assertResponseMeta(t *testing.T, expected, actual Meta) { - assert.Equal(t, expected.Status, actual.Status, "The status of response doesn't matches") - assert.Equal(t, expected.StatusCode, actual.StatusCode, "The Status code of response doesn't matches") - assert.Equal(t, expected.Message, actual.Message, "The Message of response doesn't matches") + assert.Equal(t, expected.Status, actual.Status, "The status of response doesn't match") + assert.Equal(t, expected.StatusCode, actual.StatusCode, "The Status code of response doesn't match") + assert.Equal(t, expected.Message, actual.Message, "The Message of response doesn't match") } func assertUserSame(t *testing.T, expected, actual User, quotaAvailable bool) { - if expected.ID == "" { - // Check the auto generated userId - assert.Regexp( - t, - "[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}", - actual.ID, "the userid is not a valid uuid", - ) - } else { - assert.Equal(t, expected.ID, actual.ID, "UserId doesn't match for user %v", expected.Username) - } - assert.Equal(t, expected.Username, actual.Username, "Username doesn't match for user %v", expected.Username) - assert.Equal(t, expected.Email, actual.Email, "email doesn't match for user %v", expected.Username) - assert.Equal(t, expected.Enabled, actual.Enabled, "enabled doesn't match for user %v", expected.Username) - assert.Equal(t, expected.Displayname, actual.Displayname, "displayname doesn't match for user %v", expected.Username) - if quotaAvailable { - assert.NotZero(t, actual.Quota.Free) - assert.NotZero(t, actual.Quota.Used) - assert.NotZero(t, actual.Quota.Total) - assert.Equal(t, "default", actual.Quota.Definition) - } else { - assert.Equal(t, expected.Quota, actual.Quota, "Quota match for user %v", expected.Username) - } + if expected.ID == "" { + // Check the auto generated userId + assert.Regexp( + t, + "[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}", + actual.ID, "the userid is not a valid uuid", + ) + } else { + assert.Equal(t, expected.ID, actual.ID, "UserId doesn't match for user %v", expected.ID) + } + assert.Equal(t, expected.Email, actual.Email, "email doesn't match for user %v", expected.ID) + assert.Equal(t, expected.Enabled, actual.Enabled, "enabled doesn't match for user %v", expected.ID) + if expected.Displayname == "" { + assert.Equal(t, expected.ID, actual.Displayname, "displayname doesn't match for user %v", expected.ID) + } else { + assert.Equal(t, expected.Displayname, actual.Displayname, "displayname doesn't match for user %v", expected.ID) + } + if quotaAvailable { + assert.NotZero(t, actual.Quota.Free) + assert.NotZero(t, actual.Quota.Used) + assert.NotZero(t, actual.Quota.Total) + assert.Equal(t, "default", actual.Quota.Definition) + } else { + assert.Equal(t, expected.Quota, actual.Quota, "Quota match for user %v", expected.ID) + } - // FIXME: gidnumber and Uidnumber are always 0 - // https://github.com/owncloud/ocis/ocs/issues/45 - assert.Equal(t, 0, actual.UIDNumber, "UidNumber doesn't match for user %v", expected.Username) - assert.Equal(t, 0, actual.GIDNumber, "GIDNumber doesn't match for user %v", expected.Username) + if expected.UIDNumber != 0 { + assert.Equal(t, expected.UIDNumber, actual.UIDNumber, "UidNumber doesn't match for user %s", expected.ID) + } + if expected.GIDNumber != 0 { + assert.Equal(t, expected.GIDNumber, actual.GIDNumber, "GidNumber doesn't match for user %s", expected.ID) + } } func deleteAccount(t *testing.T, id string) (*empty.Empty, error) { - client := service.Client() - cl := accountsProto.NewAccountsService("com.owncloud.api.accounts", client) + cl := accountsProto.NewAccountsService("com.owncloud.api.accounts", service.Client()) - req := &accountsProto.DeleteAccountRequest{Id: id} - res, err := cl.DeleteAccount(context.Background(), req) - return res, err + req := &accountsProto.DeleteAccountRequest{Id: id} + res, err := cl.DeleteAccount(context.Background(), req) + return res, err } func deleteGroup(t *testing.T, id string) (*empty.Empty, error) { - client := service.Client() - cl := accountsProto.NewGroupsService("com.owncloud.api.accounts", client) + cl := accountsProto.NewGroupsService("com.owncloud.api.accounts", service.Client()) - req := &accountsProto.DeleteGroupRequest{Id: id} - res, err := cl.DeleteGroup(context.Background(), req) - return res, err + req := &accountsProto.DeleteGroupRequest{Id: id} + res, err := cl.DeleteGroup(context.Background(), req) + return res, err } func buildRoleServiceMock() settings.RoleService { - return settings.MockRoleService{ - AssignRoleToUserFunc: func(ctx context.Context, req *settings.AssignRoleToUserRequest, opts ...client.CallOption) (res *settings.AssignRoleToUserResponse, err error) { - mockedRoleAssignment[req.AccountUuid] = req.RoleId - return &settings.AssignRoleToUserResponse{ - Assignment: &settings.UserRoleAssignment{ - AccountUuid: req.AccountUuid, - RoleId: req.RoleId, - }, - }, nil - }, - } + return settings.MockRoleService{ + AssignRoleToUserFunc: func(ctx context.Context, req *settings.AssignRoleToUserRequest, opts ...client.CallOption) (res *settings.AssignRoleToUserResponse, err error) { + mockedRoleAssignment[req.AccountUuid] = req.RoleId + return &settings.AssignRoleToUserResponse{ + Assignment: &settings.UserRoleAssignment{ + AccountUuid: req.AccountUuid, + RoleId: req.RoleId, + }, + }, nil + }, + } } func init() { - service = grpc.NewService( - grpc.Namespace("com.owncloud.api"), - grpc.Name("accounts"), - grpc.Address("localhost:9180"), - ) + service = grpc.NewService( + grpc.Namespace("com.owncloud.api"), + grpc.Name("accounts"), + grpc.Address("localhost:9180"), + ) - c := &accountsCfg.Config{ - Server: accountsCfg.Server{ - AccountsDataPath: dataPath, - }, - Repo: accountsCfg.Repo{ - Disk: accountsCfg.Disk{ - Path: dataPath, + c := &accountsCfg.Config{ + Server: accountsCfg.Server{ + AccountsDataPath: dataPath, }, - }, - Log: accountsCfg.Log{ - Level: "info", - Pretty: true, - Color: true, - }, - } + Repo: accountsCfg.Repo{ + Disk: accountsCfg.Disk{ + Path: dataPath, + }, + }, + Log: accountsCfg.Log{ + Level: "info", + Pretty: true, + Color: true, + }, + } - var hdlr *accountsSvc.Service - var err error + var hdlr *accountsSvc.Service + var err error - if hdlr, err = accountsSvc.New( - accountsSvc.Logger(accountsCmd.NewLogger(c)), - accountsSvc.Config(c), - accountsSvc.RoleService(buildRoleServiceMock())); err != nil { - log.Fatalf("Could not create new service") - } + if hdlr, err = accountsSvc.New( + accountsSvc.Logger(accountsCmd.NewLogger(c)), + accountsSvc.Config(c), + accountsSvc.RoleService(buildRoleServiceMock())); err != nil { + log.Fatalf("Could not create new service") + } - err = accountsProto.RegisterAccountsServiceHandler(service.Server(), hdlr) - if err != nil { - log.Fatal("could not register the Accounts handler") - } - err = accountsProto.RegisterGroupsServiceHandler(service.Server(), hdlr) - if err != nil { - log.Fatal("could not register the Groups handler") - } + err = accountsProto.RegisterAccountsServiceHandler(service.Server(), hdlr) + if err != nil { + log.Fatal("could not register the Accounts handler") + } + err = accountsProto.RegisterGroupsServiceHandler(service.Server(), hdlr) + if err != nil { + log.Fatal("could not register the Groups handler") + } - err = service.Server().Start() - if err != nil { - log.Fatalf("could not start server: %v", err) - } + err = service.Server().Start() + if err != nil { + log.Fatalf("could not start server: %v", err) + } } func cleanUp(t *testing.T) { - datastore := filepath.Join(dataPath, "accounts") + datastore := filepath.Join(dataPath, "accounts") - files, err := ioutil.ReadDir(datastore) - if err != nil { - log.Fatal(err) - } + files, err := ioutil.ReadDir(datastore) + if err != nil { + log.Fatal(err) + } - for _, f := range files { - found := false - for _, defUser := range DefaultUsers { - if f.Name() == defUser { - found = true - break - } - } + for _, f := range files { + found := false + for _, defUser := range DefaultUsers { + if f.Name() == defUser { + found = true + break + } + } - if !found { - deleteAccount(t, f.Name()) - } - } + if !found { + deleteAccount(t, f.Name()) + } + } - datastoreGroups := filepath.Join(dataPath, "groups") + datastoreGroups := filepath.Join(dataPath, "groups") - files, err = ioutil.ReadDir(datastoreGroups) - if err != nil { - log.Fatal(err) - } + files, err = ioutil.ReadDir(datastoreGroups) + if err != nil { + log.Fatal(err) + } - for _, f := range files { - found := false - for _, defGrp := range DefaultGroups { - if f.Name() == defGrp { - found = true - break - } - } + for _, f := range files { + found := false + for _, defGrp := range DefaultGroups { + if f.Name() == defGrp { + found = true + break + } + } - if !found { - deleteGroup(t, f.Name()) - } - } + if !found { + deleteGroup(t, f.Name()) + } + } } func sendRequest(method, endpoint, body, auth string) (*httptest.ResponseRecorder, error) { - var reader = strings.NewReader(body) - req, err := http.NewRequest(method, endpoint, reader) - if err != nil { - return nil, err - } - req.Header.Set("Content-Type", "application/x-www-form-urlencoded") + var reader = strings.NewReader(body) + req, err := http.NewRequest(method, endpoint, reader) + if err != nil { + return nil, err + } + req.Header.Set("Content-Type", "application/x-www-form-urlencoded") - if auth != "" { - req.Header.Set("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(auth))) - } + if auth != "" { + req.Header.Set("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(auth))) + } - rr := httptest.NewRecorder() + rr := httptest.NewRecorder() - service := getService() - service.ServeHTTP(rr, req) + service := getService() + service.ServeHTTP(rr, req) - return rr, nil + return rr, nil } func getService() svc.Service { - c := &config.Config{ - HTTP: config.HTTP{ - Root: "/", - Addr: "localhost:9110", - }, - TokenManager: config.TokenManager{ - JWTSecret: "HELLO-secret", - }, - Log: config.Log{ - Level: "debug", - }, - } + c := &config.Config{ + HTTP: config.HTTP{ + Root: "/", + Addr: "localhost:9110", + }, + TokenManager: config.TokenManager{ + JWTSecret: "HELLO-secret", + }, + Log: config.Log{ + Level: "debug", + }, + } - var logger ocisLog.Logger + var logger ocisLog.Logger - svc := svc.NewService( - svc.Logger(logger), - svc.Config(c), - ) - - return svc + return svc.NewService( + svc.Logger(logger), + svc.Config(c), + ) } func createUser(u User) error { - _, err := sendRequest( - "POST", - userProvisioningEndPoint, - u.getUserRequestString(), - adminBasicAuth, - ) + _, err := sendRequest( + "POST", + userProvisioningEndPoint, + u.getUserRequestString(), + adminBasicAuth, + ) - if err != nil { - return err - } - return nil + if err != nil { + return err + } + return nil } func createGroup(g Group) error { //lint:file-ignore U1000 not implemented - _, err := sendRequest( - "POST", - groupProvisioningEndPoint, - g.getGroupRequestString(), - adminBasicAuth, - ) + _, err := sendRequest( + "POST", + groupProvisioningEndPoint, + g.getGroupRequestString(), + adminBasicAuth, + ) - if err != nil { - return err - } - return nil + if err != nil { + return err + } + return nil } func TestCreateUser(t *testing.T) { - testData := []struct { - user User - err *Meta - }{ - // A simple user - { - User{ - Enabled: "true", - Username: "rutherford", - ID: "rutherford", - Email: "rutherford@example.com", - Displayname: "ErnestRutherFord", - Password: "newPassword", - }, - nil, - }, + scenarios := []struct { + name string + user User + err *Meta + }{ + { + "A simple user", + User{ + Enabled: "true", + ID: "rutherford", + Email: "rutherford@example.com", + Displayname: "ErnestRutherFord", + Password: "newPassword", + }, + nil, + }, + { + "User with Uid and Gid defined", + User{ + Enabled: "true", + ID: "thomson", + Email: "thomson@example.com", + Displayname: "J. J. Thomson", + UIDNumber: 20027, + GIDNumber: 30000, + Password: "newPassword", + }, + nil, + }, + // https://github.com/owncloud/ocis-ocs/issues/50 + { + "User without password", + User{ + Enabled: "true", + ID: "john", + Email: "john@example.com", + Displayname: "John Dalton", + }, + nil, + }, + // https://github.com/owncloud/ocis-ocs/issues/49 + { + "User with special character in userid", + User{ + Enabled: "true", + ID: "schrödinger", + Email: "schrödinger@example.com", + Displayname: "Erwin Schrödinger", + Password: "newPassword", + }, + &Meta{ + Status: "error", + StatusCode: 400, + Message: "preferred_name 'schrödinger' must be at least the local part of an email", + }, + }, + { + "User with different userid and email", + User{ + Enabled: "true", + ID: "planck", + Email: "max@example.com", + Displayname: "Max Planck", + Password: "newPassword", + }, + nil, + }, + { + "User without displayname", + User{ + Enabled: "true", + ID: "oppenheimer", + Email: "robert@example.com", + Password: "newPassword", + }, + nil, + }, + { + "User wit invalid email", + User{ + Enabled: "true", + ID: "chadwick", + Email: "not_a_email", + Password: "newPassword", + }, + &Meta{ + Status: "error", + StatusCode: 400, + Message: "mail 'not_a_email' must be a valid email", + }, + }, + { + "User without email", + User{ + Enabled: "true", + ID: "chadwick", + Password: "newPassword", + }, + &Meta{ + Status: "error", + StatusCode: 400, + Message: "mail '' must be a valid email", + }, + }, + { + "User without userid", + User{ + Enabled: "true", + Email: "james@example.com", + Password: "newPassword", + }, + &Meta{ + Status: "error", + StatusCode: 400, + Message: "preferred_name '' must be at least the local part of an email", + }, + }, + } - // User with Uid and Gid defined - { - User{ - Enabled: "true", - Username: "thomson", - ID: "thomson", - Email: "thomson@example.com", - Displayname: "J. J. Thomson", - UIDNumber: 20027, - GIDNumber: 30000, - Password: "newPassword", - }, - nil, - }, + for _, ocsVersion := range ocsVersions { + for _, format := range formats { + for _, scenario := range scenarios { + t.Run(fmt.Sprintf("%s (ocs=%s, format=%s)", scenario.name, ocsVersion, format), func(t *testing.T) { + formatpart := getFormatString(format) + res, err := sendRequest( + "POST", + fmt.Sprintf("/%v/cloud/users%v", ocsVersion, formatpart), + scenario.user.getUserRequestString(), + adminBasicAuth, + ) + assert.NoError(t, err) - // User with different username and Id - { - User{ - Enabled: "true", - Username: "niels", - ID: "bohr", - Email: "bohr@example.com", - Displayname: "Niels Bohr", - Password: "newPassword", - }, - nil, - }, + var response SingleUserResponse + if format == "json" { + err = json.Unmarshal(res.Body.Bytes(), &response) + assert.NoError(t, err) + } else { + err = xml.Unmarshal(res.Body.Bytes(), &response.Ocs) + assert.NoError(t, err) + } - // User without password - // https://github.com/owncloud/ocis/ocs/issues/50 - { - User{ - Enabled: "true", - Username: "john", - ID: "john", - Email: "john@example.com", - Displayname: "John Dalton", - }, - nil, - }, + if scenario.err == nil { + assert.True(t, response.Ocs.Meta.Success(ocsVersion), unsuccessfulResponseText) + assertStatusCode(t, 200, res, ocsVersion) + assertUserSame(t, scenario.user, response.Ocs.Data, false) + } else { + assertStatusCode(t, 400, res, ocsVersion) + assertResponseMeta(t, *scenario.err, response.Ocs.Meta) + } - // User with special character in username - // https://github.com/owncloud/ocis/ocs/issues/49 - { - User{ - Enabled: "true", - Username: "schrödinger", - ID: "schrödinger", - Email: "schrödinger@example.com", - Displayname: "Erwin Schrödinger", - Password: "newPassword", - }, - &Meta{ - Status: "error", - StatusCode: 400, - Message: "preferred_name 'schrödinger' must be at least the local part of an email", - }, - }, + var id string + if scenario.user.ID != "" { + id = scenario.user.ID + } else { + id = response.Ocs.Data.ID + } - // User with different userid and email - { - User{ - Enabled: "true", - Username: "planck", - ID: "planck", - Email: "max@example.com", - Displayname: "Max Planck", - Password: "newPassword", - }, - nil, - }, + res, err = sendRequest( + "GET", + userProvisioningEndPoint, + "", + adminBasicAuth, + ) + assert.NoError(t, err) - // User with different userid and email and username - { - User{ - Enabled: "true", - Username: "hisenberg", - ID: "hberg", - Email: "werner@example.com", - Displayname: "Werner Hisenberg", - Password: "newPassword", - }, - nil, - }, + var usersResponse GetUsersResponse + err = json.Unmarshal(res.Body.Bytes(), &usersResponse) + assert.NoError(t, err) - // User without displayname - { - User{ - Enabled: "true", - Username: "oppenheimer", - ID: "oppenheimer", - Email: "robert@example.com", - Password: "newPassword", - }, - nil, - }, + assert.True(t, usersResponse.Ocs.Meta.Success(ocsV1), unsuccessfulResponseText) - // User wit invalid email - { - User{ - Enabled: "true", - Username: "chadwick", - ID: "chadwick", - Email: "not_a_email", - Password: "newPassword", - }, - &Meta{ - Status: "error", - StatusCode: 400, - Message: "mail 'not_a_email' must be a valid email", - }, - }, - - // User without email - { - User{ - Enabled: "true", - Username: "chadwick", - ID: "chadwick", - Password: "newPassword", - }, - &Meta{ - Status: "error", - StatusCode: 400, - Message: "mail '' must be a valid email", - }, - }, - - // User without username - { - User{ - Enabled: "true", - ID: "chadwick", - Email: "james@example.com", - Password: "newPassword", - }, - &Meta{ - Status: "error", - StatusCode: 400, - Message: "preferred_name '' must be at least the local part of an email", - }, - }, - - // User without userid - { - User{ - Enabled: "true", - Username: "chadwick", - Email: "james@example.com", - Password: "newPassword", - }, - nil, - }, - } - - for _, ocsVersion := range ocsVersions { - for _, format := range formats { - for _, data := range testData { - formatpart := getFormatString(format) - res, err := sendRequest( - "POST", - fmt.Sprintf("/%v/cloud/users%v", ocsVersion, formatpart), - data.user.getUserRequestString(), - adminBasicAuth, - ) - - if err != nil { - t.Fatal(err) - } - - var response SingleUserResponse - - if format == "json" { - if err := json.Unmarshal(res.Body.Bytes(), &response); err != nil { - t.Fatal(err) - } - } else { - if err := xml.Unmarshal(res.Body.Bytes(), &response.Ocs); err != nil { - t.Fatal(err) - } - } - - if data.err == nil { - assert.True(t, response.Ocs.Meta.Success(ocsVersion), unsuccessfulResponseText) - assertStatusCode(t, 200, res, ocsVersion) - assertUserSame(t, data.user, response.Ocs.Data, false) - } else { - assertStatusCode(t, 400, res, ocsVersion) - assertResponseMeta(t, *data.err, response.Ocs.Meta) - } - - var id string - if data.user.ID != "" { - id = data.user.ID - } else { - id = response.Ocs.Data.ID - } - - res, err = sendRequest( - "GET", - userProvisioningEndPoint, - "", - adminBasicAuth, - ) - - if err != nil { - t.Fatal(err) - } - - var usersResponse GetUsersResponse - if err := json.Unmarshal(res.Body.Bytes(), &usersResponse); err != nil { - t.Fatal(err) - } - - assert.True(t, usersResponse.Ocs.Meta.Success(ocsV1), unsuccessfulResponseText) - - if data.err == nil { - assert.Contains(t, usersResponse.Ocs.Data.Users, id) - } else { - assert.NotContains(t, usersResponse.Ocs.Data.Users, data.user.ID) - } - } - cleanUp(t) - } - } + if scenario.err == nil { + assert.Contains(t, usersResponse.Ocs.Data.Users, id) + } else { + assert.NotContains(t, usersResponse.Ocs.Data.Users, scenario.user.ID) + } + cleanUp(t) + }) + } + } + } } func TestGetUsers(t *testing.T) { - users := []User{ - { - Enabled: "true", - Username: "rutherford", - ID: "rutherford", - Email: "rutherford@example.com", - Displayname: "Ernest RutherFord", - }, - { - Enabled: "true", - Username: "thomson", - ID: "thomson", - Email: "thomson@example.com", - Displayname: "J. J. Thomson", - }, - } + users := []User{ + { + Enabled: "true", + ID: "rutherford", + Email: "rutherford@example.com", + Displayname: "Ernest RutherFord", + }, + { + Enabled: "true", + ID: "thomson", + Email: "thomson@example.com", + Displayname: "J. J. Thomson", + }, + } - for _, ocsVersion := range ocsVersions { - for _, format := range formats { - for _, user := range users { - err := createUser(user) - if err != nil { - t.Fatal(err) - } - } + for _, ocsVersion := range ocsVersions { + for _, format := range formats { + for _, user := range users { + err := createUser(user) + if err != nil { + t.Fatal(err) + } + } - formatpart := getFormatString(format) - res, err := sendRequest( - "GET", - fmt.Sprintf("/%v/cloud/users%v", ocsVersion, formatpart), - "", - adminBasicAuth, - ) + formatpart := getFormatString(format) + res, err := sendRequest( + "GET", + fmt.Sprintf("/%v/cloud/users%v", ocsVersion, formatpart), + "", + adminBasicAuth, + ) - if err != nil { - t.Fatal(err) - } + if err != nil { + t.Fatal(err) + } - var response GetUsersResponse + var response GetUsersResponse - if format == "json" { - if err := json.Unmarshal(res.Body.Bytes(), &response); err != nil { - t.Fatal(err) - } - } else { - if err := xml.Unmarshal(res.Body.Bytes(), &response.Ocs); err != nil { - t.Fatal(err) - } - } + if format == "json" { + if err := json.Unmarshal(res.Body.Bytes(), &response); err != nil { + t.Fatal(err) + } + } else { + if err := xml.Unmarshal(res.Body.Bytes(), &response.Ocs); err != nil { + t.Fatal(err) + } + } - assertStatusCode(t, 200, res, ocsVersion) - assert.True(t, response.Ocs.Meta.Success(ocsVersion), unsuccessfulResponseText) - for _, user := range users { - assert.Contains(t, response.Ocs.Data.Users, user.Username) - } - cleanUp(t) - } - } + assertStatusCode(t, 200, res, ocsVersion) + assert.True(t, response.Ocs.Meta.Success(ocsVersion), unsuccessfulResponseText) + for _, user := range users { + assert.Contains(t, response.Ocs.Data.Users, user.ID) + } + cleanUp(t) + } + } } func TestGetUsersDefaultUsers(t *testing.T) { - for _, ocsVersion := range ocsVersions { - for _, format := range formats { - formatpart := getFormatString(format) - res, err := sendRequest( - "GET", - fmt.Sprintf("/%v/cloud/users%v", ocsVersion, formatpart), - "", - adminBasicAuth, - ) + for _, ocsVersion := range ocsVersions { + for _, format := range formats { + formatpart := getFormatString(format) + res, err := sendRequest( + "GET", + fmt.Sprintf("/%v/cloud/users%v", ocsVersion, formatpart), + "", + adminBasicAuth, + ) - if err != nil { - t.Fatal(err) - } + if err != nil { + t.Fatal(err) + } - var response GetUsersResponse + var response GetUsersResponse - if format == "json" { - if err := json.Unmarshal(res.Body.Bytes(), &response); err != nil { - t.Fatal(err) - } - } else { - if err := xml.Unmarshal(res.Body.Bytes(), &response.Ocs); err != nil { - t.Fatal(err) - } - } + if format == "json" { + if err := json.Unmarshal(res.Body.Bytes(), &response); err != nil { + t.Fatal(err) + } + } else { + if err := xml.Unmarshal(res.Body.Bytes(), &response.Ocs); err != nil { + t.Fatal(err) + } + } - assertStatusCode(t, 200, res, ocsVersion) - assert.True(t, response.Ocs.Meta.Success(ocsVersion), unsuccessfulResponseText) - for _, user := range DefaultUsers { - assert.Contains(t, response.Ocs.Data.Users, user) - } - cleanUp(t) - } - } + assertStatusCode(t, 200, res, ocsVersion) + assert.True(t, response.Ocs.Meta.Success(ocsVersion), unsuccessfulResponseText) + for _, user := range DefaultUsers { + assert.Contains(t, response.Ocs.Data.Users, user) + } + cleanUp(t) + } + } } func TestGetUser(t *testing.T) { - users := []User{ - { - Enabled: "true", - Username: "rutherford", - ID: "rutherford", - Email: "rutherford@example.com", - Displayname: "Ernest RutherFord", - }, - { - Enabled: "true", - Username: "thomson", - ID: "thomson", - Email: "thomson@example.com", - Displayname: "J. J. Thomson", - }, - } + users := []User{ + { + Enabled: "true", + ID: "rutherford", + Email: "rutherford@example.com", + Displayname: "Ernest RutherFord", + }, + { + Enabled: "true", + ID: "thomson", + Email: "thomson@example.com", + Displayname: "J. J. Thomson", + }, + } - for _, ocsVersion := range ocsVersions { - for _, format := range formats { + for _, ocsVersion := range ocsVersions { + for _, format := range formats { - for _, user := range users { - err := createUser(user) - if err != nil { - t.Fatal(err) - } - } - formatpart := getFormatString(format) - for _, user := range users { - res, err := sendRequest( - "GET", - fmt.Sprintf("/%s/cloud/users/%s%s", ocsVersion, user.ID, formatpart), - "", - adminBasicAuth, - ) + for _, user := range users { + err := createUser(user) + if err != nil { + t.Fatal(err) + } + } + formatpart := getFormatString(format) + for _, user := range users { + res, err := sendRequest( + "GET", + fmt.Sprintf("/%s/cloud/users/%s%s", ocsVersion, user.ID, formatpart), + "", + adminBasicAuth, + ) - if err != nil { - t.Fatal(err) - } + if err != nil { + t.Fatal(err) + } - var response SingleUserResponse - if format == "json" { - if err := json.Unmarshal(res.Body.Bytes(), &response); err != nil { - t.Fatal(err) - } - } else { - if err := xml.Unmarshal(res.Body.Bytes(), &response.Ocs); err != nil { - t.Fatal(err) - } - } + var response SingleUserResponse + if format == "json" { + if err := json.Unmarshal(res.Body.Bytes(), &response); err != nil { + t.Fatal(err) + } + } else { + if err := xml.Unmarshal(res.Body.Bytes(), &response.Ocs); err != nil { + t.Fatal(err) + } + } - assertStatusCode(t, 200, res, ocsVersion) - assert.True(t, response.Ocs.Meta.Success(ocsVersion), "The response was expected to pass but it failed") - assertUserSame(t, user, response.Ocs.Data, true) - } - cleanUp(t) - } - } + assertStatusCode(t, 200, res, ocsVersion) + assert.True(t, response.Ocs.Meta.Success(ocsVersion), "The response was expected to pass but it failed") + assertUserSame(t, user, response.Ocs.Data, true) + } + cleanUp(t) + } + } } func TestGetUserInvalidId(t *testing.T) { - invalidUsers := []string{ - "1", - "invalid", - "3434234233", - "1am41validUs3r", - "_-@@--$$__", - } - for _, ocsVersion := range ocsVersions { - for _, format := range formats { - formatpart := getFormatString(format) - for _, user := range invalidUsers { - res, err := sendRequest( - "GET", - fmt.Sprintf("/%s/cloud/user/%s%s", ocsVersion, user, formatpart), - "", - adminBasicAuth, - ) + invalidUsers := []string{ + "1", + "invalid", + "3434234233", + "1am41validUs3r", + "_-@@--$$__", + } + for _, ocsVersion := range ocsVersions { + for _, format := range formats { + formatpart := getFormatString(format) + for _, user := range invalidUsers { + res, err := sendRequest( + "GET", + fmt.Sprintf("/%s/cloud/user/%s%s", ocsVersion, user, formatpart), + "", + adminBasicAuth, + ) - if err != nil { - t.Fatal(err) - } + if err != nil { + t.Fatal(err) + } - var response SingleUserResponse - if format == "json" { - if err := json.Unmarshal(res.Body.Bytes(), &response); err != nil { - t.Fatal(err) - } - } else { - if err := xml.Unmarshal(res.Body.Bytes(), &response.Ocs); err != nil { - t.Fatal(err) - } - } + var response SingleUserResponse + if format == "json" { + if err := json.Unmarshal(res.Body.Bytes(), &response); err != nil { + t.Fatal(err) + } + } else { + if err := xml.Unmarshal(res.Body.Bytes(), &response.Ocs); err != nil { + t.Fatal(err) + } + } - assertStatusCode(t, 404, res, ocsVersion) - assert.False(t, response.Ocs.Meta.Success(ocsVersion), "the response was expected to fail but passed") - assertResponseMeta(t, Meta{ - Status: "error", - StatusCode: 998, - Message: "not found", - }, response.Ocs.Meta) - cleanUp(t) - } - } - } + assertStatusCode(t, 404, res, ocsVersion) + assert.False(t, response.Ocs.Meta.Success(ocsVersion), "the response was expected to fail but passed") + assertResponseMeta(t, Meta{ + Status: "error", + StatusCode: 998, + Message: "not found", + }, response.Ocs.Meta) + cleanUp(t) + } + } + } } func TestDeleteUser(t *testing.T) { - users := []User{ - { - Enabled: "true", - Username: "rutherford", - ID: "rutherford", - Email: "rutherford@example.com", - Displayname: "Ernest RutherFord", - }, - { - Enabled: "true", - Username: "thomson", - ID: "thomson", - Email: "thomson@example.com", - Displayname: "J. J. Thomson", - }, - } + users := []User{ + { + Enabled: "true", + ID: "rutherford", + Email: "rutherford@example.com", + Displayname: "Ernest RutherFord", + }, + { + Enabled: "true", + ID: "thomson", + Email: "thomson@example.com", + Displayname: "J. J. Thomson", + }, + } - for _, ocsVersion := range ocsVersions { - for _, format := range formats { - for _, user := range users { - err := createUser(user) - if err != nil { - t.Fatal(err) - } - } + for _, ocsVersion := range ocsVersions { + for _, format := range formats { + for _, user := range users { + err := createUser(user) + if err != nil { + t.Fatal(err) + } + } - formatpart := getFormatString(format) - res, err := sendRequest( - "DELETE", - fmt.Sprintf("/%s/cloud/users/rutherford%s", ocsVersion, formatpart), - "", - adminBasicAuth, - ) + formatpart := getFormatString(format) + res, err := sendRequest( + "DELETE", + fmt.Sprintf("/%s/cloud/users/rutherford%s", ocsVersion, formatpart), + "", + adminBasicAuth, + ) - if err != nil { - t.Fatal(err) - } + if err != nil { + t.Fatal(err) + } - var response EmptyResponse - if format == "json" { - if err := json.Unmarshal(res.Body.Bytes(), &response); err != nil { - t.Fatal(err) - } - } else { - if err := xml.Unmarshal(res.Body.Bytes(), &response.Ocs); err != nil { - t.Fatal(err) - } - } + var response EmptyResponse + if format == "json" { + if err := json.Unmarshal(res.Body.Bytes(), &response); err != nil { + t.Fatal(err) + } + } else { + if err := xml.Unmarshal(res.Body.Bytes(), &response.Ocs); err != nil { + t.Fatal(err) + } + } - assertStatusCode(t, 200, res, ocsVersion) - assert.True(t, response.Ocs.Meta.Success(ocsVersion), unsuccessfulResponseText) - assert.Empty(t, response.Ocs.Data) + assertStatusCode(t, 200, res, ocsVersion) + assert.True(t, response.Ocs.Meta.Success(ocsVersion), unsuccessfulResponseText) + assert.Empty(t, response.Ocs.Data) - // Check deleted user doesn't exist and the other user does - res, err = sendRequest( - "GET", - userProvisioningEndPoint, - "", - adminBasicAuth, - ) + // Check deleted user doesn't exist and the other user does + res, err = sendRequest( + "GET", + userProvisioningEndPoint, + "", + adminBasicAuth, + ) - if err != nil { - t.Fatal(err) - } + if err != nil { + t.Fatal(err) + } - var usersResponse GetUsersResponse - if err := json.Unmarshal(res.Body.Bytes(), &usersResponse); err != nil { - t.Fatal(err) - } + var usersResponse GetUsersResponse + if err := json.Unmarshal(res.Body.Bytes(), &usersResponse); err != nil { + t.Fatal(err) + } - assert.True(t, usersResponse.Ocs.Meta.Success(ocsV1), unsuccessfulResponseText) - assert.Contains(t, usersResponse.Ocs.Data.Users, "thomson") - assert.NotContains(t, usersResponse.Ocs.Data.Users, "rutherford") + assert.True(t, usersResponse.Ocs.Meta.Success(ocsV1), unsuccessfulResponseText) + assert.Contains(t, usersResponse.Ocs.Data.Users, "thomson") + assert.NotContains(t, usersResponse.Ocs.Data.Users, "rutherford") - cleanUp(t) - } - } + cleanUp(t) + } + } } func TestDeleteUserInvalidId(t *testing.T) { - invalidUsers := []string{ - "1", - "invalid", - "3434234233", - "1am41validUs3r", - "_-@@--$$__", - } - for _, ocsVersion := range ocsVersions { - for _, format := range formats { - for _, user := range invalidUsers { - formatpart := getFormatString(format) - res, err := sendRequest( - "DELETE", - fmt.Sprintf("/%s/cloud/users/%s%s", ocsVersion, user, formatpart), - "", - adminBasicAuth, - ) + invalidUsers := []string{ + "1", + "invalid", + "3434234233", + "1am41validUs3r", + "_-@@--$$__", + } + for _, ocsVersion := range ocsVersions { + for _, format := range formats { + for _, user := range invalidUsers { + t.Run(fmt.Sprintf("%s (ocs=%s, format=%s)", user, ocsVersion, format), func(t *testing.T) { + formatpart := getFormatString(format) + res, err := sendRequest( + "DELETE", + fmt.Sprintf("/%s/cloud/users/%s%s", ocsVersion, user, formatpart), + "", + adminBasicAuth, + ) + assert.NoError(t, err) - if err != nil { - t.Fatal(err) - } + var response EmptyResponse + if format == "json" { + err = json.Unmarshal(res.Body.Bytes(), &response) + assert.NoError(t, err) + } else { + err = xml.Unmarshal(res.Body.Bytes(), &response.Ocs) + assert.NoError(t, err) + } - var response EmptyResponse - if format == "json" { - if err := json.Unmarshal(res.Body.Bytes(), &response); err != nil { - t.Fatal(err) - } - } else { - if err := xml.Unmarshal(res.Body.Bytes(), &response.Ocs); err != nil { - t.Fatal(err) - } - } + assertStatusCode(t, 404, res, ocsVersion) + assert.False(t, response.Ocs.Meta.Success(ocsVersion), "The response was not expected to be successful but was") + assert.Empty(t, response.Ocs.Data) - assertStatusCode(t, 404, res, ocsVersion) - assert.False(t, response.Ocs.Meta.Success(ocsVersion), "The response was not expected to be successful but was") - assert.Empty(t, response.Ocs.Data) - - assertResponseMeta(t, Meta{ - Status: "error", - StatusCode: 998, - Message: "The requested user could not be found", - }, response.Ocs.Meta) - } - } - } + assertResponseMeta(t, Meta{ + Status: "error", + StatusCode: 998, + Message: "The requested user could not be found", + }, response.Ocs.Meta) + }) + } + } + } } func TestUpdateUser(t *testing.T) { - user := User{ - Enabled: "true", - Username: "rutherford", - ID: "rutherford", - Email: "rutherford@example.com", - Displayname: "Ernest RutherFord", - } + user := User{ + Enabled: "true", + ID: "rutherford", + Email: "rutherford@example.com", + Displayname: "Ernest RutherFord", + } - testData := []struct { - UpdateKey string - UpdateValue string - Error *Meta - }{ - { - "displayname", - "James Chadwick", - nil, - }, - { - "display", - "Neils Bohr", - nil, - }, - { - "email", - "ford@user.org", - nil, - }, - // Invalid email doesn't gives error - // https://github.com/owncloud/ocis/ocs/issues/46 - { - "email", - "not_a_valid_email", - nil, - }, - { - "password", - "strongpass1234", - nil, - }, - { - "username", - "e_rutherford", - nil, - }, - // Empty values doesn't gives error - // https://github.com/owncloud/ocis/ocs/issues/51 - { - "email", - "", - nil, - }, - { - "username", - "", - nil, - }, - { - "password", - "", - nil, - }, + testData := []struct { + UpdateKey string + UpdateValue string + Error *Meta + }{ + { + "displayname", + "James Chadwick", + nil, + }, + { + "display", + "Neils Bohr", + nil, + }, + { + "email", + "ford@user.org", + nil, + }, + { + "email", + "not_a_valid_email", + &Meta{ + Status: "error", + StatusCode: 400, + Message: "mail 'not_a_valid_email' must be a valid email", + }, + }, + { + "password", + "strongpass1234", + nil, + }, + { + "email", + "", + nil, + }, + { + "password", + "", + nil, + }, + // Invalid Keys + { + "invalid_key", + "validvalue", + &Meta{ + Status: "error", + StatusCode: 103, + Message: "unknown key 'invalid_key'", + }, + }, + { + "12345", + "validvalue", + &Meta{ + Status: "error", + StatusCode: 103, + Message: "unknown key '12345'", + }, + }, + { + "", + "validvalue", + &Meta{ + Status: "error", + StatusCode: 103, + Message: "unknown key ''", + }, + }, + { + "", + "", + &Meta{ + Status: "error", + StatusCode: 103, + Message: "unknown key ''", + }, + }, + } - // Invalid Keys - { - "invalid_key", - "validvalue", - &Meta{ - Status: "error", - StatusCode: 103, - Message: "unknown key 'invalid_key'", - }, - }, - { - "12345", - "validvalue", - &Meta{ - Status: "error", - StatusCode: 103, - Message: "unknown key '12345'", - }, - }, - { - "", - "validvalue", - &Meta{ - Status: "error", - StatusCode: 103, - Message: "unknown key ''", - }, - }, - { - "", - "", - &Meta{ - Status: "error", - StatusCode: 103, - Message: "unknown key ''", - }, - }, - } + for _, ocsVersion := range ocsVersions { + for _, format := range formats { + formatpart := getFormatString(format) + for _, data := range testData { + err := createUser(user) + if err != nil { + t.Fatalf("Failed while creating user: %v", err) + } - for _, ocsVersion := range ocsVersions { - for _, format := range formats { - formatpart := getFormatString(format) - for _, data := range testData { - err := createUser(user) - if err != nil { - t.Fatalf("Failed while creating user: %v", err) - } + params := url.Values{} - params := url.Values{} + params.Add("key", data.UpdateKey) + params.Add("value", data.UpdateValue) - params.Add("key", data.UpdateKey) - params.Add("value", data.UpdateValue) + res, err := sendRequest( + "PUT", + fmt.Sprintf("/%s/cloud/users/rutherford%s", ocsVersion, formatpart), + params.Encode(), + adminBasicAuth, + ) - res, err := sendRequest( - "PUT", - fmt.Sprintf("/%s/cloud/users/rutherford%s", ocsVersion, formatpart), - params.Encode(), - adminBasicAuth, - ) + updatedUser := user + switch data.UpdateKey { + case "email": + updatedUser.Email = data.UpdateValue + case "displayname": + updatedUser.Displayname = data.UpdateValue + case "display": + updatedUser.Displayname = data.UpdateValue + } - updatedUser := user - switch data.UpdateKey { - case "username": - updatedUser.Username = data.UpdateValue - case "email": - updatedUser.Email = data.UpdateValue - case "displayname": - updatedUser.Displayname = data.UpdateValue - case "display": - updatedUser.Displayname = data.UpdateValue - } + if err != nil { + t.Fatal(err) + } - if err != nil { - t.Fatal(err) - } + var response struct { + Ocs struct { + Meta Meta `json:"meta" xml:"meta"` + } `json:"ocs" xml:"ocs"` + } - var response struct { - Ocs struct { - Meta Meta `json:"meta" xml:"meta"` - } `json:"ocs" xml:"ocs"` - } + if format == "json" { + if err := json.Unmarshal(res.Body.Bytes(), &response); err != nil { + t.Fatal(err) + } + } else { + if err := xml.Unmarshal(res.Body.Bytes(), &response.Ocs); err != nil { + t.Fatal(err) + } + } - if format == "json" { - if err := json.Unmarshal(res.Body.Bytes(), &response); err != nil { - t.Fatal(err) - } - } else { - if err := xml.Unmarshal(res.Body.Bytes(), &response.Ocs); err != nil { - t.Fatal(err) - } - } + if data.Error != nil { + assertResponseMeta(t, *data.Error, response.Ocs.Meta) + assertStatusCode(t, 400, res, ocsVersion) + } else { + assert.True(t, response.Ocs.Meta.Success(ocsVersion), unsuccessfulResponseText) + assertStatusCode(t, 200, res, ocsVersion) + } - if data.Error != nil { - assertResponseMeta(t, *data.Error, response.Ocs.Meta) - assertStatusCode(t, 400, res, ocsVersion) - } else { - assert.True(t, response.Ocs.Meta.Success(ocsVersion), unsuccessfulResponseText) - assertStatusCode(t, 200, res, ocsVersion) - } + // Check deleted user doesn't exist and the other user does + res, err = sendRequest( + "GET", + "/v1.php/cloud/users/rutherford?format=json", + "", + adminBasicAuth, + ) - // Check deleted user doesn't exist and the other user does - res, err = sendRequest( - "GET", - "/v1.php/cloud/users/rutherford?format=json", - "", - adminBasicAuth, - ) + if err != nil { + t.Fatal(err) + } - if err != nil { - t.Fatal(err) - } + var usersResponse SingleUserResponse + if err := json.Unmarshal(res.Body.Bytes(), &usersResponse); err != nil { + t.Fatal(err) + } - var usersResponse SingleUserResponse - if err := json.Unmarshal(res.Body.Bytes(), &usersResponse); err != nil { - t.Fatal(err) - } - - assert.True(t, usersResponse.Ocs.Meta.Success(ocsV1), unsuccessfulResponseText) - if data.Error == nil { - assertUserSame(t, updatedUser, usersResponse.Ocs.Data, true) - } else { - assertUserSame(t, user, usersResponse.Ocs.Data, true) - } - cleanUp(t) - } - } - } + assert.True(t, usersResponse.Ocs.Meta.Success(ocsV1), unsuccessfulResponseText) + if data.Error == nil { + assertUserSame(t, updatedUser, usersResponse.Ocs.Data, true) + } else { + assertUserSame(t, user, usersResponse.Ocs.Data, true) + } + cleanUp(t) + } + } + } } // This is a bug demonstration test for endpoint '/cloud/user' // Link to the issue: https://github.com/owncloud/ocis/ocs/issues/52 func TestGetSingleUser(t *testing.T) { - user := User{ - Enabled: "true", - Username: "rutherford", - ID: "rutherford", - Email: "rutherford@example.com", - Displayname: "Ernest RutherFord", - Password: "password", - } + user := User{ + Enabled: "true", + ID: "rutherford", + Email: "rutherford@example.com", + Displayname: "Ernest RutherFord", + Password: "password", + } - for _, ocsVersion := range ocsVersions { - for _, format := range formats { - err := createUser(user) - if err != nil { - t.Fatal(err) - } + for _, ocsVersion := range ocsVersions { + for _, format := range formats { + err := createUser(user) + if err != nil { + t.Fatal(err) + } - formatpart := getFormatString(format) - res, err := sendRequest( - "GET", - fmt.Sprintf("/%v/cloud/user%v", ocsVersion, formatpart), - "", - fmt.Sprintf("%v:%v", user.Username, user.Password), - ) + formatpart := getFormatString(format) + res, err := sendRequest( + "GET", + fmt.Sprintf("/%v/cloud/user%v", ocsVersion, formatpart), + "", + fmt.Sprintf("%v:%v", user.ID, user.Password), + ) - if err != nil { - t.Fatal(err) - } + if err != nil { + t.Fatal(err) + } - var response EmptyResponse + var response EmptyResponse - if format == "json" { - if err := json.Unmarshal(res.Body.Bytes(), &response); err != nil { - log.Println(err) - t.Fatal(err) - } - } else { - if err := xml.Unmarshal(res.Body.Bytes(), &response.Ocs); err != nil { - t.Fatal(err) - } - } + if format == "json" { + if err := json.Unmarshal(res.Body.Bytes(), &response); err != nil { + log.Println(err) + t.Fatal(err) + } + } else { + if err := xml.Unmarshal(res.Body.Bytes(), &response.Ocs); err != nil { + t.Fatal(err) + } + } - assertStatusCode(t, 400, res, ocsVersion) - assert.False(t, response.Ocs.Meta.Success(ocsVersion), "The response was expected to be a failure but was not") - assertResponseMeta(t, Meta{ - Status: "error", - StatusCode: 400, - Message: "missing user in context", - }, response.Ocs.Meta) - assert.Empty(t, response.Ocs.Data) - cleanUp(t) - } - } + assertStatusCode(t, 400, res, ocsVersion) + assert.False(t, response.Ocs.Meta.Success(ocsVersion), "The response was expected to be a failure but was not") + assertResponseMeta(t, Meta{ + Status: "error", + StatusCode: 400, + Message: "missing user in context", + }, response.Ocs.Meta) + assert.Empty(t, response.Ocs.Data) + cleanUp(t) + } + } } // This is a bug demonstration test for endpoint '/cloud/user' // Link to the issue: https://github.com/owncloud/ocis/ocs/issues/53 func TestGetUserSigningKey(t *testing.T) { - user := User{ - Enabled: "true", - Username: "rutherford", - ID: "rutherford", - Email: "rutherford@example.com", - Displayname: "Ernest RutherFord", - Password: "password", - } + user := User{ + Enabled: "true", + ID: "rutherford", + Email: "rutherford@example.com", + Displayname: "Ernest RutherFord", + Password: "password", + } - for _, ocsVersion := range ocsVersions { - for _, format := range formats { - err := createUser(user) - if err != nil { - t.Fatal(err) - } + for _, ocsVersion := range ocsVersions { + for _, format := range formats { + err := createUser(user) + if err != nil { + t.Fatal(err) + } - formatpart := getFormatString(format) - res, err := sendRequest( - "GET", - fmt.Sprintf("/%v/cloud/user/signing-key%v", ocsVersion, formatpart), - "", - fmt.Sprintf("%v:%v", user.Username, user.Password), - ) + formatpart := getFormatString(format) + res, err := sendRequest( + "GET", + fmt.Sprintf("/%v/cloud/user/signing-key%v", ocsVersion, formatpart), + "", + fmt.Sprintf("%v:%v", user.ID, user.Password), + ) - if err != nil { - t.Fatal(err) - } + if err != nil { + t.Fatal(err) + } - var response EmptyResponse + var response EmptyResponse - if format == "json" { - if err := json.Unmarshal(res.Body.Bytes(), &response); err != nil { - log.Println(err) - t.Fatal(err) - } - } else { - if err := xml.Unmarshal(res.Body.Bytes(), &response.Ocs); err != nil { - t.Fatal(err) - } - } + if format == "json" { + if err := json.Unmarshal(res.Body.Bytes(), &response); err != nil { + log.Println(err) + t.Fatal(err) + } + } else { + if err := xml.Unmarshal(res.Body.Bytes(), &response.Ocs); err != nil { + t.Fatal(err) + } + } - assertStatusCode(t, 400, res, ocsVersion) - assert.False(t, response.Ocs.Meta.Success(ocsVersion), "The response was expected to be a failure but was not") - assertResponseMeta(t, Meta{ - Status: "error", - StatusCode: 400, - Message: "missing user in context", - }, response.Ocs.Meta) - assert.Empty(t, response.Ocs.Data) - cleanUp(t) - } - } + assertStatusCode(t, 400, res, ocsVersion) + assert.False(t, response.Ocs.Meta.Success(ocsVersion), "The response was expected to be a failure but was not") + assertResponseMeta(t, Meta{ + Status: "error", + StatusCode: 400, + Message: "missing user in context", + }, response.Ocs.Meta) + assert.Empty(t, response.Ocs.Data) + cleanUp(t) + } + } } func AddUserToGroup(userid, groupid string) error { - res, err := sendRequest( - "POST", - fmt.Sprintf("/v2.php/cloud/users/%s/groups", userid), - fmt.Sprintf("groupid=%v", groupid), - adminBasicAuth, - ) - if err != nil { - return err - } - if res.Code != 200 { - return fmt.Errorf("Failed while adding the user to group") - } - return nil + res, err := sendRequest( + "POST", + fmt.Sprintf("/v2.php/cloud/users/%s/groups", userid), + fmt.Sprintf("groupid=%v", groupid), + adminBasicAuth, + ) + if err != nil { + return err + } + if res.Code != 200 { + return fmt.Errorf("Failed while adding the user to group") + } + return nil } func TestListUsersGroupNewUsers(t *testing.T) { - users := []User{ - { - Enabled: "true", - Username: "rutherford", - ID: "rutherford", - Email: "rutherford@example.com", - Displayname: "Ernest RutherFord", - }, - { - Enabled: "true", - Username: "thomson", - ID: "thomson", - Email: "thomson@example.com", - Displayname: "J. J. Thomson", - }, - } + users := []User{ + { + Enabled: "true", + ID: "rutherford", + Email: "rutherford@example.com", + Displayname: "Ernest RutherFord", + }, + { + Enabled: "true", + ID: "thomson", + Email: "thomson@example.com", + Displayname: "J. J. Thomson", + }, + } - for _, ocsVersion := range ocsVersions { - for _, format := range formats { - formatpart := getFormatString(format) - for _, user := range users { - err := createUser(user) - if err != nil { - t.Fatal(err) - } + for _, ocsVersion := range ocsVersions { + for _, format := range formats { + formatpart := getFormatString(format) + for _, user := range users { + err := createUser(user) + if err != nil { + t.Fatal(err) + } - res, err := sendRequest( - "GET", - fmt.Sprintf("/%s/cloud/users/%s/groups%s", ocsVersion, user.ID, formatpart), - "", - adminBasicAuth, - ) + res, err := sendRequest( + "GET", + fmt.Sprintf("/%s/cloud/users/%s/groups%s", ocsVersion, user.ID, formatpart), + "", + adminBasicAuth, + ) - if err != nil { - t.Fatal(err) - } + if err != nil { + t.Fatal(err) + } - var response GetUsersGroupsResponse - if format == "json" { - if err := json.Unmarshal(res.Body.Bytes(), &response); err != nil { - t.Fatal(err) - } - } else { - if err := xml.Unmarshal(res.Body.Bytes(), &response.Ocs); err != nil { - t.Fatal(err) - } - } + var response GetUsersGroupsResponse + if format == "json" { + if err := json.Unmarshal(res.Body.Bytes(), &response); err != nil { + t.Fatal(err) + } + } else { + if err := xml.Unmarshal(res.Body.Bytes(), &response.Ocs); err != nil { + t.Fatal(err) + } + } - assertStatusCode(t, 200, res, ocsVersion) - assert.True(t, response.Ocs.Meta.Success(ocsVersion), unsuccessfulResponseText) - assert.Empty(t, response.Ocs.Data.Groups) + assertStatusCode(t, 200, res, ocsVersion) + assert.True(t, response.Ocs.Meta.Success(ocsVersion), unsuccessfulResponseText) + assert.Equal(t, []string{groupUsers}, response.Ocs.Data.Groups) - cleanUp(t) - } - } - } + cleanUp(t) + } + } + } } func TestListUsersGroupDefaultUsers(t *testing.T) { - DefaultGroups := map[string][]string{ - userIDEinstein: { - groupUsers, - groupSailingLovers, - groupViolinHaters, - groupPhysicsLovers, - }, - userIDKonnectd: { - groupSysUsers, - }, - userIDFeynman: { - groupUsers, - groupQuantumLovers, - groupPhilosophyHaters, - groupPhysicsLovers, - }, - userIDReva: { - groupSysUsers, - }, - userIDMarie: { - groupUsers, - groupRadiumLovers, - groupPoloniumLovers, - groupPhysicsLovers, - }, - userIDMoss: { - groupUsers, - }, - } + DefaultGroups := map[string][]string{ + userIDEinstein: { + groupUsers, + groupSailingLovers, + groupViolinHaters, + groupPhysicsLovers, + }, + userIDKonnectd: { + groupSysUsers, + }, + userIDFeynman: { + groupUsers, + groupQuantumLovers, + groupPhilosophyHaters, + groupPhysicsLovers, + }, + userIDReva: { + groupSysUsers, + }, + userIDMarie: { + groupUsers, + groupRadiumLovers, + groupPoloniumLovers, + groupPhysicsLovers, + }, + userIDMoss: { + groupUsers, + }, + } - for _, ocsVersion := range ocsVersions { - for _, format := range formats { - formatpart := getFormatString(format) - for _, user := range DefaultUsers { - res, err := sendRequest( - "GET", - fmt.Sprintf("/%s/cloud/users/%s/groups%s", ocsVersion, user, formatpart), - "", - adminBasicAuth, - ) + for _, ocsVersion := range ocsVersions { + for _, format := range formats { + formatpart := getFormatString(format) + for _, user := range DefaultUsers { + res, err := sendRequest( + "GET", + fmt.Sprintf("/%s/cloud/users/%s/groups%s", ocsVersion, user, formatpart), + "", + adminBasicAuth, + ) - if err != nil { - t.Fatal(err) - } + if err != nil { + t.Fatal(err) + } - var response GetUsersGroupsResponse - if format == "json" { - if err := json.Unmarshal(res.Body.Bytes(), &response); err != nil { - t.Fatal(err) - } - } else { - if err := xml.Unmarshal(res.Body.Bytes(), &response.Ocs); err != nil { - t.Fatal(err) - } - } + var response GetUsersGroupsResponse + if format == "json" { + if err := json.Unmarshal(res.Body.Bytes(), &response); err != nil { + t.Fatal(err) + } + } else { + if err := xml.Unmarshal(res.Body.Bytes(), &response.Ocs); err != nil { + t.Fatal(err) + } + } - assertStatusCode(t, 200, res, ocsVersion) - assert.True(t, response.Ocs.Meta.Success(ocsVersion), unsuccessfulResponseText) + assertStatusCode(t, 200, res, ocsVersion) + assert.True(t, response.Ocs.Meta.Success(ocsVersion), unsuccessfulResponseText) - assert.Equal(t, DefaultGroups[user], response.Ocs.Data.Groups) - } - } - } - cleanUp(t) + assert.Equal(t, DefaultGroups[user], response.Ocs.Data.Groups) + } + } + } + cleanUp(t) } func TestGetGroupForUserInvalidUserId(t *testing.T) { - invalidUsers := []string{ - "1", - "invalid", - "3434234233", - "1am41validUs3r", - "_-@@--$$__", - } - for _, ocsVersion := range ocsVersions { - for _, format := range formats { - formatpart := getFormatString(format) - for _, user := range invalidUsers { - res, err := sendRequest( - "GET", - fmt.Sprintf("/%s/cloud/users/%s/groups%s", ocsVersion, user, formatpart), - "", - adminBasicAuth, - ) + invalidUsers := []string{ + "1", + "invalid", + "3434234233", + "1am41validUs3r", + "_-@@--$$__", + } + for _, ocsVersion := range ocsVersions { + for _, format := range formats { + formatpart := getFormatString(format) + for _, user := range invalidUsers { + res, err := sendRequest( + "GET", + fmt.Sprintf("/%s/cloud/users/%s/groups%s", ocsVersion, user, formatpart), + "", + adminBasicAuth, + ) - if err != nil { - t.Fatal(err) - } + if err != nil { + t.Fatal(err) + } - var response EmptyResponse - if format == "json" { - if err := json.Unmarshal(res.Body.Bytes(), &response); err != nil { - t.Fatal(err) - } - } else { - if err := xml.Unmarshal(res.Body.Bytes(), &response.Ocs); err != nil { - t.Fatal(err) - } - } + var response EmptyResponse + if format == "json" { + if err := json.Unmarshal(res.Body.Bytes(), &response); err != nil { + t.Fatal(err) + } + } else { + if err := xml.Unmarshal(res.Body.Bytes(), &response.Ocs); err != nil { + t.Fatal(err) + } + } - assertStatusCode(t, 404, res, ocsVersion) - assert.False(t, response.Ocs.Meta.Success(ocsVersion), unsuccessfulResponseText) - assertResponseMeta(t, Meta{ - Status: "error", - StatusCode: 998, - Message: "The requested user could not be found", - }, response.Ocs.Meta) + assertStatusCode(t, 404, res, ocsVersion) + assert.False(t, response.Ocs.Meta.Success(ocsVersion), unsuccessfulResponseText) + assertResponseMeta(t, Meta{ + Status: "error", + StatusCode: 998, + Message: "The requested user could not be found", + }, response.Ocs.Meta) - assert.Empty(t, response.Ocs.Data) - } - } - } + assert.Empty(t, response.Ocs.Data) + } + } + } } func TestAddUsersToGroupsNewUsers(t *testing.T) { - users := []User{ - { - Enabled: "true", - Username: "rutherford", - ID: "rutherford", - Email: "rutherford@example.com", - Displayname: "Ernest RutherFord", - }, - { - Enabled: "true", - Username: "thomson", - ID: "thomson", - Email: "thomson@example.com", - Displayname: "J. J. Thomson", - }, - } + users := []User{ + { + Enabled: "true", + ID: "rutherford", + Email: "rutherford@example.com", + Displayname: "Ernest RutherFord", + }, + { + Enabled: "true", + ID: "thomson", + Email: "thomson@example.com", + Displayname: "J. J. Thomson", + }, + } - for _, ocsVersion := range ocsVersions { - for _, format := range formats { - formatpart := getFormatString(format) - for _, user := range users { - err := createUser(user) - if err != nil { - t.Fatal(err) - } + for _, ocsVersion := range ocsVersions { + for _, format := range formats { + formatpart := getFormatString(format) + for _, user := range users { + err := createUser(user) + if err != nil { + t.Fatal(err) + } - // group id for Physics lover - groupid := groupPhysicsLovers + // group id for Physics lover + groupid := groupPhysicsLovers - res, err := sendRequest( - "POST", - fmt.Sprintf("/%s/cloud/users/%s/groups%s", ocsVersion, user.ID, formatpart), - "groupid="+groupid, - adminBasicAuth, - ) + res, err := sendRequest( + "POST", + fmt.Sprintf("/%s/cloud/users/%s/groups%s", ocsVersion, user.ID, formatpart), + "groupid="+groupid, + adminBasicAuth, + ) - if err != nil { - t.Fatal(err) - } + if err != nil { + t.Fatal(err) + } - var response EmptyResponse - if format == "json" { - if err := json.Unmarshal(res.Body.Bytes(), &response); err != nil { - t.Fatal(err) - } - } else { - if err := xml.Unmarshal(res.Body.Bytes(), &response.Ocs); err != nil { - t.Fatal(err) - } - } + var response EmptyResponse + if format == "json" { + if err := json.Unmarshal(res.Body.Bytes(), &response); err != nil { + t.Fatal(err) + } + } else { + if err := xml.Unmarshal(res.Body.Bytes(), &response.Ocs); err != nil { + t.Fatal(err) + } + } - assertStatusCode(t, 200, res, ocsVersion) - assert.True(t, response.Ocs.Meta.Success(ocsVersion), unsuccessfulResponseText) - assert.Empty(t, response.Ocs.Data) + assertStatusCode(t, 200, res, ocsVersion) + assert.True(t, response.Ocs.Meta.Success(ocsVersion), unsuccessfulResponseText) + assert.Empty(t, response.Ocs.Data) - // Check the user is in the group - res, err = sendRequest( - "GET", - fmt.Sprintf("/%s/cloud/users/%s/groups?format=json", ocsVersion, user.ID), - "", - adminBasicAuth, - ) - if err != nil { - t.Fatal(err) - } - var grpResponse GetUsersGroupsResponse - if err := json.Unmarshal(res.Body.Bytes(), &grpResponse); err != nil { - t.Fatal(err) - } - assert.Contains(t, grpResponse.Ocs.Data.Groups, groupid) + // Check the user is in the group + res, err = sendRequest( + "GET", + fmt.Sprintf("/%s/cloud/users/%s/groups?format=json", ocsVersion, user.ID), + "", + adminBasicAuth, + ) + if err != nil { + t.Fatal(err) + } + var grpResponse GetUsersGroupsResponse + if err := json.Unmarshal(res.Body.Bytes(), &grpResponse); err != nil { + t.Fatal(err) + } + assert.Contains(t, grpResponse.Ocs.Data.Groups, groupid) - cleanUp(t) - } - } - } + cleanUp(t) + } + } + } } -// Issue: https://github.com/owncloud/ocis/ocs/issues/55 Incorrect message when adding user to non existing group func TestAddUsersToGroupInvalidGroup(t *testing.T) { - user := User{ - Enabled: "true", - Username: "rutherford", - ID: "rutherford", - Email: "rutherford@example.com", - Displayname: "Ernest RutherFord", - } - err := createUser(user) - if err != nil { - t.Fatal(err) - } + user := User{ + Enabled: "true", + ID: "rutherford", + Email: "rutherford@example.com", + Displayname: "Ernest RutherFord", + } + err := createUser(user) + if err != nil { + t.Fatal(err) + } - invalidGroups := []string{ - "1", - "invalid", - "3434234233", - "1am41validUs3r", - "_-@@--$$__", - "c7fbe8c4-139b-4376-b307-cf0a8c2d0d9c", - } + invalidGroups := []string{ + "1", + "invalid", + "3434234233", + "1am41validUs3r", + "_-@@--$$__", + "c7fbe8c4-139b-4376-b307-cf0a8c2d0d9c", + } - for _, ocsVersion := range ocsVersions { - for _, format := range formats { - formatpart := getFormatString(format) - for _, groupid := range invalidGroups { - res, err := sendRequest( - "POST", - fmt.Sprintf("/%s/cloud/users/rutherford/groups%s", ocsVersion, formatpart), - "groupid="+groupid, - adminBasicAuth, - ) + for _, ocsVersion := range ocsVersions { + for _, format := range formats { + formatpart := getFormatString(format) + for _, groupid := range invalidGroups { + res, err := sendRequest( + "POST", + fmt.Sprintf("/%s/cloud/users/rutherford/groups%s", ocsVersion, formatpart), + "groupid="+groupid, + adminBasicAuth, + ) - if err != nil { - t.Fatal(err) - } + if err != nil { + t.Fatal(err) + } - var response EmptyResponse - if format == "json" { - if err := json.Unmarshal(res.Body.Bytes(), &response); err != nil { - t.Fatal(err) - } - } else { - if err := xml.Unmarshal(res.Body.Bytes(), &response.Ocs); err != nil { - t.Fatal(err) - } - } + var response EmptyResponse + if format == "json" { + if err := json.Unmarshal(res.Body.Bytes(), &response); err != nil { + t.Fatal(err) + } + } else { + if err := xml.Unmarshal(res.Body.Bytes(), &response.Ocs); err != nil { + t.Fatal(err) + } + } - assertStatusCode(t, 404, res, ocsVersion) - assert.False(t, response.Ocs.Meta.Success(ocsVersion), "The response was expected to be fail but was successful") - assertResponseMeta(t, Meta{ - "error", - 998, - "The requested user could not be found", - }, response.Ocs.Meta) - assert.Empty(t, response.Ocs.Data) - } - } - } - cleanUp(t) + assertStatusCode(t, 404, res, ocsVersion) + assert.False(t, response.Ocs.Meta.Success(ocsVersion), "The response was expected to be fail but was successful") + assertResponseMeta(t, Meta{ + "error", + 998, + "The requested group could not be found", + }, response.Ocs.Meta) + assert.Empty(t, response.Ocs.Data) + } + } + } + cleanUp(t) } // Issue: https://github.com/owncloud/ocis/ocs/issues/57 - cannot remove user from group func TestRemoveUserFromGroup(t *testing.T) { - user := User{ - Enabled: "true", - Username: "rutherford", - ID: "rutherford", - Email: "rutherford@example.com", - Displayname: "Ernest RutherFord", - } + user := User{ + Enabled: "true", + ID: "rutherford", + Email: "rutherford@example.com", + Displayname: "Ernest RutherFord", + } - groups := []string{ - groupRadiumLovers, - groupPoloniumLovers, - groupPhysicsLovers, - } + groups := []string{ + groupRadiumLovers, + groupPoloniumLovers, + groupPhysicsLovers, + } - var err error - for _, ocsVersion := range ocsVersions { - for _, format := range formats { - formatpart := getFormatString(format) + var err error + for _, ocsVersion := range ocsVersions { + for _, format := range formats { + formatpart := getFormatString(format) - err = createUser(user) - if err != nil { - t.Fatalf("Failed while creating new user: %v", err) - } - for _, group := range groups { - err := AddUserToGroup(user.ID, group) - if err != nil { - t.Fatalf("Failed while creating new user: %v", err) - } - } + err = createUser(user) + if err != nil { + t.Fatalf("Failed while creating new user: %v", err) + } + for _, group := range groups { + err := AddUserToGroup(user.ID, group) + if err != nil { + t.Fatalf("Failed while creating new user: %v", err) + } + } - // Remove user from one group - res, err := sendRequest( - "DELETE", - fmt.Sprintf("/%s/cloud/users/%s/groups%s", ocsVersion, user.ID, formatpart), - "groupid="+groups[0], - adminBasicAuth, - ) + // Remove user from one group + res, err := sendRequest( + "DELETE", + fmt.Sprintf("/%s/cloud/users/%s/groups%s", ocsVersion, user.ID, formatpart), + "groupid="+groups[0], + adminBasicAuth, + ) - if err != nil { - t.Fatal(err) - } + if err != nil { + t.Fatal(err) + } - var response EmptyResponse - if format == "json" { - if err := json.Unmarshal(res.Body.Bytes(), &response); err != nil { - t.Fatal(err) - } - } else { - if err := xml.Unmarshal(res.Body.Bytes(), &response.Ocs); err != nil { - t.Fatal(err) - } - } + var response EmptyResponse + if format == "json" { + if err := json.Unmarshal(res.Body.Bytes(), &response); err != nil { + t.Fatal(err) + } + } else { + if err := xml.Unmarshal(res.Body.Bytes(), &response.Ocs); err != nil { + t.Fatal(err) + } + } - assertStatusCode(t, 500, res, ocsVersion) - assertResponseMeta(t, Meta{ - "error", - 996, - "{\"id\":\".\",\"code\":500,\"detail\":\"could not clean up group id: invalid id .\",\"status\":\"Internal Server Error\"}", - }, response.Ocs.Meta) - assert.Empty(t, response.Ocs.Data) + assertStatusCode(t, 500, res, ocsVersion) + assertResponseMeta(t, Meta{ + "error", + 996, + "{\"id\":\".\",\"code\":500,\"detail\":\"could not clean up group id: invalid id .\",\"status\":\"Internal Server Error\"}", + }, response.Ocs.Meta) + assert.Empty(t, response.Ocs.Data) - // Check the users are correctly added to group - res, err = sendRequest( - "GET", - fmt.Sprintf("/%s/cloud/users/%s/groups?format=json", ocsVersion, user.ID), - "", - adminBasicAuth, - ) - if err != nil { - t.Fatal(err) - } - var grpResponse GetUsersGroupsResponse - if err := json.Unmarshal(res.Body.Bytes(), &grpResponse); err != nil { - t.Fatal(err) - } + // Check the users are correctly added to group + res, err = sendRequest( + "GET", + fmt.Sprintf("/%s/cloud/users/%s/groups?format=json", ocsVersion, user.ID), + "", + adminBasicAuth, + ) + if err != nil { + t.Fatal(err) + } + var grpResponse GetUsersGroupsResponse + if err := json.Unmarshal(res.Body.Bytes(), &grpResponse); err != nil { + t.Fatal(err) + } - // Change this line once the issue is fixed - // assert.NotContains(t, grpResponse.Ocs.Data.Groups, groups[0]) - assert.Contains(t, grpResponse.Ocs.Data.Groups, groups[0]) - assert.Contains(t, grpResponse.Ocs.Data.Groups, groups[1]) - assert.Contains(t, grpResponse.Ocs.Data.Groups, groups[2]) - cleanUp(t) - } - } + // Change this line once the issue is fixed + // assert.NotContains(t, grpResponse.Ocs.Data.Groups, groups[0]) + assert.Contains(t, grpResponse.Ocs.Data.Groups, groups[0]) + assert.Contains(t, grpResponse.Ocs.Data.Groups, groups[1]) + assert.Contains(t, grpResponse.Ocs.Data.Groups, groups[2]) + cleanUp(t) + } + } } // Issue: https://github.com/owncloud/ocis-ocs/issues/59 - cloud/capabilities endpoint not implemented @@ -1885,338 +1804,338 @@ func TestGetConfig(t *testing.T) { } } func TestGetGroupsDefaultGroups(t *testing.T) { - for _, ocsVersion := range ocsVersions { - for _, format := range formats { - formatpart := getFormatString(format) + for _, ocsVersion := range ocsVersions { + for _, format := range formats { + formatpart := getFormatString(format) - res, err := sendRequest( - "GET", - fmt.Sprintf("/%s/cloud/groups%s", ocsVersion, formatpart), - "", - adminBasicAuth, - ) + res, err := sendRequest( + "GET", + fmt.Sprintf("/%s/cloud/groups%s", ocsVersion, formatpart), + "", + adminBasicAuth, + ) - if err != nil { - t.Fatal(err) - } + if err != nil { + t.Fatal(err) + } - var response GetGroupsResponse - if format == "json" { - if err := json.Unmarshal(res.Body.Bytes(), &response); err != nil { - t.Fatal(err) - } - } else { - if err := xml.Unmarshal(res.Body.Bytes(), &response.Ocs); err != nil { - t.Fatal(err) - } - } + var response GetGroupsResponse + if format == "json" { + if err := json.Unmarshal(res.Body.Bytes(), &response); err != nil { + t.Fatal(err) + } + } else { + if err := xml.Unmarshal(res.Body.Bytes(), &response.Ocs); err != nil { + t.Fatal(err) + } + } - assertStatusCode(t, 200, res, ocsVersion) - assert.True(t, response.Ocs.Meta.Success(ocsVersion), unsuccessfulResponseText) - assert.Equal(t, DefaultGroups, response.Ocs.Data.Groups) - } - } + assertStatusCode(t, 200, res, ocsVersion) + assert.True(t, response.Ocs.Meta.Success(ocsVersion), unsuccessfulResponseText) + assert.Subset(t, DefaultGroups, response.Ocs.Data.Groups) + } + } } func TestCreateGroup(t *testing.T) { - testData := []struct { - group Group - err *Meta - }{ - // A simple group - { - Group{ - ID: "grp1", - GIDNumber: 32222, - Displayname: "Group Name", - }, - &Meta{ - Status: "error", - StatusCode: 999, - Message: "not implemented", - }, - }, - } - for _, ocsVersion := range ocsVersions { - for _, format := range formats { - for _, data := range testData { - formatpart := getFormatString(format) - res, err := sendRequest( - "POST", - fmt.Sprintf("/%v/cloud/groups%v", ocsVersion, formatpart), - data.group.getGroupRequestString(), - adminBasicAuth, - ) + testData := []struct { + group Group + err *Meta + }{ + // A simple group + { + Group{ + ID: "grp1", + GIDNumber: 32222, + Displayname: "Group Name", + }, + &Meta{ + Status: "error", + StatusCode: 999, + Message: "not implemented", + }, + }, + } + for _, ocsVersion := range ocsVersions { + for _, format := range formats { + for _, data := range testData { + formatpart := getFormatString(format) + res, err := sendRequest( + "POST", + fmt.Sprintf("/%v/cloud/groups%v", ocsVersion, formatpart), + data.group.getGroupRequestString(), + adminBasicAuth, + ) - if err != nil { - t.Fatal(err) - } + if err != nil { + t.Fatal(err) + } - var response EmptyResponse + var response EmptyResponse - if format == "json" { - if err := json.Unmarshal(res.Body.Bytes(), &response); err != nil { - t.Fatal(err) - } - } else { - if err := xml.Unmarshal(res.Body.Bytes(), &response.Ocs); err != nil { - t.Fatal(err) - } - } + if format == "json" { + if err := json.Unmarshal(res.Body.Bytes(), &response); err != nil { + t.Fatal(err) + } + } else { + if err := xml.Unmarshal(res.Body.Bytes(), &response.Ocs); err != nil { + t.Fatal(err) + } + } - if data.err == nil { - assert.True(t, response.Ocs.Meta.Success(ocsVersion), unsuccessfulResponseText) - } else { - assertResponseMeta(t, *data.err, response.Ocs.Meta) - } + if data.err == nil { + assert.True(t, response.Ocs.Meta.Success(ocsVersion), unsuccessfulResponseText) + } else { + assertResponseMeta(t, *data.err, response.Ocs.Meta) + } - // Check the group exists of not - res, err = sendRequest( - "GET", - "/v2.php/cloud/groups?format=json", - "", - adminBasicAuth, - ) - if err != nil { - t.Fatal(err) - } - var groupResponse GetGroupsResponse - if err := json.Unmarshal(res.Body.Bytes(), &groupResponse); err != nil { - t.Fatal(err) - } - if data.err == nil { - assert.Contains(t, groupResponse.Ocs.Data.Groups, data.group.ID) - } else { - assert.NotContains(t, groupResponse.Ocs.Data.Groups, data.group.ID) - } - cleanUp(t) - } - } - } + // Check the group exists of not + res, err = sendRequest( + "GET", + "/v2.php/cloud/groups?format=json", + "", + adminBasicAuth, + ) + if err != nil { + t.Fatal(err) + } + var groupResponse GetGroupsResponse + if err := json.Unmarshal(res.Body.Bytes(), &groupResponse); err != nil { + t.Fatal(err) + } + if data.err == nil { + assert.Contains(t, groupResponse.Ocs.Data.Groups, data.group.ID) + } else { + assert.NotContains(t, groupResponse.Ocs.Data.Groups, data.group.ID) + } + cleanUp(t) + } + } + } } // Add group not implemented // Unskip this test after adding group is implemented. func TestDeleteGroup(t *testing.T) { - t.Skip() - testData := []Group{ - { - ID: "grp1", - GIDNumber: 32222, - Displayname: "Group Name", - }, - } - for _, ocsVersion := range ocsVersions { - for _, format := range formats { - formatpart := getFormatString(format) - for _, data := range testData { - err := createGroup(data) + t.Skip() + testData := []Group{ + { + ID: "grp1", + GIDNumber: 32222, + Displayname: "Group Name", + }, + } + for _, ocsVersion := range ocsVersions { + for _, format := range formats { + formatpart := getFormatString(format) + for _, data := range testData { + err := createGroup(data) - res, err := sendRequest( - "DELETE", - fmt.Sprintf("/%v/cloud/groups/%v%v", ocsVersion, data.ID, formatpart), - "groupid="+data.ID, - adminBasicAuth, - ) - if err != nil { - t.Fatal(err) - } + res, err := sendRequest( + "DELETE", + fmt.Sprintf("/%v/cloud/groups/%v%v", ocsVersion, data.ID, formatpart), + "groupid="+data.ID, + adminBasicAuth, + ) + if err != nil { + t.Fatal(err) + } - var response EmptyResponse - if format == "json" { - if err := json.Unmarshal(res.Body.Bytes(), &response); err != nil { - t.Fatal(err) - } - } else { - if err := xml.Unmarshal(res.Body.Bytes(), &response.Ocs); err != nil { - t.Fatal(err) - } - } - assert.True(t, response.Ocs.Meta.Success(ocsVersion), unsuccessfulResponseText) + var response EmptyResponse + if format == "json" { + if err := json.Unmarshal(res.Body.Bytes(), &response); err != nil { + t.Fatal(err) + } + } else { + if err := xml.Unmarshal(res.Body.Bytes(), &response.Ocs); err != nil { + t.Fatal(err) + } + } + assert.True(t, response.Ocs.Meta.Success(ocsVersion), unsuccessfulResponseText) - // Check the group does not exists - res, err = sendRequest( - "GET", - "/v2.php/cloud/groups?format=json", - "", - adminBasicAuth, - ) - if err != nil { - t.Fatal(err) - } - var groupResponse GetGroupsResponse - if err := json.Unmarshal(res.Body.Bytes(), &groupResponse); err != nil { - t.Fatal(err) - } + // Check the group does not exists + res, err = sendRequest( + "GET", + "/v2.php/cloud/groups?format=json", + "", + adminBasicAuth, + ) + if err != nil { + t.Fatal(err) + } + var groupResponse GetGroupsResponse + if err := json.Unmarshal(res.Body.Bytes(), &groupResponse); err != nil { + t.Fatal(err) + } - assert.NotContains(t, groupResponse.Ocs.Data.Groups, data.ID) - cleanUp(t) - } - } - } + assert.NotContains(t, groupResponse.Ocs.Data.Groups, data.ID) + cleanUp(t) + } + } + } } func TestDeleteGroupInvalidGroups(t *testing.T) { - testData := []string{ - "1", - "invalid", - "3434234233", - "1am41validUs3r", - "_-@@--$$__", - } - for _, ocsVersion := range ocsVersions { - for _, format := range formats { - formatpart := getFormatString(format) - for _, data := range testData { - res, err := sendRequest( - "DELETE", - fmt.Sprintf("/%v/cloud/groups/%v%v", ocsVersion, data, formatpart), - "groupid="+data, - adminBasicAuth, - ) + testData := []string{ + "1", + "invalid", + "3434234233", + "1am41validUs3r", + "_-@@--$$__", + } + for _, ocsVersion := range ocsVersions { + for _, format := range formats { + formatpart := getFormatString(format) + for _, data := range testData { + res, err := sendRequest( + "DELETE", + fmt.Sprintf("/%v/cloud/groups/%v%v", ocsVersion, data, formatpart), + "groupid="+data, + adminBasicAuth, + ) - if err != nil { - t.Fatal(err) - } + if err != nil { + t.Fatal(err) + } - var response EmptyResponse + var response EmptyResponse - if format == "json" { - if err := json.Unmarshal(res.Body.Bytes(), &response); err != nil { - t.Fatal(err) - } - } else { - if err := xml.Unmarshal(res.Body.Bytes(), &response.Ocs); err != nil { - t.Fatal(err) - } - } + if format == "json" { + if err := json.Unmarshal(res.Body.Bytes(), &response); err != nil { + t.Fatal(err) + } + } else { + if err := xml.Unmarshal(res.Body.Bytes(), &response.Ocs); err != nil { + t.Fatal(err) + } + } - assertStatusCode(t, 404, res, ocsVersion) - assert.False(t, response.Ocs.Meta.Success(ocsVersion), "The response was expected to fail but was successful") - assertResponseMeta(t, Meta{ - "error", - 998, - "The requested group could not be found", - }, response.Ocs.Meta) - cleanUp(t) - } - } - } + assertStatusCode(t, 404, res, ocsVersion) + assert.False(t, response.Ocs.Meta.Success(ocsVersion), "The response was expected to fail but was successful") + assertResponseMeta(t, Meta{ + "error", + 998, + "The requested group could not be found", + }, response.Ocs.Meta) + cleanUp(t) + } + } + } } func TestGetGroupMembersDefaultGroups(t *testing.T) { - defaultGroups := map[string][]string{ - groupSysUsers: { - userIDKonnectd, - userIDReva, - }, - groupUsers: { - userIDEinstein, - userIDMarie, - userIDFeynman, - }, - groupSailingLovers: { - userIDEinstein, - }, - groupViolinHaters: { - userIDEinstein, - }, - groupPoloniumLovers: { - userIDMarie, - }, - groupQuantumLovers: { - userIDFeynman, - }, - groupPhilosophyHaters: { - userIDFeynman, - }, - groupPhysicsLovers: { - userIDEinstein, - userIDMarie, - userIDFeynman, - }, - } - for _, ocsVersion := range ocsVersions { - for _, format := range formats { - for group, members := range defaultGroups { - formatpart := getFormatString(format) - res, err := sendRequest( - "GET", - fmt.Sprintf("/%v/cloud/groups/%v%v", ocsVersion, group, formatpart), - "", - adminBasicAuth, - ) + defaultGroups := map[string][]string{ + groupSysUsers: { + userIDKonnectd, + userIDReva, + }, + groupUsers: { + userIDEinstein, + userIDMarie, + userIDFeynman, + }, + groupSailingLovers: { + userIDEinstein, + }, + groupViolinHaters: { + userIDEinstein, + }, + groupPoloniumLovers: { + userIDMarie, + }, + groupQuantumLovers: { + userIDFeynman, + }, + groupPhilosophyHaters: { + userIDFeynman, + }, + groupPhysicsLovers: { + userIDEinstein, + userIDMarie, + userIDFeynman, + }, + } + for _, ocsVersion := range ocsVersions { + for _, format := range formats { + for group, members := range defaultGroups { + formatpart := getFormatString(format) + res, err := sendRequest( + "GET", + fmt.Sprintf("/%v/cloud/groups/%v%v", ocsVersion, group, formatpart), + "", + adminBasicAuth, + ) - if err != nil { - t.Fatal(err) - } + if err != nil { + t.Fatal(err) + } - var response GetGroupMembersResponse + var response GetGroupMembersResponse - if format == "json" { - if err := json.Unmarshal(res.Body.Bytes(), &response); err != nil { - t.Fatal(err) - } - } else { - if err := xml.Unmarshal(res.Body.Bytes(), &response.Ocs); err != nil { - t.Fatal(err) - } - } + if format == "json" { + if err := json.Unmarshal(res.Body.Bytes(), &response); err != nil { + t.Fatal(err) + } + } else { + if err := xml.Unmarshal(res.Body.Bytes(), &response.Ocs); err != nil { + t.Fatal(err) + } + } - assertStatusCode(t, 200, res, ocsVersion) - assert.True(t, response.Ocs.Meta.Success(ocsVersion), unsuccessfulResponseText) - assert.Equal(t, members, response.Ocs.Data.Users) + assertStatusCode(t, 200, res, ocsVersion) + assert.True(t, response.Ocs.Meta.Success(ocsVersion), unsuccessfulResponseText) + assert.Equal(t, members, response.Ocs.Data.Users) - cleanUp(t) - } - } - } + cleanUp(t) + } + } + } } func TestListMembersInvalidGroups(t *testing.T) { - testData := []string{ - "1", - "invalid", - "3434234233", - "1am41validUs3r", - "_-@@--$$__", - } - for _, ocsVersion := range ocsVersions { - for _, format := range formats { - formatpart := getFormatString(format) - for _, group := range testData { - res, err := sendRequest( - "GET", - fmt.Sprintf("/%v/cloud/groups/%v%v", ocsVersion, group, formatpart), - "", - adminBasicAuth, - ) + testData := []string{ + "1", + "invalid", + "3434234233", + "1am41validUs3r", + "_-@@--$$__", + } + for _, ocsVersion := range ocsVersions { + for _, format := range formats { + formatpart := getFormatString(format) + for _, group := range testData { + res, err := sendRequest( + "GET", + fmt.Sprintf("/%v/cloud/groups/%v%v", ocsVersion, group, formatpart), + "", + adminBasicAuth, + ) - if err != nil { - t.Fatal(err) - } + if err != nil { + t.Fatal(err) + } - var response EmptyResponse + var response EmptyResponse - if format == "json" { - if err := json.Unmarshal(res.Body.Bytes(), &response); err != nil { - t.Fatal(err) - } - } else { - if err := xml.Unmarshal(res.Body.Bytes(), &response.Ocs); err != nil { - t.Fatal(err) - } - } + if format == "json" { + if err := json.Unmarshal(res.Body.Bytes(), &response); err != nil { + t.Fatal(err) + } + } else { + if err := xml.Unmarshal(res.Body.Bytes(), &response.Ocs); err != nil { + t.Fatal(err) + } + } - assertStatusCode(t, 404, res, ocsVersion) - assert.False(t, response.Ocs.Meta.Success(ocsVersion), "The response was expected to fail but was successful") - assertResponseMeta(t, Meta{ - "error", - 998, - "The requested group could not be found", - }, response.Ocs.Meta) - cleanUp(t) - } - } - } + assertStatusCode(t, 404, res, ocsVersion) + assert.False(t, response.Ocs.Meta.Success(ocsVersion), "The response was expected to fail but was successful") + assertResponseMeta(t, Meta{ + "error", + 998, + "The requested group could not be found", + }, response.Ocs.Meta) + cleanUp(t) + } + } + } } diff --git a/ocs/pkg/service/v0/data/user.go b/ocs/pkg/service/v0/data/user.go index c33d878a9..34da73400 100644 --- a/ocs/pkg/service/v0/data/user.go +++ b/ocs/pkg/service/v0/data/user.go @@ -7,10 +7,8 @@ type Users struct { // User holds the payload for a GetUser response type User struct { - // TODO needs better naming, clarify if we need a userid, a username or both Enabled string `json:"enabled" xml:"enabled"` - UserID string `json:"id" xml:"id"` - Username string `json:"username" xml:"username"` + UserID string `json:"id" xml:"id"`// UserID is mapped to the preferred_name attribute in accounts DisplayName string `json:"display-name" xml:"display-name"` LegacyDisplayName string `json:"displayname" xml:"displayname"` Email string `json:"email" xml:"email"` diff --git a/ocs/pkg/service/v0/groups.go b/ocs/pkg/service/v0/groups.go index f349ba04a..559b18073 100644 --- a/ocs/pkg/service/v0/groups.go +++ b/ocs/pkg/service/v0/groups.go @@ -3,6 +3,7 @@ package svc import ( "fmt" "net/http" + "regexp" "github.com/go-chi/chi" "github.com/go-chi/render" @@ -16,18 +17,26 @@ import ( // ListUserGroups lists a users groups func (o Ocs) ListUserGroups(w http.ResponseWriter, r *http.Request) { userid := chi.URLParam(r, "userid") + var account *accounts.Account + var err error - account, err := o.getAccountService().GetAccount(r.Context(), &accounts.GetAccountRequest{Id: userid}) - - if err != nil { - merr := merrors.FromError(err) - if merr.Code == http.StatusNotFound { - render.Render(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "The requested user could not be found")) - } else { - render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error())) + if isValidUUID(userid) { + account, err = o.getAccountService().GetAccount(r.Context(), &accounts.GetAccountRequest{ + Id: userid, + }) + } else { + // despite the confusion, if we make it here we got ourselves a username + account, err = o.fetchAccountByUsername(r.Context(), userid) + if err != nil { + merr := merrors.FromError(err) + if merr.Code == http.StatusNotFound { + render.Render(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "The requested user could not be found")) + } else { + render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error())) + } + o.logger.Error().Err(err).Str("userid", userid).Msg("could not get list of user groups") + return } - o.logger.Error().Err(err).Str("userid", userid).Msg("could not get list of user groups") - return } groups := []string{} @@ -49,16 +58,26 @@ func (o Ocs) AddToGroup(w http.ResponseWriter, r *http.Request) { render.Render(w, r, response.ErrRender(data.MetaBadRequest.StatusCode, "empty group assignment: unspecified group")) return } + account, err := o.fetchAccountByUsername(r.Context(), userid) + if err != nil { + merr := merrors.FromError(err) + if merr.Code == http.StatusNotFound { + render.Render(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "The requested user could not be found")) + } else { + render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error())) + } + return + } - _, err := o.getGroupsService().AddMember(r.Context(), &accounts.AddMemberRequest{ - AccountId: userid, + _, err = o.getGroupsService().AddMember(r.Context(), &accounts.AddMemberRequest{ + AccountId: account.Id, GroupId: groupid, }) if err != nil { merr := merrors.FromError(err) if merr.Code == http.StatusNotFound { - render.Render(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "The requested user could not be found")) + render.Render(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "The requested group could not be found")) } else { render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error())) } @@ -75,15 +94,37 @@ func (o Ocs) RemoveFromGroup(w http.ResponseWriter, r *http.Request) { userid := chi.URLParam(r, "userid") groupid := r.URL.Query().Get("groupid") - _, err := o.getGroupsService().RemoveMember(r.Context(), &accounts.RemoveMemberRequest{ - AccountId: userid, + var account *accounts.Account + var err error + + if isValidUUID(userid) { + account, _ = o.getAccountService().GetAccount(r.Context(), &accounts.GetAccountRequest{ + Id: userid, + }) + } else { + // despite the confusion, if we make it here we got ourselves a username + account, err = o.fetchAccountByUsername(r.Context(), userid) + if err != nil { + merr := merrors.FromError(err) + if merr.Code == http.StatusNotFound { + render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, "The requested user could not be found")) + } else { + render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error())) + } + o.logger.Error().Err(err).Str("userid", userid).Msg("could not get list of user groups") + return + } + } + + _, err = o.getGroupsService().RemoveMember(r.Context(), &accounts.RemoveMemberRequest{ + AccountId: account.Id, GroupId: groupid, }) if err != nil { merr := merrors.FromError(err) if merr.Code == http.StatusNotFound { - render.Render(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "The requested user could not be found")) + render.Render(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "The requested group could not be found")) } else { render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error())) } @@ -175,3 +216,8 @@ func (o Ocs) GetGroupMembers(w http.ResponseWriter, r *http.Request) { o.logger.Error().Err(err).Int("count", len(members)).Str("groupid", groupid).Msg("listing group members") render.Render(w, r, response.DataRender(&data.Users{Users: members})) } + +func isValidUUID(uuid string) bool { + r := regexp.MustCompile("^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[8|9|aA|bB][a-fA-F0-9]{3}-[a-fA-F0-9]{12}$") + return r.MatchString(uuid) +} diff --git a/ocs/pkg/service/v0/users.go b/ocs/pkg/service/v0/users.go index c4a3a6021..4e257c81b 100644 --- a/ocs/pkg/service/v0/users.go +++ b/ocs/pkg/service/v0/users.go @@ -1,6 +1,7 @@ package svc import ( + "context" "crypto/rand" "encoding/hex" "fmt" @@ -25,6 +26,8 @@ import ( func (o Ocs) GetUser(w http.ResponseWriter, r *http.Request) { // TODO this endpoint needs authentication using the roles and permissions userid := chi.URLParam(r, "userid") + var account *accounts.Account + var err error if userid == "" { u, ok := user.ContextGetUser(r.Context()) @@ -32,13 +35,12 @@ func (o Ocs) GetUser(w http.ResponseWriter, r *http.Request) { render.Render(w, r, response.ErrRender(data.MetaBadRequest.StatusCode, "missing user in context")) return } - - userid = u.Id.OpaqueId + account, err = o.getAccountService().GetAccount(r.Context(), &accounts.GetAccountRequest{ + Id: u.Id.OpaqueId, + }) + } else { + account, err = o.fetchAccountByUsername(r.Context(), userid) } - - account, err := o.getAccountService().GetAccount(r.Context(), &accounts.GetAccountRequest{ - Id: userid, - }) if err != nil { merr := merrors.FromError(err) if merr.Code == http.StatusNotFound { @@ -64,9 +66,8 @@ func (o Ocs) GetUser(w http.ResponseWriter, r *http.Request) { enabled = "false" } - render.Render(w, r, response.DataRender(&data.User{ - UserID: account.Id, // TODO userid vs username! implications for clients if we return the userid here? -> implement graph ASAP? - Username: account.PreferredName, + d := &data.User{ + UserID: account.PreferredName, DisplayName: account.DisplayName, LegacyDisplayName: account.DisplayName, Email: account.Mail, @@ -82,7 +83,8 @@ func (o Ocs) GetUser(w http.ResponseWriter, r *http.Request) { Relative: 0.18, Definition: "default", }, - })) + } + render.Render(w, r, response.DataRender(d)) } // AddUser creates a new user account @@ -90,7 +92,6 @@ func (o Ocs) AddUser(w http.ResponseWriter, r *http.Request) { // TODO this endpoint needs authentication using the roles and permissions userid := r.PostFormValue("userid") password := r.PostFormValue("password") - username := r.PostFormValue("username") displayname := r.PostFormValue("displayname") email := r.PostFormValue("email") uid := r.PostFormValue("uidnumber") @@ -118,24 +119,20 @@ func (o Ocs) AddUser(w http.ResponseWriter, r *http.Request) { // fallbacks /* TODO decide if we want to make these fallbacks. Keep in mind: - - ocis requires a username and email - - the username should really be different from the userid - if username == "" { - username = userid - } - if displayname == "" { - displayname = username - } + - ocis requires a preferred_name and email */ + if displayname == "" { + displayname = userid + } newAccount := &accounts.Account{ + Id: userid, DisplayName: displayname, - PreferredName: username, - OnPremisesSamAccountName: username, + PreferredName: userid, + OnPremisesSamAccountName: userid, PasswordProfile: &accounts.PasswordProfile{ Password: password, }, - Id: userid, Mail: email, AccountEnabled: true, } @@ -178,12 +175,11 @@ func (o Ocs) AddUser(w http.ResponseWriter, r *http.Request) { } render.Render(w, r, response.DataRender(&data.User{ UserID: account.Id, - Username: account.PreferredName, DisplayName: account.DisplayName, LegacyDisplayName: account.DisplayName, Email: account.Mail, UIDNumber: account.UidNumber, - GIDNumber: account.UidNumber, + GIDNumber: account.GidNumber, Enabled: enabled, })) } @@ -191,9 +187,22 @@ func (o Ocs) AddUser(w http.ResponseWriter, r *http.Request) { // EditUser creates a new user account func (o Ocs) EditUser(w http.ResponseWriter, r *http.Request) { // TODO this endpoint needs authentication + userid := chi.URLParam(r, "userid") + account, err := o.fetchAccountByUsername(r.Context(), userid) + if err != nil { + merr := merrors.FromError(err) + if merr.Code == http.StatusNotFound { + render.Render(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "The requested user could not be found")) + } else { + render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error())) + } + o.logger.Error().Err(err).Str("userid", userid).Msg("could not edit user") + return + } + req := accounts.UpdateAccountRequest{ Account: &accounts.Account{ - Id: chi.URLParam(r, "userid"), + Id: account.Id, }, } key := r.PostFormValue("key") @@ -221,12 +230,10 @@ func (o Ocs) EditUser(w http.ResponseWriter, r *http.Request) { return } - account, err := o.getAccountService().UpdateAccount(r.Context(), &req) + account, err = o.getAccountService().UpdateAccount(r.Context(), &req) if err != nil { merr := merrors.FromError(err) switch merr.Code { - case http.StatusNotFound: - render.Render(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "The requested user could not be found")) case http.StatusBadRequest: render.Render(w, r, response.ErrRender(data.MetaBadRequest.StatusCode, merr.Detail)) default: @@ -247,11 +254,24 @@ func (o Ocs) EditUser(w http.ResponseWriter, r *http.Request) { // DeleteUser deletes a user func (o Ocs) DeleteUser(w http.ResponseWriter, r *http.Request) { - req := accounts.DeleteAccountRequest{ - Id: chi.URLParam(r, "userid"), + userid := chi.URLParam(r, "userid") + account, err := o.fetchAccountByUsername(r.Context(), userid) + if err != nil { + merr := merrors.FromError(err) + if merr.Code == http.StatusNotFound { + render.Render(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "The requested user could not be found")) + } else { + render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error())) + } + o.logger.Error().Err(err).Str("userid", userid).Msg("could not delete user") + return } - _, err := o.getAccountService().DeleteAccount(r.Context(), &req) + req := accounts.DeleteAccountRequest{ + Id: account.Id, + } + + _, err = o.getAccountService().DeleteAccount(r.Context(), &req) if err != nil { merr := merrors.FromError(err) if merr.Code == http.StatusNotFound { @@ -344,7 +364,7 @@ func (o Ocs) ListUsers(w http.ResponseWriter, r *http.Request) { search := r.URL.Query().Get("search") query := "" if search != "" { - query = fmt.Sprintf("id eq '%s' or on_premises_sam_account_name eq '%s'", escapeValue(search), escapeValue(search)) + query = fmt.Sprintf("on_premises_sam_account_name eq '%s'", escapeValue(search)) } res, err := o.getAccountService().ListAccounts(r.Context(), &accounts.ListAccountsRequest{ @@ -368,3 +388,17 @@ func (o Ocs) ListUsers(w http.ResponseWriter, r *http.Request) { func escapeValue(value string) string { return strings.ReplaceAll(value, "'", "''") } + +func (o Ocs) fetchAccountByUsername(ctx context.Context, name string) (*accounts.Account, error) { + var res *accounts.ListAccountsResponse + res, err := o.getAccountService().ListAccounts(ctx, &accounts.ListAccountsRequest{ + Query: fmt.Sprintf("on_premises_sam_account_name eq '%v'", escapeValue(name)), + }) + if err != nil { + return nil, err + } + if res != nil && len(res.Accounts) == 1 { + return res.Accounts[0], nil + } + return nil, merrors.NotFound("", "The requested user could not be found") +} diff --git a/proxy/go.mod b/proxy/go.mod index d13eed229..3d6007392 100644 --- a/proxy/go.mod +++ b/proxy/go.mod @@ -7,8 +7,8 @@ require ( contrib.go.opencensus.io/exporter/ocagent v0.7.0 contrib.go.opencensus.io/exporter/zipkin v0.1.1 github.com/coreos/go-oidc v2.2.1+incompatible - github.com/cs3org/go-cs3apis v0.0.0-20200810113633-b00aca449666 - github.com/cs3org/reva v1.2.1-0.20200826162318-c0f54e1f37ea + github.com/cs3org/go-cs3apis v0.0.0-20200929101248-821df597ec8d + github.com/cs3org/reva v1.3.1-0.20201021065855-dc400f81ecbc github.com/justinas/alice v1.2.0 github.com/micro/cli/v2 v2.1.2 github.com/micro/go-micro/v2 v2.9.1 @@ -23,10 +23,10 @@ require ( github.com/prometheus/client_golang v1.7.1 github.com/restic/calens v0.2.0 github.com/spf13/viper v1.7.0 - go.opencensus.io v0.22.4 + go.opencensus.io v0.22.5 golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d - google.golang.org/grpc v1.31.1 + google.golang.org/grpc v1.32.0 ) replace ( diff --git a/proxy/go.sum b/proxy/go.sum index 27895ec26..98074785e 100644 --- a/proxy/go.sum +++ b/proxy/go.sum @@ -131,6 +131,7 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkY github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg= +github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg= github.com/ascarter/requestid v0.0.0-20170313220838-5b76ab3d4aee h1:3T/l+vMotQ7cDSLWNAn2Vg1SAQ3mdyLgBWWBitSS3uU= github.com/ascarter/requestid v0.0.0-20170313220838-5b76ab3d4aee/go.mod h1:u7Wtt4WATGGgae9mURNGQQqxAudPKrxfsbSDSGOso+g= github.com/asim/go-awsxray v0.0.0-20161209120537-0d8a60b6e205/go.mod h1:frVmN4PtXUuL1EbZn0uL4PHSTKNKFnbMpBIhngqMuNQ= @@ -146,6 +147,8 @@ github.com/aws/aws-sdk-go v1.28.2/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN github.com/aws/aws-sdk-go v1.29.26/go.mod h1:1KvfttTE3SPKMpo8g2c6jL3ZKfXtFvKscTgahTma5Xg= github.com/aws/aws-sdk-go v1.33.19/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= github.com/aws/aws-sdk-go v1.34.2/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= +github.com/aws/aws-sdk-go v1.34.12/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= +github.com/aws/aws-sdk-go v1.35.9/go.mod h1:tlPOdRjfxPBpNIwqDj61rmsnA85v9jc0Ps9+muhnW+k= github.com/aws/aws-xray-sdk-go v0.9.4/go.mod h1:XtMKdBQfpVut+tJEwI7+dJFRxxRdxHDyVNp2tHXRq04= github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f/go.mod h1:AuiFmCCPBSrqvVMvuqFuk0qogytodnVFVSN5CeJB8Gc= github.com/beevik/etree v1.1.0/go.mod h1:r8Aw8JqVegEf0w2fDnATrX9VpkMcyFeM0FhwO62wh+A= @@ -184,6 +187,7 @@ github.com/bwmarrin/discordgo v0.19.0/go.mod h1:O9S4p+ofTFwB02em7jkpkV8M3R0/PUVO github.com/bwmarrin/discordgo v0.20.1/go.mod h1:O9S4p+ofTFwB02em7jkpkV8M3R0/PUVOwN61zSZ0r4Q= github.com/bwmarrin/discordgo v0.20.2/go.mod h1:O9S4p+ofTFwB02em7jkpkV8M3R0/PUVOwN61zSZ0r4Q= github.com/c-bata/go-prompt v0.2.3/go.mod h1:VzqtzE2ksDBcdln8G7mk2RX9QyGjH+OVqOCSiVIqS34= +github.com/c-bata/go-prompt v0.2.5/go.mod h1:vFnjEGDIIA/Lib7giyE4E9c50Lvl8j0S+7FVlAwDAVw= github.com/caddyserver/certmagic v0.10.6/go.mod h1:Y8jcUBctgk/IhpAzlHKfimZNyXCkfGgRTC0orl8gROQ= github.com/cenkalti/backoff v2.1.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= @@ -260,17 +264,22 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3 github.com/crewjam/httperr v0.0.0-20190612203328-a946449404da/go.mod h1:+rmNIXRvYMqLQeR4DHyTvs6y0MEMymTz4vyFpFkKTPs= github.com/crewjam/saml v0.4.0/go.mod h1:geQUbAAwmTKNJFDzoXaTssZHY26O89PHIm3K3YWjWnI= github.com/cs3org/cato v0.0.0-20200626150132-28a40e643719/go.mod h1:XJEZ3/EQuI3BXTp/6DUzFr850vlxq11I6satRtz0YQ4= +github.com/cs3org/cato v0.0.0-20200828125504-e418fc54dd5e/go.mod h1:XJEZ3/EQuI3BXTp/6DUzFr850vlxq11I6satRtz0YQ4= github.com/cs3org/go-cs3apis v0.0.0-20191128165347-19746c015c83/go.mod h1:IsVGyZrOLUQD48JIhlM/xb3Vz6He5o2+W0ZTfUGY+IU= github.com/cs3org/go-cs3apis v0.0.0-20200306065539-29abc33f5be0/go.mod h1:UXha4TguuB52H14EMoSsCqDj7k8a/t7g4gVP+bgY5LY= github.com/cs3org/go-cs3apis v0.0.0-20200408065125-6e23f3ecec0a/go.mod h1:UXha4TguuB52H14EMoSsCqDj7k8a/t7g4gVP+bgY5LY= github.com/cs3org/go-cs3apis v0.0.0-20200730121022-c4f3d4f7ddfd/go.mod h1:UXha4TguuB52H14EMoSsCqDj7k8a/t7g4gVP+bgY5LY= github.com/cs3org/go-cs3apis v0.0.0-20200810113633-b00aca449666 h1:E7VsSSN/2YZLSwrDMJJdAWU11lP7W1qkcXbrslb0PM0= github.com/cs3org/go-cs3apis v0.0.0-20200810113633-b00aca449666/go.mod h1:UXha4TguuB52H14EMoSsCqDj7k8a/t7g4gVP+bgY5LY= +github.com/cs3org/go-cs3apis v0.0.0-20200929101248-821df597ec8d/go.mod h1:UXha4TguuB52H14EMoSsCqDj7k8a/t7g4gVP+bgY5LY= github.com/cs3org/reva v0.0.2-0.20200115110931-4c7513415ec5/go.mod h1:Hk3eCcdhtv4eIhKvRK736fQuOyS1HuHnUcz0Dq6NK1A= github.com/cs3org/reva v0.1.0/go.mod h1:8j6QyyAq9Kjj7RPfJb7M1aEmw5DmsuCJKUULXxYOyRo= github.com/cs3org/reva v1.1.0/go.mod h1:fBzTrNuAKdQ62ybjpdu8nyhBin90/3/3s6DGQDCdBp4= github.com/cs3org/reva v1.2.1-0.20200826162318-c0f54e1f37ea h1:xVyJvR+GoaBrRJV6GnDflgm9bzkmjchCBBg0nMiHu6M= github.com/cs3org/reva v1.2.1-0.20200826162318-c0f54e1f37ea/go.mod h1:FvYB+UPpuPCw0hH42qHVR1R2atZyy69METZI/zEaST8= +github.com/cs3org/reva v1.2.2-0.20200924071957-e6676516e61e/go.mod h1:DOV5SjpOBKN+aWfOHLdA4KiLQkpyC786PQaXEdRAZ0M= +github.com/cs3org/reva v1.3.1-0.20201021065855-dc400f81ecbc h1:8y6N/bscmzdXd4yAv6bIJiqggter8iEmb4cYMhuZkE8= +github.com/cs3org/reva v1.3.1-0.20201021065855-dc400f81ecbc/go.mod h1:rTJhfVoZggB5iSPH5oWqQSO+W1iTQIxNmaX/ueS9GAU= github.com/cznic/b v0.0.0-20181122101859-a26611c4d92d/go.mod h1:URriBxXwVq5ijiJ12C7iIZqlA69nTlI+LgI6/pwftG8= github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548/go.mod h1:e6NPNENfs9mPDVNRekM7lKScauxd5kXTr1Mfyig6TDM= github.com/cznic/strutil v0.0.0-20181122101858-275e90344537/go.mod h1:AHHPPPXTw0h6pVabbcbyGRK1DckRn7r/STdZEeIDzZc= @@ -282,8 +291,12 @@ github.com/deckarep/golang-set v1.7.1/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14y github.com/decker502/dnspod-go v0.2.0/go.mod h1:qsurYu1FgxcDwfSwXJdLt4kRsBLZeosEb9uq4Sy+08g= github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f/go.mod h1:xH/i4TFMt8koVQZ6WFms69WAsDWr2XsYL3Hkl7jkoLE= github.com/devigned/tab v0.1.1/go.mod h1:XG9mPq0dFghrYvoBF3xdRrJzSTX1b7IQrvaL9mzjeJY= +github.com/dgraph-io/ristretto v0.0.1/go.mod h1:T40EBc7CJke8TkpiYfGGKAeFjSaxuFXhuXRyumBd6RE= +github.com/dgraph-io/ristretto v0.0.2/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= +github.com/dgraph-io/ristretto v0.0.3/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8= github.com/dnaeon/go-vcr v0.0.0-20180814043457-aafff18a5cc2/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= @@ -379,6 +392,7 @@ github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2 github.com/go-ldap/ldap v3.0.2+incompatible/go.mod h1:qfd9rJvER9Q0/D/Sqn1DfHRoBp40uXYvFoEVrNEPqRc= github.com/go-ldap/ldap/v3 v3.1.7/go.mod h1:5Zun81jBTabRaI8lzN7E1JjyEl1g6zI6u9pd8luAK4Q= github.com/go-ldap/ldap/v3 v3.2.3/go.mod h1:iYS1MdmrmceOJ1QOTnRXrIs7i3kloqtmGQjRvjKpyMg= +github.com/go-ldap/ldap/v3 v3.2.4/go.mod h1:iYS1MdmrmceOJ1QOTnRXrIs7i3kloqtmGQjRvjKpyMg= github.com/go-log/log v0.1.0/go.mod h1:4mBwpdRMFLiuXZDCwU2lKQFsoSCo72j3HqBK9d81N2M= github.com/go-log/log v0.2.0/go.mod h1:xzCnwajcues/6w7lne3yK2QU7DBPW7kqbgPGG5AF65U= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= @@ -621,6 +635,7 @@ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvq github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/gomodule/redigo v1.8.2/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0= @@ -687,6 +702,7 @@ github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de github.com/grpc-ecosystem/go-grpc-middleware v1.1.0/go.mod h1:f5nM7jw/oeRSadq3xCzHAvxcr8HZnzsqU6ILg/0NiiE= github.com/grpc-ecosystem/go-grpc-middleware v1.2.0/go.mod h1:mJzapYve32yjrKlk9GbyCZHuPgZsrbyIbyKhSzOpg6s= github.com/grpc-ecosystem/go-grpc-middleware v1.2.1/go.mod h1:EaizFBKfUKtMIF5iaDEhniwNedqGo9FuLFzppDr3uwI= +github.com/grpc-ecosystem/go-grpc-middleware v1.2.2/go.mod h1:EaizFBKfUKtMIF5iaDEhniwNedqGo9FuLFzppDr3uwI= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.8.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= @@ -748,6 +764,7 @@ github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw= github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= +github.com/iancoleman/strcase v0.1.2/go.mod h1:SK73tn/9oHe+/Y0h39VT4UCxmurVJkR5NA7kMEAOgSE= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df/go.mod h1:QMZY7/J/KSQEhKWFeDesPjMj+wCHReeknARU3wqlyN4= github.com/ijc/Gotty v0.0.0-20170406111628-a8b993ba6abd/go.mod h1:3LVOLeyx9XVvwPgrt2be44XgSqndprz1G18rSk8KD84= @@ -770,6 +787,8 @@ github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a/go.mod h1:yL958EeXv8 github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik= +github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/jmhodges/levigo v1.0.0/go.mod h1:Q6Qx+uH3RAqyK4rFQroq9RL7mdkABMcfhEI+nNuzMJQ= github.com/jmoiron/sqlx v0.0.0-20180614180643-0dae4fefe7c0/go.mod h1:IiEW3SEiiErVyFdH8NTuWjSifiEQKUoyK3LNqr2kCHU= github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks= @@ -810,6 +829,7 @@ github.com/kolo/xmlrpc v0.0.0-20190717152603-07c4ee3fd181/go.mod h1:o03bZfuBwAXH github.com/konsorten/go-windows-terminal-sequences v0.0.0-20180402223658-b729f2633dfe/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= @@ -874,6 +894,7 @@ github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNx github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= +github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= @@ -888,8 +909,10 @@ github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsO github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-tty v0.0.0-20180219170247-931426f7535a/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE= +github.com/mattn/go-tty v0.0.3/go.mod h1:ihxohKRERHTVzN+aSVRwACLCeqIoZAWpoICkkvrWyR0= github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw= github.com/mattn/goveralls v0.0.5/go.mod h1:Xg2LHi51faXLyKXwsndxiW6uxEEQT9+3sjGzzwU4xy0= +github.com/mattn/goveralls v0.0.6/go.mod h1:h8b4ow6FxSPMQHF6o2ve3qsclnffZjYTNEKmLesRwqw= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mendsley/gojwk v0.0.0-20141217222730-4d5ec6e58103/go.mod h1:o9YPB5aGP8ob35Vy6+vyq3P3bWe7NQWzf+JLiXCiMaE= @@ -954,6 +977,7 @@ github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:F github.com/mitchellh/mapstructure v1.0.0/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.3.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.3.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.3.3 h1:SzB1nHZ2Xi+17FP0zVQBHIZqvwRN9408fJO8h+eeNA8= github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= @@ -1050,13 +1074,17 @@ github.com/ory/fosite v0.29.0/go.mod h1:0atSZmXO7CAcs6NPMI/Qtot8tmZYj04Nddoold4S github.com/ory/fosite v0.30.2/go.mod h1:Lq9qQ9Sl6mcea2Tt8J7PU+wUeFYPZ+vg7N3zPVKGbN8= github.com/ory/fosite v0.30.4/go.mod h1:Lq9qQ9Sl6mcea2Tt8J7PU+wUeFYPZ+vg7N3zPVKGbN8= github.com/ory/fosite v0.32.2/go.mod h1:UeBhRgW6nAjTcd8S7kAo0IFsY/rTPyOXPq/t8N20Q8I= +github.com/ory/fosite v0.33.0/go.mod h1:h+ize9gk0GvRyGjabriqSEmTkMhny+O95cijb8DVqPE= +github.com/ory/fosite v0.35.0/go.mod h1:h+ize9gk0GvRyGjabriqSEmTkMhny+O95cijb8DVqPE= github.com/ory/go-acc v0.0.0-20181118080137-ddc355013f90/go.mod h1:sxnvPCxChFuSmTJGj8FdMupeq1BezCiEpDjTUXQ4hf4= github.com/ory/go-acc v0.2.1/go.mod h1:0omgy2aa3nDBJ45VAKeLHH8ccPBudxLeic4xiDRtug0= +github.com/ory/go-acc v0.2.5/go.mod h1:4Kb/UnPcT8qRAk3IAxta+hvVapdxTLWtrr7bFLlEgpw= github.com/ory/go-convenience v0.1.0/go.mod h1:uEY/a60PL5c12nYz4V5cHY03IBmwIAEm8TWB0yn9KNs= github.com/ory/gojsonreference v0.0.0-20190720135523-6b606c2d8ee8/go.mod h1:wsH1C4nIeeQClDtD5AH7kF1uTS6zWyqfjVDTmB0Em7A= github.com/ory/gojsonschema v1.1.1-0.20190919112458-f254ca73d5e9/go.mod h1:BNZpdJgB74KOLSsWFvzw6roXg1I6O51WO8roMmW+T7Y= github.com/ory/herodot v0.6.2/go.mod h1:3BOneqcyBsVybCPAJoi92KN2BpJHcmDqAMcAAaJiJow= github.com/ory/viper v1.5.6/go.mod h1:TYmpFpKLxjQwvT4f0QPpkOn4sDXU1kDgAwJpgLYiQ28= +github.com/ory/viper v1.7.5/go.mod h1:ypOuyJmEUb3oENywQZRgeAMwqgOyDqwboO1tj3DjTaM= github.com/ory/x v0.0.85/go.mod h1:s44V8t3xyjWZREcU+mWlp4h302rTuM4aLXcW+y5FbQ8= github.com/ovh/go-ovh v0.0.0-20181109152953-ba5adb4cf014/go.mod h1:joRatxRJaZBsY3JAOEMcoOp05CnZzsx4scTxi95DHyQ= github.com/owncloud/flaex v0.0.0-20200411150708-dce59891a203/go.mod h1:jip86t4OVURJTf8CM/0e2qcji/Y4NG3l2lR8kex4JWw= @@ -1100,6 +1128,7 @@ github.com/owncloud/ocis-store v0.1.1/go.mod h1:Rav5iw0fZXYFqJl81IbyTVa/FidaBhgV github.com/owncloud/ocis-thumbnails v0.2.0/go.mod h1:97QGLawjcpbhSIscbPXJAytE6vAQi5Y+Ov55h8/mq+0= github.com/owncloud/ocis-thumbnails v0.3.0/go.mod h1:97QGLawjcpbhSIscbPXJAytE6vAQi5Y+Ov55h8/mq+0= github.com/owncloud/ocis-webdav v0.1.1/go.mod h1:E9OlGfvlxf1NHFOvFgbHWBe7/pw4Gqy/JaSJFEHQSzg= +github.com/owncloud/ocis/storage v0.0.0-20201015120921-38358ba4d4df/go.mod h1:s9kJvxtBlHEi5qc1TuPAdz2bprk9yGFe+FSOeC76Pbs= github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c h1:rp5dCmg/yLR3mgFuSOe4oEnDDmGLROTvMragMUXpTQw= github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c/go.mod h1:X07ZCGwUbLaax7L0S3Tw4hpejzu63ZrrQiUe6W0hcy0= github.com/parnurzeal/gorequest v0.2.15/go.mod h1:3Kh2QUMJoqw3icWAecsyzkpY7UzRfDhbRdTjtNwNiUE= @@ -1114,6 +1143,7 @@ github.com/pelletier/go-toml v1.4.0 h1:u3Z1r+oOXJIkxqw34zVhyPgjBsm6X2wn21NWs/HfS github.com/pelletier/go-toml v1.4.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo= github.com/pelletier/go-toml v1.6.0 h1:aetoXYr0Tv7xRU/V4B4IZJ2QcbtMUFoNb3ORp7TzIK4= github.com/pelletier/go-toml v1.6.0/go.mod h1:5N711Q9dKgbdkxHL+MEfF31hpT7l0S0s/t2kKREewys= +github.com/pelletier/go-toml v1.8.0/go.mod h1:D6yutnOGMveHEPV7VQOuvI/gXY61bv+9bAOTRnLElKs= github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= @@ -1123,7 +1153,9 @@ github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= +github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= github.com/pkg/term v0.0.0-20200520122047-c3ffed290a03/go.mod h1:Z9+Ul5bCbBKnbCvdOWbLqTHhJiYV414CURZJba6L8qA= +github.com/pkg/term v1.1.0/go.mod h1:E25nymQcrSllhX42Ok8MRm1+hyBdHY0dCeiKZ9jpNGw= github.com/pkg/xattr v0.4.1/go.mod h1:W2cGD0TBEus7MkUgv0tNZ9JutLtVO3cXu+IBRuHqnFs= github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -1194,6 +1226,7 @@ github.com/rs/zerolog v1.17.2/go.mod h1:9nvC1axdVrAHcu/s9taAVfBuIdTZLVQmKQyvrUjF github.com/rs/zerolog v1.18.0/go.mod h1:9nvC1axdVrAHcu/s9taAVfBuIdTZLVQmKQyvrUjF5+I= github.com/rs/zerolog v1.19.0 h1:hYz4ZVdUgjXTBUmrkrw55j1nHx68LfOKIQk5IYtyScg= github.com/rs/zerolog v1.19.0/go.mod h1:IzD0RJ65iWH0w97OQQebJEvTZYvsCUm9WVLWBQrJRjo= +github.com/rs/zerolog v1.20.0/go.mod h1:IzD0RJ65iWH0w97OQQebJEvTZYvsCUm9WVLWBQrJRjo= github.com/rubenv/sql-migrate v0.0.0-20190212093014-1007f53448d7/go.mod h1:WS0rl9eEliYI8DPnr3TOwz4439pay+qNgzJoVya/DmY= github.com/russellhaering/goxmldsig v0.0.0-20180430223755-7acd5e4a6ef7/go.mod h1:Oz4y6ImuOQZxynhbSXk7btjEfNBtGlj2dcaOvXl2FSM= github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo= @@ -1250,6 +1283,7 @@ github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B github.com/spf13/afero v1.2.0/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= +github.com/spf13/afero v1.3.2/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= github.com/spf13/cast v1.2.0/go.mod h1:r2rcYCSwa1IExKTDiTfzaxqT2FNHs8hODu4LnUfgKEg= github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= @@ -1374,6 +1408,7 @@ go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4 h1:LYy1Hy3MJdrCdMwwzxA/dRok4ejH+RwNGbuoD9fCjto= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0 h1:OI5t8sDa1Or+q8AeE+yKeB/SDYioSHAgcVljj9JIETY= @@ -1425,6 +1460,7 @@ golang.org/x/crypto v0.0.0-20190418165655-df01cb2cc480/go.mod h1:WFFai1msRO1wXaE golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190829043050-9756ffdc2472/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= @@ -1442,6 +1478,7 @@ golang.org/x/crypto v0.0.0-20200420201142-3c4aac89819a/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a h1:vclmkQCjlDX5OydZ9wv8rBCcS0QyQY66Mpf/7BZbInM= golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -1607,17 +1644,20 @@ golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191105231009-c1f44814a5cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191110163157-d32e6e3b99c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191113165036-4c7a9d0fe056/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200219091948-cb0a6d8edb6c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1627,10 +1667,14 @@ golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1 h1:ogLJMz+qpzav7lGMh10LMvAkM/fAoGlaiiHYiFYdm80= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200720211630-cb9d2d5c5666/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6 h1:DvY3Zkh7KabQE/kfzMvYvKirSiguP9Q/veMtkYyf0o8= golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200918174421-af09f7315aff/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1712,7 +1756,9 @@ golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapK golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= golang.org/x/tools v0.0.0-20200421042724-cfa8b22178d2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200427214658-4697a2867c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200522201501-cb1345f3a375/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200526224456-8b020aee10d2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200721223218-6123e77877b2/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200811215021-48a8ffc5b207/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200817023811-d00afeaade8f/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -1823,6 +1869,7 @@ gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.51.1 h1:GyboHr4UqMiLUybYjd22ZjQIKEJEpgtLXtuGbR21Oho= gopkg.in/ini.v1 v1.51.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.57.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/jcmturner/aescts.v1 v1.0.1/go.mod h1:nsR8qBOg+OucoIW+WMhB3GspUQXq9XorLnQb9XtvcOo= gopkg.in/jcmturner/dnsutils.v1 v1.0.1/go.mod h1:m3v+5svpVOhtFAP/wSz+yzh4Mc0Fg7eRhxkJMWSIz9Q= gopkg.in/jcmturner/goidentity.v3 v3.0.0/go.mod h1:oG2kH0IvSYNIu80dVAyu/yoefjq1mNfM5bm88whjWx4= @@ -1844,6 +1891,7 @@ gopkg.in/square/go-jose.v2 v2.4.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76 gopkg.in/square/go-jose.v2 v2.4.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/square/go-jose.v2 v2.5.0 h1:OZ4sdq+Y+SHfYB7vfthi1Ei8b0vkP8ZPQgUfUwdUSqo= gopkg.in/square/go-jose.v2 v2.5.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/src-d/go-billy.v4 v4.3.2/go.mod h1:nDjArDMp+XMs1aFAESLRjfGSgfvoYN0hDfzEk0GjC98= gopkg.in/src-d/go-git-fixtures.v3 v3.5.0/go.mod h1:dLBcvytrw/TYZsNTWCnkNF2DSIlzWYqTe3rJR56Ac7g= gopkg.in/src-d/go-git.v4 v4.13.1/go.mod h1:nx5NYcxdKxq5fpltdHnPa2Exj4Sx0EclMWZQbYDu2z8= diff --git a/settings/ui/tests/config/drone/ocis-config.json b/settings/ui/tests/config/drone/ocis-config.json index 0bf00b0aa..ab2848cdb 100644 --- a/settings/ui/tests/config/drone/ocis-config.json +++ b/settings/ui/tests/config/drone/ocis-config.json @@ -12,7 +12,6 @@ "apps": [ "files", "draw-io", - "pdf-viewer", "markdown-editor", "media-viewer" ], diff --git a/storage/pkg/service/external/external_test.go b/storage/pkg/service/external/external_test.go index 82f2e1ce0..4b65ad120 100644 --- a/storage/pkg/service/external/external_test.go +++ b/storage/pkg/service/external/external_test.go @@ -1,4 +1,5 @@ package external + // //import ( // "context"