diff --git a/changelog/unreleased/support-signed-urls.md b/changelog/unreleased/support-signed-urls.md new file mode 100644 index 0000000000..567a82c9e0 --- /dev/null +++ b/changelog/unreleased/support-signed-urls.md @@ -0,0 +1,8 @@ +Enhancement: Support signed URLs + +We added a middleware that verifies signed urls as generated by the owncloud-sdk. This allows directly downloading large files with browsers instead of using `blob://` urls, which eats memory ... + +https://github.com/owncloud/ocis-proxy/issues/73 +https://github.com/owncloud/ocis-proxy/pull/75 +https://github.com/owncloud/ocis-ocs/pull/18 +https://github.com/owncloud/owncloud-sdk/pull/504 diff --git a/go.mod b/go.mod index a3306f07f6..7791122bb6 100644 --- a/go.mod +++ b/go.mod @@ -16,11 +16,13 @@ require ( github.com/openzipkin/zipkin-go v0.2.2 github.com/owncloud/flaex v0.2.0 github.com/owncloud/ocis-accounts v0.1.2-0.20200618163128-aa8ae58dd95e - github.com/owncloud/ocis-pkg/v2 v2.2.1 + github.com/owncloud/ocis-pkg/v2 v2.2.2-0.20200527082518-5641fa4a4c8c + github.com/owncloud/ocis-store v0.0.0-20200716140351-f9670592fb7b github.com/prometheus/client_golang v1.7.0 github.com/restic/calens v0.2.0 github.com/spf13/viper v1.7.0 go.opencensus.io v0.22.4 + golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899 golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d google.golang.org/grpc v1.29.1 gopkg.in/square/go-jose.v2 v2.4.0 // indirect diff --git a/go.sum b/go.sum index 1abba4df56..0e414f236f 100644 --- a/go.sum +++ b/go.sum @@ -123,6 +123,7 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPd github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= 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/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,7 @@ github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJm github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/blevesearch/bleve v1.0.9/go.mod h1:tb04/rbU29clbtNgorgFd8XdJea4x3ybYaOjWKr+UBU= github.com/blevesearch/blevex v0.0.0-20190916190636-152f0fe5c040/go.mod h1:WH+MU2F4T0VmSdaPX+Wu5GYoZBrYWdOZWSjzvYcDmqQ= +github.com/blevesearch/cld2 v0.0.0-20200327141045-8b5f551d37f5/go.mod h1:PN0QNTLs9+j1bKy3d/GB/59wsNBFC4sWLWG3k69lWbc= github.com/blevesearch/go-porterstemmer v1.0.3/go.mod h1:angGc5Ht+k2xhJdZi511LtmxuEf0OVpvUUNrwmM1P7M= github.com/blevesearch/mmap-go v1.0.2/go.mod h1:ol2qBqYaOUsGdm7aRMRrYGgPvnwLe6Y+7LMvAB5IbSA= github.com/blevesearch/segment v0.9.0/go.mod h1:9PfHYUdQCgHktBgvtUOF4x+pc4/l8rdH0u5spnW85UQ= @@ -331,6 +333,7 @@ github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1 github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= github.com/go-openapi/strfmt v0.19.2/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU= github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= +github.com/go-ozzo/ozzo-validation/v4 v4.2.1/go.mod h1:2NKgrcHl3z6cJs+3Oo940FPRiTzuqKbvfrL2RxCj6Ew= github.com/go-playground/locales v0.12.1/go.mod h1:IUMDtCfWo/w/mtMfIE/IG2K+Ey3ygWanZIBtBW0W2TM= github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= github.com/go-playground/universal-translator v0.16.0/go.mod h1:1AnU7NaIRDWWzGEKwgtJRd2xk99HeFyHw3yid4rvQIY= @@ -399,6 +402,8 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/go-replayers/grpcreplay v0.1.0/go.mod h1:8Ig2Idjpr6gifRd6pNVggX6TC1Zw6Jx74AKp7QNH2QE= @@ -481,6 +486,7 @@ github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjG github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-version v1.0.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= @@ -509,6 +515,7 @@ github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmK 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= +github.com/ikawaha/kagome.ipadic v1.1.2/go.mod h1:DPSBbU0czaJhAb/5uKQZHMc9MTVRpDugJfX+HddPHHg= github.com/imdario/mergo v0.3.7/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.8 h1:CGgOkSJeqMRmt0D9XLWExdT4m4F1vd3FV3VPt+0VxkQ= github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= @@ -641,6 +648,7 @@ github.com/mitchellh/go-vnc v0.0.0-20150629162542-723ed9867aed/go.mod h1:3rdaFaC github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= +github.com/mitchellh/gox v1.0.1/go.mod h1:ED6BioOGXMswlXa2zxfh/xdd5QhwYliBFn9V18Ap4z4= github.com/mitchellh/hashstructure v1.0.0 h1:ZkRJX1CyOoTkar7p/mLS5TZU4nJ1Rn/F8u9dGS02Q3Y= github.com/mitchellh/hashstructure v1.0.0/go.mod h1:QjSHrPWS+BGUVBYkbTZWEnOh3G1DutKwClXU/ABz6AQ= github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= @@ -738,7 +746,12 @@ github.com/owncloud/ocis-pkg v1.2.1-0.20191217084055-eab942498596/go.mod h1:Wo0Q github.com/owncloud/ocis-pkg/v2 v2.0.1/go.mod h1:7bVnn3VUaqdmvpMkXF0QVEF1fRugs35hSkuVTAq9yjk= github.com/owncloud/ocis-pkg/v2 v2.2.1 h1:LK7WxHYugEFQ9NHTOz0EP8DRjbt51wXhyqruV03z6zI= github.com/owncloud/ocis-pkg/v2 v2.2.1/go.mod h1:MXv7QzsYsu4YWuyJxhq1kLLmJa/r5gbqHe1FXulMHaw= +github.com/owncloud/ocis-pkg/v2 v2.2.2-0.20200527082518-5641fa4a4c8c h1:hYhKSfMkPO4kRLrKqRHPmePGTCpGDGji+s4yW30+tmM= +github.com/owncloud/ocis-pkg/v2 v2.2.2-0.20200527082518-5641fa4a4c8c/go.mod h1:s894msGwDsULmsROHkbsXFCP/eSqDcteDFUntZOiJdc= github.com/owncloud/ocis-settings v0.0.0-20200522101320-46ea31026363/go.mod h1:/h0ceztOoFc3KAnm8nqZI4zwsaaZK9q4MTgtintwsXc= +github.com/owncloud/ocis-settings v0.0.0-20200629120229-69693c5f8f43/go.mod h1:AeXZVHKEU+9Xt4+/lkHE5rx+sJH2if9dIrUGLhe+JOY= +github.com/owncloud/ocis-store v0.0.0-20200716140351-f9670592fb7b h1:tjfH02oEawuMdMt3pJdCjFyuWgNRUjV7rdjoTF56Mrw= +github.com/owncloud/ocis-store v0.0.0-20200716140351-f9670592fb7b/go.mod h1:7WRMnx4ffwtckNl4qD2Gj/d5fvl84jyydOV2FbUUu3A= 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= @@ -889,11 +902,14 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.0 h1:jlIyCplCJFULU/01vCkhKuTyc3OorI3bJFuw6obfgho= +github.com/stretchr/testify v1.6.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stvp/tempredis v0.0.0-20181119212430-b82af8480203/go.mod h1:oqN97ltKNihBbwlX8dLpwxCl3+HnXKV/R0e+sRLd9C8= 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/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= +github.com/tebeka/snowball v0.4.2/go.mod h1:4IfL14h1lvwZcp1sfXuuc7/7yCsvVffTWxWxCLfFpYg= 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/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= @@ -1001,6 +1017,8 @@ golang.org/x/crypto v0.0.0-20200320181102-891825fb96df h1:lDWgvUvNnaTnNBc/dwOty8 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-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= @@ -1032,6 +1050,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-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 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= @@ -1211,6 +1231,8 @@ 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-20200427214658-4697a2867c88 h1:Nj7oNnL9tSACMt2JvszZN6P4IXiy1t6E/YRMr7YtaSw= golang.org/x/tools v0.0.0-20200427214658-4697a2867c88/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/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= @@ -1283,6 +1305,8 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2 google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.24.0 h1:UhZDfRO8JRQru4/+LlLE0BRKGF8L+PICnvYZmx/fEGA= google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= gopkg.in/DataDog/dd-trace-go.v1 v1.19.0/go.mod h1:DVp8HmDh8PuTu2Z0fVVlBsyWaC++fzwVCaGWylTe3tg= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk= @@ -1343,6 +1367,8 @@ gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8/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= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/pkg/command/server.go b/pkg/command/server.go index e9b30b9dd4..fcec85dd4a 100644 --- a/pkg/command/server.go +++ b/pkg/command/server.go @@ -22,6 +22,7 @@ import ( "contrib.go.opencensus.io/exporter/zipkin" "github.com/micro/cli/v2" mclient "github.com/micro/go-micro/v2/client" + "github.com/micro/go-micro/v2/client/grpc" "github.com/oklog/run" openzipkin "github.com/openzipkin/zipkin-go" zipkinhttp "github.com/openzipkin/zipkin-go/reporter/http" @@ -32,6 +33,7 @@ import ( "github.com/owncloud/ocis-proxy/pkg/proxy" "github.com/owncloud/ocis-proxy/pkg/server/debug" proxyHTTP "github.com/owncloud/ocis-proxy/pkg/server/http" + storepb "github.com/owncloud/ocis-store/pkg/proto/v0" "go.opencensus.io/stats/view" "go.opencensus.io/trace" ) @@ -245,6 +247,37 @@ func Server(cfg *config.Config) *cli.Command { } func loadMiddlewares(ctx context.Context, l log.Logger, cfg *config.Config) alice.Chain { + + psMW := middleware.PresignedURL( + middleware.Logger(l), + middleware.Store(storepb.NewStoreService("com.owncloud.api.store", grpc.NewClient())), + ) + + // TODO this won't work with a registry other than mdns. Look into Micro's client initialization. + // https://github.com/owncloud/ocis-proxy/issues/38 + accounts := acc.NewAccountsService("com.owncloud.api.accounts", mclient.DefaultClient) + + uuidMW := middleware.AccountUUID( + middleware.Logger(l), + middleware.TokenManagerConfig(cfg.TokenManager), + middleware.AccountsClient(accounts), + ) + + // the connection will be established in a non blocking fashion + sc, err := cs3.GetGatewayServiceClient(cfg.Reva.Address) + if err != nil { + l.Error().Err(err). + Str("gateway", cfg.Reva.Address). + Msg("Failed to create reva gateway service client") + } + + chMW := middleware.CreateHome( + middleware.Logger(l), + middleware.RevaGatewayClient(sc), + middleware.AccountsClient(accounts), + middleware.TokenManagerConfig(cfg.TokenManager), + ) + if cfg.OIDC.Issuer != "" { l.Info().Msg("Loading OIDC-Middleware") l.Debug().Interface("oidc_config", cfg.OIDC).Msg("OIDC-Config") @@ -274,33 +307,8 @@ func loadMiddlewares(ctx context.Context, l log.Logger, cfg *config.Config) alic middleware.OIDCProviderFunc(provider), ) - // TODO this won't work with a registry other than mdns. Look into Micro's client initialization. - // https://github.com/owncloud/ocis-proxy/issues/38 - accounts := acc.NewAccountsService("com.owncloud.api.accounts", mclient.DefaultClient) - - uuidMW := middleware.AccountUUID( - middleware.Logger(l), - middleware.TokenManagerConfig(cfg.TokenManager), - middleware.AccountsClient(accounts), - ) - - // the connection will be established in a non blocking fashion - sc, err := cs3.GetGatewayServiceClient(cfg.Reva.Address) - if err != nil { - l.Error().Err(err). - Str("gateway", cfg.Reva.Address). - Msg("Failed to create reva gateway service client") - } - - chMW := middleware.CreateHome( - middleware.Logger(l), - middleware.RevaGatewayClient(sc), - middleware.AccountsClient(accounts), - middleware.TokenManagerConfig(cfg.TokenManager), - ) - - return alice.New(middleware.RedirectToHTTPS, oidcMW, uuidMW, chMW) + return alice.New(middleware.RedirectToHTTPS, oidcMW, psMW, uuidMW, chMW) } - return alice.New(middleware.RedirectToHTTPS) + return alice.New(middleware.RedirectToHTTPS, psMW, uuidMW, chMW) } diff --git a/pkg/middleware/account_uuid.go b/pkg/middleware/account_uuid.go index 529dfc67af..d7969f9dda 100644 --- a/pkg/middleware/account_uuid.go +++ b/pkg/middleware/account_uuid.go @@ -13,23 +13,23 @@ import ( oidc "github.com/owncloud/ocis-pkg/v2/oidc" ) -func getAccount(l log.Logger, claims *oidc.StandardClaims, ac acc.AccountsService) (account *acc.Account, status int) { - entry, err := svcCache.Get(AccountsKey, claims.Email) +func getAccount(l log.Logger, ac acc.AccountsService, query string) (account *acc.Account, status int) { + entry, err := svcCache.Get(AccountsKey, query) if err != nil { - l.Debug().Msgf("No cache entry for %v", claims.Email) + l.Debug().Msgf("No cache entry for %s", query) resp, err := ac.ListAccounts(context.Background(), &acc.ListAccountsRequest{ - Query: fmt.Sprintf("mail eq '%s'", strings.ReplaceAll(claims.Email, "'", "''")), + Query: query, PageSize: 2, }) if err != nil { - l.Error().Err(err).Str("email", claims.Email).Msgf("Error fetching from accounts-service") + l.Error().Err(err).Str("query", query).Msgf("Error fetching from accounts-service") status = http.StatusInternalServerError return } if len(resp.Accounts) <= 0 { - l.Error().Str("email", claims.Email).Msgf("Account not found") + l.Error().Str("query", query).Msgf("Account not found") status = http.StatusNotFound return } @@ -37,20 +37,21 @@ func getAccount(l log.Logger, claims *oidc.StandardClaims, ac acc.AccountsServic // TODO provision account if len(resp.Accounts) > 1 { - l.Error().Str("email", claims.Email).Msgf("More than one account with this email found. Not logging user in.") + l.Error().Str("query", query).Msgf("More than one account found. Not logging user in.") status = http.StatusForbidden return } - err = svcCache.Set(AccountsKey, claims.Email, *resp.Accounts[0]) + err = svcCache.Set(AccountsKey, query, *resp.Accounts[0]) if err != nil { - l.Err(err).Str("email", claims.Email).Msgf("Could not cache user") + l.Err(err).Str("query", query).Msgf("Could not cache user") status = http.StatusInternalServerError return } account = resp.Accounts[0] } else { + l.Debug().Msgf("using cache entry for %s", query) a, ok := entry.V.(acc.Account) // TODO how can we directly point to the cached account? if !ok { status = http.StatusInternalServerError @@ -104,10 +105,17 @@ func AccountUUID(opts ...Option) func(next http.Handler) http.Handler { return } - // TODO allow lookup by username? - // TODO allow lookup by custom claim, eg an id - - account, status := getAccount(l, claims, opt.AccountsClient) + var account *acc.Account + var status int + if claims.Email != "" { + account, status = getAccount(l, opt.AccountsClient, fmt.Sprintf("mail eq '%s'", strings.ReplaceAll(claims.Email, "'", "''"))) + } else if claims.PreferredUsername != "" { + account, status = getAccount(l, opt.AccountsClient, fmt.Sprintf("preferred_name eq '%s'", strings.ReplaceAll(claims.PreferredUsername, "'", "''"))) + } else { + // TODO allow lookup by custom claim, eg an id ... or sub + l.Error().Err(err).Msgf("Could not lookup account, no mail or preferred_username claim set") + w.WriteHeader(http.StatusInternalServerError) + } if status != 0 { if status == http.StatusNotFound { account, status = createAccount(l, claims, opt.AccountsClient) diff --git a/pkg/middleware/account_uuid_test.go b/pkg/middleware/account_uuid_test.go index abf2e8515d..7241fa901c 100644 --- a/pkg/middleware/account_uuid_test.go +++ b/pkg/middleware/account_uuid_test.go @@ -17,13 +17,13 @@ import ( // TODO testing the getAccount method should inject a cache func TestGetAccountSuccess(t *testing.T) { svcCache.Invalidate(AccountsKey, "success") - if _, status := getAccount(log.NewLogger(), &oidc.StandardClaims{Email: "success"}, mockAccountUUIDMiddlewareAccSvc(false, true)); status != 0 { + if _, status := getAccount(log.NewLogger(), mockAccountUUIDMiddlewareAccSvc(false, true), "mail eq 'success'"); status != 0 { t.Errorf("expected an account") } } func TestGetAccountInternalError(t *testing.T) { svcCache.Invalidate(AccountsKey, "failure") - if _, status := getAccount(log.NewLogger(), &oidc.StandardClaims{Email: "failure"}, mockAccountUUIDMiddlewareAccSvc(true, false)); status != http.StatusInternalServerError { + if _, status := getAccount(log.NewLogger(), mockAccountUUIDMiddlewareAccSvc(true, false), "mail eq 'failure'"); status != http.StatusInternalServerError { t.Errorf("expected an internal server error") } } diff --git a/pkg/middleware/options.go b/pkg/middleware/options.go index 8a4dace009..e6ab964298 100644 --- a/pkg/middleware/options.go +++ b/pkg/middleware/options.go @@ -7,6 +7,7 @@ import ( acc "github.com/owncloud/ocis-accounts/pkg/proto/v0" "github.com/owncloud/ocis-pkg/v2/log" "github.com/owncloud/ocis-proxy/pkg/config" + storepb "github.com/owncloud/ocis-store/pkg/proto/v0" ) // Option defines a single option function. @@ -26,9 +27,11 @@ type Options struct { OIDCProviderFunc func() (OIDCProvider, error) // RevaGatewayClient to send requests to the reva gateway RevaGatewayClient gateway.GatewayAPIClient + // Store for persisting data + Store storepb.StoreService } -// newOIDCOptions initializes the available default options. +// newOptions initializes the available default options. func newOptions(opts ...Option) Options { opt := Options{} @@ -75,8 +78,15 @@ func OIDCProviderFunc(f func() (OIDCProvider, error)) Option { } // RevaGatewayClient provides a function to set the the reva gateway service client option. -func RevaGatewayClient(sc gateway.GatewayAPIClient) Option { +func RevaGatewayClient(gc gateway.GatewayAPIClient) Option { return func(o *Options) { - o.RevaGatewayClient = sc + o.RevaGatewayClient = gc + } +} + +// Store provides a function to set the store option. +func Store(sc storepb.StoreService) Option { + return func(o *Options) { + o.Store = sc } } diff --git a/pkg/middleware/presigned_url.go b/pkg/middleware/presigned_url.go new file mode 100644 index 0000000000..74b629650f --- /dev/null +++ b/pkg/middleware/presigned_url.go @@ -0,0 +1,118 @@ +package middleware + +import ( + "context" + "crypto/sha512" + "encoding/hex" + "net/http" + "strings" + "time" + + "github.com/owncloud/ocis-pkg/v2/log" + ocisoidc "github.com/owncloud/ocis-pkg/v2/oidc" + storepb "github.com/owncloud/ocis-store/pkg/proto/v0" + "golang.org/x/crypto/pbkdf2" +) + +// PresignedURL provides a middleware to check access secured by a presigned URL. +func PresignedURL(opts ...Option) func(next http.Handler) http.Handler { + opt := newOptions(opts...) + l := opt.Logger + + return func(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if isSignedRequest(r) { + if signedRequestIsValid(l, r, opt.Store) { + // use openid claims to let the account_uuid middleware do a lookup by username + claims := ocisoidc.StandardClaims{ + PreferredUsername: r.URL.Query().Get("OC-Credential"), + } + + // inject claims to the request context for the account_uuid middleware + ctxWithClaims := ocisoidc.NewContext(r.Context(), &claims) + r = r.WithContext(ctxWithClaims) + + next.ServeHTTP(w, r) + } else { + http.Error(w, "Invalid url signature", http.StatusUnauthorized) + return + } + } + next.ServeHTTP(w, r) + }) + } +} + +func isSignedRequest(r *http.Request) bool { + return r.URL.Query().Get("OC-Signature") != "" +} + +func signedRequestIsValid(l log.Logger, r *http.Request, s storepb.StoreService) bool { + // cheap checks first + // TODO OC-Algorithm - defined the used algo (e.g. sha256 or sha512 - we should agree on one default algo and make this parameter optional) + // OC-Credential - defines the user scope (shall we use the owncloud user id here - this might leak internal data ....) REQUIRED + // OC-Date - defined the date the url was signed (ISO 8601 UTC) REQUIRED + // OC-Expires - defines the expiry interval in seconds (between 1 and 604800 = 7 days) REQUIRED + // TODO OC-Verb - defines for which http verb the request is valid - defaults to GET OPTIONAL + // OC-Signature - the computed signature - server will verify the request upon this REQUIRED + if r.URL.Query().Get("OC-Signature") == "" || r.URL.Query().Get("OC-Credential") == "" || r.URL.Query().Get("OC-Date") == "" || r.URL.Query().Get("OC-Expires") == "" || r.URL.Query().Get("OC-Verb") == "" { + return false + } + + if !strings.EqualFold(r.Method, r.URL.Query().Get("OC-Verb")) { + return false + } + + if t, err := time.Parse(time.RFC3339, r.URL.Query().Get("OC-Date")); err != nil { + return false + } else if expires, err := time.ParseDuration(r.URL.Query().Get("OC-Expires") + "s"); err != nil { + return false + } else { + t.Add(expires) + if t.After(time.Now()) { + return false + } + } + + signingKey, err := getSigningKey(r.Context(), s, r.URL.Query().Get("OC-Credential")) + if err != nil { + l.Error().Err(err).Msg("could not retrieve signing key") + return false + } + if len(signingKey) == 0 { + l.Error().Err(err).Msg("signing key empty") + return false + } + + q := r.URL.Query() + signature := q.Get("OC-Signature") + q.Del("OC-Signature") + r.URL.RawQuery = q.Encode() + url := r.URL.String() + if !r.URL.IsAbs() { + url = "https://" + r.Host + url // TODO where do we get the scheme from + } + // the oc10 signature check: $hash = \hash_pbkdf2("sha512", $url, $signingKey, 10000, 64, false); + // - sets the length of the output string to 64 + // - sets raw output to false -> if raw_output is FALSE length corresponds to twice the byte-length of the derived key (as every byte of the key is returned as two hexits). + // TODO change to length 128 in oc10? + // fo golangs pbkdf2.Key we need to use 32 because it will be encoded into 64 hexits later + hash := pbkdf2.Key([]byte(url), signingKey, 10000, 32, sha512.New) + + return hex.EncodeToString(hash) == signature +} + +func getSigningKey(ctx context.Context, s storepb.StoreService, credential string) ([]byte, error) { + res, err := s.Read(ctx, &storepb.ReadRequest{ + Options: &storepb.ReadOptions{ + Database: "proxy", + Table: "signing-keys", + }, + Key: credential, + }) + if err != nil || len(res.Records) < 1 { + return []byte{}, err + } + + return res.Records[0].Value, nil +} diff --git a/pkg/proxy/proxy.go b/pkg/proxy/proxy.go index 3f46820dd0..fa43b02415 100644 --- a/pkg/proxy/proxy.go +++ b/pkg/proxy/proxy.go @@ -253,6 +253,11 @@ func defaultPolicies() []config.Policy { Endpoint: "/ocs/", Backend: "http://localhost:9140", }, + { + Type: config.RegexRoute, + Endpoint: "/ocs/v[12].php/cloud/user/signing-key", + Backend: "http://localhost:9110", + }, { Type: config.QueryRoute, Endpoint: "/remote.php/?preview=1",