diff --git a/server/go.mod b/server/go.mod index 9a25c7b4f..57c7622e3 100644 --- a/server/go.mod +++ b/server/go.mod @@ -14,7 +14,7 @@ require ( github.com/SentimensRG/ctx v0.0.0-20180729130232-0bfd988c655d github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d github.com/brianvoe/gofakeit/v6 v6.16.0 - github.com/crewjam/saml v0.4.6 + github.com/crewjam/saml v0.4.13 github.com/crusttech/go-oidc v0.0.0-20180918092017-982855dad3e1 github.com/davecgh/go-spew v1.1.1 github.com/denisenkom/go-mssqldb v0.12.3 @@ -24,6 +24,7 @@ require ( github.com/doug-martin/goqu/v9 v9.18.0 github.com/edwvee/exiffix v0.0.0-20210922235313-0f6cbda5e58f github.com/evanw/esbuild v0.14.38 + github.com/fogleman/gg v1.3.0 github.com/fsnotify/fsnotify v1.5.3 github.com/gabriel-vasile/mimetype v1.4.0 github.com/getsentry/sentry-go v0.13.0 @@ -33,7 +34,8 @@ require ( github.com/go-chi/jwtauth v1.2.0 github.com/go-oauth2/oauth2/v4 v4.4.3 github.com/go-sql-driver/mysql v1.6.0 - github.com/golang-jwt/jwt/v4 v4.4.1 + github.com/golang-jwt/jwt/v4 v4.4.3 + github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 github.com/golang/mock v1.6.0 github.com/golang/protobuf v1.5.2 github.com/google/uuid v1.1.2 @@ -61,18 +63,19 @@ require ( github.com/spf13/cobra v1.4.0 github.com/steinfletcher/apitest v1.5.11 github.com/steinfletcher/apitest-jsonpath v1.7.1 - github.com/stretchr/testify v1.7.1 + github.com/stretchr/testify v1.8.1 github.com/tidwall/btree v1.3.1 github.com/valyala/fastjson v1.6.3 go.uber.org/atomic v1.9.0 go.uber.org/zap v1.21.0 golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d + golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8 golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5 golang.org/x/text v0.8.0 google.golang.org/grpc v1.46.0 google.golang.org/protobuf v1.28.0 gopkg.in/mail.v2 v2.3.1 - gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b + gopkg.in/yaml.v3 v3.0.1 moul.io/zapfilter v1.7.0 rsc.io/qr v0.2.0 ) @@ -89,14 +92,12 @@ require ( github.com/crewjam/httperr v0.2.0 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.0-20210816181553-5444fa50b93d // indirect github.com/dlclark/regexp2 v1.8.1 // indirect - github.com/fogleman/gg v1.3.0 // indirect github.com/go-chi/chi v3.3.4+incompatible // indirect github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect github.com/goccy/go-json v0.9.6 // indirect github.com/golang-jwt/jwt v3.2.2+incompatible // indirect github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe // indirect github.com/golang-sql/sqlexp v0.1.0 // indirect - github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect github.com/google/pprof v0.0.0-20230228050547-1710fef4ab10 // indirect github.com/gorilla/css v1.0.0 // indirect github.com/gorilla/mux v1.8.0 // indirect @@ -124,14 +125,12 @@ require ( github.com/prometheus/client_model v0.2.0 // indirect github.com/prometheus/common v0.32.1 // indirect github.com/prometheus/procfs v0.7.3 // indirect - github.com/russellhaering/goxmldsig v1.1.1 // indirect + github.com/russellhaering/goxmldsig v1.2.0 // indirect github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd // indirect github.com/shopspring/decimal v1.3.1 // indirect github.com/spf13/pflag v1.0.5 // indirect - github.com/stretchr/objx v0.2.0 // indirect github.com/valyala/fasthttp v1.35.0 // indirect go.uber.org/multierr v1.7.0 // indirect - golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8 // indirect golang.org/x/net v0.0.0-20220722155237-a158d28d115b // indirect golang.org/x/sys v0.5.0 // indirect golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect diff --git a/server/go.sum b/server/go.sum index ce41507f9..7d89db7b8 100644 --- a/server/go.sum +++ b/server/go.sum @@ -126,14 +126,14 @@ github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46t github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/crewjam/httperr v0.2.0 h1:b2BfXR8U3AlIHwNeFFvZ+BV1LFvKLlzMjzaTnZMybNo= github.com/crewjam/httperr v0.2.0/go.mod h1:Jlz+Sg/XqBQhyMjdDiC+GNNRzZTD7x39Gu3pglZ5oH4= -github.com/crewjam/saml v0.4.6 h1:XCUFPkQSJLvzyl4cW9OvpWUbRf0gE7VUpU8ZnilbeM4= -github.com/crewjam/saml v0.4.6/go.mod h1:ZBOXnNPFzB3CgOkRm7Nd6IVdkG+l/wF+0ZXLqD96t1A= +github.com/crewjam/saml v0.4.13 h1:TYHggH/hwP7eArqiXSJUvtOPNzQDyQ7vwmwEqlFWhMc= +github.com/crewjam/saml v0.4.13/go.mod h1:igEejV+fihTIlHXYP8zOec3V5A8y3lws5bQBFsTm4gA= github.com/crusttech/go-oidc v0.0.0-20180918092017-982855dad3e1 h1:V2GKd4ImRY9lFUu3TclHNmqgJCADdA7muym9JuVEPlY= github.com/crusttech/go-oidc v0.0.0-20180918092017-982855dad3e1/go.mod h1:2LVBu240CBZgYkGOSRx733tkh9QUNcH+fIqmeAsdrds= 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/dchest/uniuri v0.0.0-20200228104902-7aecb25e1fe5/go.mod h1:GgB8SF9nRG+GqaDtLcwJZsQFhcogVCJ79j4EdT0c2V4= +github.com/dchest/uniuri v1.2.0/go.mod h1:fSzm4SLHzNZvWLvWJew423PhAzkpNQYq+uNLq4kxhkY= github.com/deckarep/golang-set v1.7.1 h1:SCQV0S6gTtp6itiFrTqI+pfmJ4LN85S1YzhDf9rTHJQ= github.com/deckarep/golang-set v1.7.1/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ= github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= @@ -222,10 +222,9 @@ github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7a github.com/golang-jwt/jwt v3.2.1+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= -github.com/golang-jwt/jwt/v4 v4.1.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= -github.com/golang-jwt/jwt/v4 v4.4.1 h1:pC5DB52sCeK48Wlb9oPcdhnjkz1TKt1D/P7WKJ0kUcQ= -github.com/golang-jwt/jwt/v4 v4.4.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang-jwt/jwt/v4 v4.4.3 h1:Hxl6lhQFj4AnOX6MLrsCb/+7tCj7DxP7VA+2rDIq5AU= +github.com/golang-jwt/jwt/v4 v4.4.3/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A= @@ -278,8 +277,9 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -385,8 +385,9 @@ 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.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -505,10 +506,11 @@ github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1 github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= -github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= -github.com/russellhaering/goxmldsig v1.1.1 h1:vI0r2osGF1A9PLvsGdPUAGwEIrKa4Pj5sesSBsebIxM= -github.com/russellhaering/goxmldsig v1.1.1/go.mod h1:gM4MDENBQf7M+V824SGfyIUVFWydB7n0KkEubVJl+Tw= +github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/russellhaering/goxmldsig v1.2.0 h1:Y6GTTc9Un5hCxSzVz4UIWQ/zuVwDvzJk80guqzwx6Vg= +github.com/russellhaering/goxmldsig v1.2.0/go.mod h1:gM4MDENBQf7M+V824SGfyIUVFWydB7n0KkEubVJl+Tw= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd h1:CmH9+J6ZSsIjUK3dcGsnCnO41eRBOnY12zwkn5qVwgc= github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk= @@ -545,16 +547,19 @@ github.com/steinfletcher/apitest-jsonpath v1.7.1 h1:dj0Z/7DJt2Ts/R52OeVbnMp+gegc github.com/steinfletcher/apitest-jsonpath v1.7.1/go.mod h1:FzU2i3ZIyIdF6yBI8a0DZ10gCC6jhiD1yAdU0nCz5Do= 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= -github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48= -github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/tailscale/depaware v0.0.0-20210622194025-720c4b409502/go.mod h1:p9lPsd+cx33L3H9nNoecRRxPssFKUwwI50I3pZ0yT+8= github.com/tidwall/btree v0.0.0-20191029221954-400434d76274/go.mod h1:huei1BkDWJ3/sLXmO+bsCNELL+Bp2Kks9OLyQFkzvA8= github.com/tidwall/btree v1.3.1 h1:636+tdVDs8Hjcf35Di260W2xCW4KuoXOKyk9QWOvCpA= @@ -636,10 +641,10 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201217014255-9d1352758620/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= -golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20220128200615-198e4374d7ed/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d h1:sK3txAijHtOK88l68nt020reeT1ZdKLIYetKl95FzVY= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= @@ -914,7 +919,6 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= @@ -1091,8 +1095,9 @@ gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= 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= diff --git a/server/vendor/github.com/crewjam/saml/README.md b/server/vendor/github.com/crewjam/saml/README.md index 71f247868..c0b980587 100644 --- a/server/vendor/github.com/crewjam/saml/README.md +++ b/server/vendor/github.com/crewjam/saml/README.md @@ -58,7 +58,7 @@ import ( ) func hello(w http.ResponseWriter, r *http.Request) { - fmt.Fprintf(w, "Hello, %s!", samlsp.AttributeFromContext(r.Context(), "cn")) + fmt.Fprintf(w, "Hello, %s!", samlsp.AttributeFromContext(r.Context(), "displayName")) } func main() { diff --git a/server/vendor/github.com/crewjam/saml/flate.go b/server/vendor/github.com/crewjam/saml/flate.go new file mode 100644 index 000000000..4d14e7805 --- /dev/null +++ b/server/vendor/github.com/crewjam/saml/flate.go @@ -0,0 +1,31 @@ +package saml + +import ( + "compress/flate" + "fmt" + "io" +) + +const flateUncompressLimit = 10 * 1024 * 1024 // 10MB + +func newSaferFlateReader(r io.Reader) io.ReadCloser { + return &saferFlateReader{r: flate.NewReader(r)} +} + +type saferFlateReader struct { + r io.ReadCloser + count int +} + +func (r *saferFlateReader) Read(p []byte) (n int, err error) { + if r.count+len(p) > flateUncompressLimit { + return 0, fmt.Errorf("flate: uncompress limit exceeded (%d bytes)", flateUncompressLimit) + } + n, err = r.r.Read(p) + r.count += n + return n, err +} + +func (r *saferFlateReader) Close() error { + return r.r.Close() +} diff --git a/server/vendor/github.com/crewjam/saml/identity_provider.go b/server/vendor/github.com/crewjam/saml/identity_provider.go index 4c7282ae9..bcea5828f 100644 --- a/server/vendor/github.com/crewjam/saml/identity_provider.go +++ b/server/vendor/github.com/crewjam/saml/identity_provider.go @@ -2,7 +2,6 @@ package saml import ( "bytes" - "compress/flate" "crypto" "crypto/tls" "crypto/x509" @@ -36,7 +35,10 @@ type Session struct { ExpireTime time.Time Index string - NameID string + NameID string + NameIDFormat string + SubjectID string + Groups []string UserName string UserEmail string @@ -360,7 +362,7 @@ func NewIdpAuthnRequest(idp *IdentityProvider, r *http.Request) (*IdpAuthnReques if err != nil { return nil, fmt.Errorf("cannot decode request: %s", err) } - req.RequestBuffer, err = ioutil.ReadAll(flate.NewReader(bytes.NewReader(compressedRequest))) + req.RequestBuffer, err = ioutil.ReadAll(newSaferFlateReader(bytes.NewReader(compressedRequest))) if err != nil { return nil, fmt.Errorf("cannot decompress request: %s", err) } @@ -734,6 +736,19 @@ func (DefaultAssertionMaker) MakeAssertion(req *IdpAuthnRequest, session *Sessio }) } + if session.SubjectID != "" { + attributes = append(attributes, Attribute{ + Name: "urn:oasis:names:tc:SAML:attribute:subject-id", + NameFormat: "urn:oasis:names:tc:SAML:2.0:attrname-format:uri", + Values: []AttributeValue{ + { + Type: "xs:string", + Value: session.SubjectID, + }, + }, + }) + } + // allow for some clock skew in the validity period using the // issuer's apparent clock. notBefore := req.Now.Add(-1 * MaxClockSkew) @@ -743,6 +758,12 @@ func (DefaultAssertionMaker) MakeAssertion(req *IdpAuthnRequest, session *Sessio notOnOrAfterAfter = notBefore.Add(MaxIssueDelay) } + nameIDFormat := "urn:oasis:names:tc:SAML:2.0:nameid-format:transient" + + if session.NameIDFormat != "" { + nameIDFormat = session.NameIDFormat + } + req.Assertion = &Assertion{ ID: fmt.Sprintf("id-%x", randomBytes(20)), IssueInstant: TimeNow(), @@ -753,7 +774,7 @@ func (DefaultAssertionMaker) MakeAssertion(req *IdpAuthnRequest, session *Sessio }, Subject: &Subject{ NameID: &NameID{ - Format: "urn:oasis:names:tc:SAML:2.0:nameid-format:transient", + Format: nameIDFormat, NameQualifier: req.IDP.Metadata().EntityID, SPNameQualifier: req.ServiceProviderMetadata.EntityID, Value: session.NameID, @@ -875,12 +896,23 @@ func (req *IdpAuthnRequest) MakeAssertionEl() error { return nil } -// WriteResponse writes the `Response` to the http.ResponseWriter. If -// `Response` is not already set, it calls MakeResponse to produce it. -func (req *IdpAuthnRequest) WriteResponse(w http.ResponseWriter) error { +// IdpAuthnRequestForm contans HTML form information to be submitted to the +// SAML HTTP POST binding ACS. +type IdpAuthnRequestForm struct { + URL string + SAMLResponse string + RelayState string +} + +// PostBinding creates the HTTP POST form information for this +// `IdpAuthnRequest`. If `Response` is not already set, it calls MakeResponse +// to produce it. +func (req *IdpAuthnRequest) PostBinding() (IdpAuthnRequestForm, error) { + var form IdpAuthnRequestForm + if req.ResponseEl == nil { if err := req.MakeResponse(); err != nil { - return err + return form, err } } @@ -888,45 +920,48 @@ func (req *IdpAuthnRequest) WriteResponse(w http.ResponseWriter) error { doc.SetRoot(req.ResponseEl) responseBuf, err := doc.WriteToBytes() if err != nil { - return err + return form, err } - // the only supported binding is the HTTP-POST binding - switch req.ACSEndpoint.Binding { - case HTTPPostBinding: - tmpl := template.Must(template.New("saml-post-form").Parse(`` + - `
` + - `` + - `` + - ``)) - data := struct { - URL string - SAMLResponse string - RelayState string - }{ - URL: req.ACSEndpoint.Location, - SAMLResponse: base64.StdEncoding.EncodeToString(responseBuf), - RelayState: req.RelayState, - } - - buf := bytes.NewBuffer(nil) - if err := tmpl.Execute(buf, data); err != nil { - return err - } - if _, err := io.Copy(w, buf); err != nil { - return err - } - return nil - - default: - return fmt.Errorf("%s: unsupported binding %s", + if req.ACSEndpoint.Binding != HTTPPostBinding { + return form, fmt.Errorf("%s: unsupported binding %s", req.ServiceProviderMetadata.EntityID, req.ACSEndpoint.Binding) } + + form.URL = req.ACSEndpoint.Location + form.SAMLResponse = base64.StdEncoding.EncodeToString(responseBuf) + form.RelayState = req.RelayState + + return form, nil +} + +// WriteResponse writes the `Response` to the http.ResponseWriter. If +// `Response` is not already set, it calls MakeResponse to produce it. +func (req *IdpAuthnRequest) WriteResponse(w http.ResponseWriter) error { + form, err := req.PostBinding() + if err != nil { + return err + } + + tmpl := template.Must(template.New("saml-post-form").Parse(`` + + `` + + `` + + `` + + ``)) + + buf := bytes.NewBuffer(nil) + if err := tmpl.Execute(buf, form); err != nil { + return err + } + if _, err := io.Copy(w, buf); err != nil { + return err + } + return nil } // getSPEncryptionCert returns the certificate which we can use to encrypt things diff --git a/server/vendor/github.com/crewjam/saml/saml.go b/server/vendor/github.com/crewjam/saml/saml.go index e559182e8..b171e56d5 100644 --- a/server/vendor/github.com/crewjam/saml/saml.go +++ b/server/vendor/github.com/crewjam/saml/saml.go @@ -1,13 +1,13 @@ // Package saml contains a partial implementation of the SAML standard in golang. // SAML is a standard for identity federation, i.e. either allowing a third party to authenticate your users or allowing third parties to rely on us to authenticate their users. // -// Introduction +// # Introduction // // In SAML parlance an Identity Provider (IDP) is a service that knows how to authenticate users. A Service Provider (SP) is a service that delegates authentication to an IDP. If you are building a service where users log in with someone else's credentials, then you are a Service Provider. This package supports implementing both service providers and identity providers. // // The core package contains the implementation of SAML. The package samlsp provides helper middleware suitable for use in Service Provider applications. The package samlidp provides a rudimentary IDP service that is useful for testing or as a starting point for other integrations. // -// Breaking Changes +// # Breaking Changes // // Version 0.4.0 introduces a few breaking changes to the _samlsp_ package in order to make the package more extensible, and to clean up the interfaces a bit. The default behavior remains the same, but you can now provide interface implementations of _RequestTracker_ (which tracks pending requests), _Session_ (which handles maintaining a session) and _OnError_ which handles reporting errors. // @@ -32,7 +32,7 @@ // - `CookieDomain` - Instead assign a custom CookieRequestTracker or CookieSessionProvider // - `CookieDomain` - Instead assign a custom CookieRequestTracker or CookieSessionProvider // -// Getting Started as a Service Provider +// # Getting Started as a Service Provider // // Let us assume we have a simple web application to protect. We'll modify this application so it uses SAML to authenticate users. // @@ -40,24 +40,27 @@ // package main // // import ( -// "fmt" -// "net/http" +// +// "fmt" +// "net/http" +// // ) // -// func hello(w http.ResponseWriter, r *http.Request) { -// fmt.Fprintf(w, "Hello, World!") -// } +// func hello(w http.ResponseWriter, r *http.Request) { +// fmt.Fprintf(w, "Hello, World!") +// } +// +// func main() { +// app := http.HandlerFunc(hello) +// http.Handle("/hello", app) +// http.ListenAndServe(":8000", nil) +// } // -// func main() { -// app := http.HandlerFunc(hello) -// http.Handle("/hello", app) -// http.ListenAndServe(":8000", nil) -// } // ``` // // Each service provider must have an self-signed X.509 key pair established. You can generate your own with something like this: // -// openssl req -x509 -newkey rsa:2048 -keyout myservice.key -out myservice.cert -days 365 -nodes -subj "/CN=myservice.example.com" +// openssl req -x509 -newkey rsa:2048 -keyout myservice.key -out myservice.cert -days 365 -nodes -subj "/CN=myservice.example.com" // // We will use `samlsp.Middleware` to wrap the endpoint we want to protect. Middleware provides both an `http.Handler` to serve the SAML specific URLs and a set of wrappers to require the user to be logged in. We also provide the URL where the service provider can fetch the metadata from the IDP at startup. In our case, we'll use [samltest.id](https://samltest.id/), an identity provider designed for testing. // @@ -65,57 +68,60 @@ // package main // // import ( -// "crypto/rsa" -// "crypto/tls" -// "crypto/x509" -// "fmt" -// "net/http" -// "net/url" // -// "github.com/crewjam/saml/samlsp" +// "crypto/rsa" +// "crypto/tls" +// "crypto/x509" +// "fmt" +// "net/http" +// "net/url" +// +// "github.com/crewjam/saml/samlsp" +// // ) // -// func hello(w http.ResponseWriter, r *http.Request) { -// fmt.Fprintf(w, "Hello, %s!", samlsp.Token(r.Context()).Attributes.Get("cn")) -// } +// func hello(w http.ResponseWriter, r *http.Request) { +// fmt.Fprintf(w, "Hello, %s!", samlsp.Token(r.Context()).Attributes.Get("cn")) +// } // -// func main() { -// keyPair, err := tls.LoadX509KeyPair("myservice.cert", "myservice.key") -// if err != nil { -// panic(err) // TODO handle error -// } -// keyPair.Leaf, err = x509.ParseCertificate(keyPair.Certificate[0]) -// if err != nil { -// panic(err) // TODO handle error -// } +// func main() { +// keyPair, err := tls.LoadX509KeyPair("myservice.cert", "myservice.key") +// if err != nil { +// panic(err) // TODO handle error +// } +// keyPair.Leaf, err = x509.ParseCertificate(keyPair.Certificate[0]) +// if err != nil { +// panic(err) // TODO handle error +// } // -// idpMetadataURL, err := url.Parse("https://samltest.id/saml/idp") -// if err != nil { -// panic(err) // TODO handle error -// } +// idpMetadataURL, err := url.Parse("https://samltest.id/saml/idp") +// if err != nil { +// panic(err) // TODO handle error +// } // -// rootURL, err := url.Parse("http://localhost:8000") -// if err != nil { -// panic(err) // TODO handle error -// } +// rootURL, err := url.Parse("http://localhost:8000") +// if err != nil { +// panic(err) // TODO handle error +// } +// +// samlSP, _ := samlsp.New(samlsp.Options{ +// URL: *rootURL, +// Key: keyPair.PrivateKey.(*rsa.PrivateKey), +// Certificate: keyPair.Leaf, +// IDPMetadataURL: idpMetadataURL, +// }) +// app := http.HandlerFunc(hello) +// http.Handle("/hello", samlSP.RequireAccount(app)) +// http.Handle("/saml/", samlSP) +// http.ListenAndServe(":8000", nil) +// } // -// samlSP, _ := samlsp.New(samlsp.Options{ -// URL: *rootURL, -// Key: keyPair.PrivateKey.(*rsa.PrivateKey), -// Certificate: keyPair.Leaf, -// IDPMetadataURL: idpMetadataURL, -// }) -// app := http.HandlerFunc(hello) -// http.Handle("/hello", samlSP.RequireAccount(app)) -// http.Handle("/saml/", samlSP) -// http.ListenAndServe(":8000", nil) -// } // ``` // // Next we'll have to register our service provider with the identity provider to establish trust from the service provider to the IDP. For [samltest.id](https://samltest.id/), you can do something like: // -// mdpath=saml-test-$USER-$HOST.xml -// curl localhost:8000/saml/metadata > $mdpath +// mdpath=saml-test-$USER-$HOST.xml +// curl localhost:8000/saml/metadata > $mdpath // // Navigate to https://samltest.id/upload.php and upload the file you fetched. // @@ -133,11 +139,11 @@ // // 1. This time when `localhost:8000/hello` is requested there is a valid session and so the main content is served. // -// Getting Started as an Identity Provider +// # Getting Started as an Identity Provider // // Please see `example/idp/` for a substantially complete example of how to use the library and helpers to be an identity provider. // -// Support +// # Support // // The SAML standard is huge and complex with many dark corners and strange, unused features. This package implements the most commonly used subset of these features required to provide a single sign on experience. The package supports at least the subset of SAML known as [interoperable SAML](http://saml2int.org). // @@ -145,13 +151,13 @@ // // The package can produce signed SAML assertions, and can validate both signed and encrypted SAML assertions. It does not support signed or encrypted requests. // -// RelayState +// # RelayState // // The _RelayState_ parameter allows you to pass user state information across the authentication flow. The most common use for this is to allow a user to request a deep link into your site, be redirected through the SAML login flow, and upon successful completion, be directed to the originally requested link, rather than the root. // // Unfortunately, _RelayState_ is less useful than it could be. Firstly, it is not authenticated, so anything you supply must be signed to avoid XSS or CSRF. Secondly, it is limited to 80 bytes in length, which precludes signing. (See section 3.6.3.1 of SAMLProfiles.) // -// References +// # References // // The SAML specification is a collection of PDFs (sadly): // @@ -165,7 +171,7 @@ // // [SAMLtest](https://samltest.id/) is a testing ground for SAML service and identity providers. // -// Security Issues +// # Security Issues // // Please do not report security issues in the issue tracker. Rather, please contact me directly at ross@kndr.org ([PGP Key `78B6038B3B9DFB88`](https://keybase.io/crewjam)). package saml diff --git a/server/vendor/github.com/crewjam/saml/samlsp/middleware.go b/server/vendor/github.com/crewjam/saml/samlsp/middleware.go index 5ba75c625..834a79c12 100644 --- a/server/vendor/github.com/crewjam/saml/samlsp/middleware.go +++ b/server/vendor/github.com/crewjam/saml/samlsp/middleware.go @@ -186,12 +186,19 @@ func (m *Middleware) CreateSessionFromAssertion(w http.ResponseWriter, r *http.R if trackedRequestIndex := r.Form.Get("RelayState"); trackedRequestIndex != "" { trackedRequest, err := m.RequestTracker.GetTrackedRequest(r, trackedRequestIndex) if err != nil { - m.OnError(w, r, err) - return - } - m.RequestTracker.StopTrackingRequest(w, r, trackedRequestIndex) + if err == http.ErrNoCookie && m.ServiceProvider.AllowIDPInitiated { + if uri := r.Form.Get("RelayState"); uri != "" { + redirectURI = uri + } + } else { + m.OnError(w, r, err) + return + } + } else { + m.RequestTracker.StopTrackingRequest(w, r, trackedRequestIndex) - redirectURI = trackedRequest.URI + redirectURI = trackedRequest.URI + } } if err := m.Session.CreateSession(w, r, assertion); err != nil { @@ -209,9 +216,8 @@ func (m *Middleware) CreateSessionFromAssertion(w http.ResponseWriter, r *http.R // // For example: // -// goji.Use(m.RequireAccount) -// goji.Use(RequireAttributeMiddleware("eduPersonAffiliation", "Staff")) -// +// goji.Use(m.RequireAccount) +// goji.Use(RequireAttributeMiddleware("eduPersonAffiliation", "Staff")) func RequireAttribute(name, value string) func(http.Handler) http.Handler { return func(handler http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { diff --git a/server/vendor/github.com/crewjam/saml/samlsp/new.go b/server/vendor/github.com/crewjam/saml/samlsp/new.go index a1ab584ec..81fa75f63 100644 --- a/server/vendor/github.com/crewjam/saml/samlsp/new.go +++ b/server/vendor/github.com/crewjam/saml/samlsp/new.go @@ -14,19 +14,23 @@ import ( // Options represents the parameters for creating a new middleware type Options struct { - EntityID string - URL url.URL - Key *rsa.PrivateKey - Certificate *x509.Certificate - Intermediates []*x509.Certificate - AllowIDPInitiated bool - DefaultRedirectURI string - IDPMetadata *saml.EntityDescriptor - SignRequest bool - UseArtifactResponse bool - ForceAuthn bool // TODO(ross): this should be *bool - CookieSameSite http.SameSite - RelayStateFunc func(w http.ResponseWriter, r *http.Request) string + EntityID string + URL url.URL + Key *rsa.PrivateKey + Certificate *x509.Certificate + Intermediates []*x509.Certificate + HTTPClient *http.Client + AllowIDPInitiated bool + DefaultRedirectURI string + IDPMetadata *saml.EntityDescriptor + SignRequest bool + UseArtifactResponse bool + ForceAuthn bool // TODO(ross): this should be *bool + RequestedAuthnContext *saml.RequestedAuthnContext + CookieSameSite http.SameSite + CookieName string + RelayStateFunc func(w http.ResponseWriter, r *http.Request) string + LogoutBindings []string } // DefaultSessionCodec returns the default SessionCodec for the provided options, @@ -44,8 +48,12 @@ func DefaultSessionCodec(opts Options) JWTSessionCodec { // DefaultSessionProvider returns the default SessionProvider for the provided options, // a CookieSessionProvider configured to store sessions in a cookie. func DefaultSessionProvider(opts Options) CookieSessionProvider { + cookieName := opts.CookieName + if cookieName == "" { + cookieName = defaultSessionCookieName + } return CookieSessionProvider{ - Name: defaultSessionCookieName, + Name: cookieName, Domain: opts.URL.Host, MaxAge: defaultSessionMaxAge, HTTPOnly: true, @@ -100,19 +108,26 @@ func DefaultServiceProvider(opts Options) saml.ServiceProvider { opts.DefaultRedirectURI = "/" } + if len(opts.LogoutBindings) == 0 { + opts.LogoutBindings = []string{saml.HTTPPostBinding} + } + return saml.ServiceProvider{ - EntityID: opts.EntityID, - Key: opts.Key, - Certificate: opts.Certificate, - Intermediates: opts.Intermediates, - MetadataURL: *metadataURL, - AcsURL: *acsURL, - SloURL: *sloURL, - IDPMetadata: opts.IDPMetadata, - ForceAuthn: forceAuthn, - SignatureMethod: signatureMethod, - AllowIDPInitiated: opts.AllowIDPInitiated, - DefaultRedirectURI: opts.DefaultRedirectURI, + EntityID: opts.EntityID, + Key: opts.Key, + Certificate: opts.Certificate, + HTTPClient: opts.HTTPClient, + Intermediates: opts.Intermediates, + MetadataURL: *metadataURL, + AcsURL: *acsURL, + SloURL: *sloURL, + IDPMetadata: opts.IDPMetadata, + ForceAuthn: forceAuthn, + RequestedAuthnContext: opts.RequestedAuthnContext, + SignatureMethod: signatureMethod, + AllowIDPInitiated: opts.AllowIDPInitiated, + DefaultRedirectURI: opts.DefaultRedirectURI, + LogoutBindings: opts.LogoutBindings, } } diff --git a/server/vendor/github.com/crewjam/saml/samlsp/request_tracker.go b/server/vendor/github.com/crewjam/saml/samlsp/request_tracker.go index ea1a484d7..f5477c8d2 100644 --- a/server/vendor/github.com/crewjam/saml/samlsp/request_tracker.go +++ b/server/vendor/github.com/crewjam/saml/samlsp/request_tracker.go @@ -8,11 +8,11 @@ import ( // // There are two main reasons for this: // -// 1. When the middleware initiates an authentication request it must track the original URL -// in order to redirect the user to the right place after the authentication completes. +// 1. When the middleware initiates an authentication request it must track the original URL +// in order to redirect the user to the right place after the authentication completes. // -// 2. After the authentication completes, we want to ensure that the user presenting the -// assertion is actually the one the request it, to mitigate request forgeries. +// 2. After the authentication completes, we want to ensure that the user presenting the +// assertion is actually the one the request it, to mitigate request forgeries. type RequestTracker interface { // TrackRequest starts tracking the SAML request with the given ID. It returns an // `index` that should be used as the RelayState in the SAMl request flow. diff --git a/server/vendor/github.com/crewjam/saml/schema.go b/server/vendor/github.com/crewjam/saml/schema.go index eefd2c665..13ea2ef60 100644 --- a/server/vendor/github.com/crewjam/saml/schema.go +++ b/server/vendor/github.com/crewjam/saml/schema.go @@ -11,6 +11,24 @@ import ( "github.com/russellhaering/goxmldsig/etreeutils" ) +// RequestedAuthnContext represents the SAML object of the same name, an indication of the +// requirements on the authentication process. +type RequestedAuthnContext struct { + XMLName xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:protocol RequestedAuthnContext"` + Comparison string `xml:",attr"` + AuthnContextClassRef string `xml:"urn:oasis:names:tc:SAML:2.0:assertion AuthnContextClassRef"` +} + +// Element returns an etree.Element representing the object in XML form. +func (r *RequestedAuthnContext) Element() *etree.Element { + el := etree.NewElement("samlp:RequestedAuthnContext") + el.CreateAttr("Comparison", r.Comparison) + elContext := etree.NewElement("saml:AuthnContextClassRef") + elContext.SetText(r.AuthnContextClassRef) + el.AddChild(elContext) + return el +} + // AuthnRequest represents the SAML object of the same name, a request from a service provider // to authenticate a user. // @@ -26,10 +44,10 @@ type AuthnRequest struct { Issuer *Issuer `xml:"urn:oasis:names:tc:SAML:2.0:assertion Issuer"` Signature *etree.Element - Subject *Subject - NameIDPolicy *NameIDPolicy `xml:"urn:oasis:names:tc:SAML:2.0:protocol NameIDPolicy"` - Conditions *Conditions - //RequestedAuthnContext *RequestedAuthnContext // TODO + Subject *Subject + NameIDPolicy *NameIDPolicy `xml:"urn:oasis:names:tc:SAML:2.0:protocol NameIDPolicy"` + Conditions *Conditions + RequestedAuthnContext *RequestedAuthnContext //Scoping *Scoping // TODO ForceAuthn *bool `xml:",attr"` @@ -77,12 +95,12 @@ func (r *LogoutRequest) Element() *etree.Element { if r.Issuer != nil { el.AddChild(r.Issuer.Element()) } - if r.NameID != nil { - el.AddChild(r.NameID.Element()) - } if r.Signature != nil { el.AddChild(r.Signature) } + if r.NameID != nil { + el.AddChild(r.NameID.Element()) + } if r.SessionIndex != nil { el.AddChild(r.SessionIndex.Element()) } @@ -159,7 +177,6 @@ func (r *LogoutRequest) Deflate() ([]byte, error) { return b.Bytes(), nil } -// Element returns an etree.Element representing the object // Element returns an etree.Element representing the object in XML form. func (r *AuthnRequest) Element() *etree.Element { el := etree.NewElement("samlp:AuthnRequest") @@ -189,9 +206,9 @@ func (r *AuthnRequest) Element() *etree.Element { if r.Conditions != nil { el.AddChild(r.Conditions.Element()) } - //if r.RequestedAuthnContext != nil { - // el.AddChild(r.RequestedAuthnContext.Element()) - //} + if r.RequestedAuthnContext != nil { + el.AddChild(r.RequestedAuthnContext.Element()) + } //if r.Scoping != nil { // el.AddChild(r.Scoping.Element()) //} @@ -306,7 +323,7 @@ func (a *NameIDPolicy) Element() *etree.Element { // ArtifactResolve represents the SAML object of the same name. type ArtifactResolve struct { - XMLName xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:protocol ArtifactResponse"` + XMLName xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:protocol ArtifactResolve"` ID string `xml:",attr"` Version string `xml:",attr"` IssueInstant time.Time `xml:",attr"` @@ -747,7 +764,7 @@ func (a *Assertion) Element() *etree.Element { for _, attributeStatement := range a.AttributeStatements { el.AddChild(attributeStatement.Element()) } - err := etreeutils.TransformExcC14n(el, canonicalizerPrefixList) + err := etreeutils.TransformExcC14n(el, canonicalizerPrefixList, false) if err != nil { panic(err) } diff --git a/server/vendor/github.com/crewjam/saml/service_provider.go b/server/vendor/github.com/crewjam/saml/service_provider.go index adf16e8de..6f6e7f4fc 100644 --- a/server/vendor/github.com/crewjam/saml/service_provider.go +++ b/server/vendor/github.com/crewjam/saml/service_provider.go @@ -3,6 +3,7 @@ package saml import ( "bytes" "compress/flate" + "context" "crypto/rsa" "crypto/tls" "crypto/x509" @@ -17,9 +18,8 @@ import ( "regexp" "time" - xrv "github.com/mattermost/xml-roundtrip-validator" - "github.com/beevik/etree" + xrv "github.com/mattermost/xml-roundtrip-validator" dsig "github.com/russellhaering/goxmldsig" "github.com/russellhaering/goxmldsig/etreeutils" @@ -72,6 +72,9 @@ type ServiceProvider struct { Certificate *x509.Certificate Intermediates []*x509.Certificate + // HTTPClient to use during SAML artifact resolution + HTTPClient *http.Client + // MetadataURL is the full URL to the metadata endpoint on this host, // i.e. https://example.com/saml/metadata MetadataURL url.URL @@ -99,6 +102,10 @@ type ServiceProvider struct { // has a SSO session at the IdP. ForceAuthn *bool + // RequestedAuthnContext allow you to specify the requested authentication + // context in authentication requests + RequestedAuthnContext *RequestedAuthnContext + // AllowIdpInitiated AllowIDPInitiated bool @@ -111,6 +118,10 @@ type ServiceProvider struct { // SignatureMethod, if non-empty, authentication requests will be signed SignatureMethod string + + // LogoutBindings specify the bindings available for SLO endpoint. If empty, + // HTTP-POST binding is used. + LogoutBindings []string } // MaxIssueDelay is the longest allowed time between when a SAML assertion is @@ -178,6 +189,15 @@ func (sp *ServiceProvider) Metadata() *EntityDescriptor { } } + var sloEndpoints []Endpoint + for _, binding := range sp.LogoutBindings { + sloEndpoints = append(sloEndpoints, Endpoint{ + Binding: binding, + Location: sp.SloURL.String(), + ResponseLocation: sp.SloURL.String(), + }) + } + return &EntityDescriptor{ EntityID: firstSet(sp.EntityID, sp.MetadataURL.String()), ValidUntil: validUntil, @@ -190,13 +210,8 @@ func (sp *ServiceProvider) Metadata() *EntityDescriptor { KeyDescriptors: keyDescriptors, ValidUntil: &validUntil, }, - SingleLogoutServices: []Endpoint{ - { - Binding: HTTPPostBinding, - Location: sp.SloURL.String(), - ResponseLocation: sp.SloURL.String(), - }, - }, + SingleLogoutServices: sloEndpoints, + NameIDFormats: []NameIDFormat{sp.AuthnNameIDFormat}, }, AuthnRequestsSigned: &authnRequestsSigned, WantAssertionsSigned: &wantAssertionsSigned, @@ -404,7 +419,8 @@ func (sp *ServiceProvider) MakeAuthenticationRequest(idpURL string, binding stri // urn:oasis:names:tc:SAML:2.0:nameid-format:transient Format: &nameIDFormat, }, - ForceAuthn: sp.ForceAuthn, + ForceAuthn: sp.ForceAuthn, + RequestedAuthnContext: sp.RequestedAuthnContext, } // We don't need to sign the XML document if the IDP uses HTTP-Redirect binding if len(sp.SignatureMethod) > 0 && binding == HTTPPostBinding { @@ -576,93 +592,80 @@ func (e ErrBadStatus) Error() string { return e.Status } -func responseIsSigned(response *etree.Element) (bool, error) { - signatureElement, err := findChild(response, "http://www.w3.org/2000/09/xmldsig#", "Signature") - if err != nil { - return false, err - } - return signatureElement != nil, nil -} - -// validateDestination validates the Destination attribute. -// If the response is signed, the Destination is required to be present. -func (sp *ServiceProvider) validateDestination(response *etree.Element, responseDom *Response) error { - signed, err := responseIsSigned(response) - if err != nil { - return err - } - - // Compare if the response is signed OR the Destination is provided. - // (Even if the response is not signed, if the Destination is set it must match.) - if signed || responseDom.Destination != "" { - if responseDom.Destination != sp.AcsURL.String() { - return fmt.Errorf("`Destination` does not match AcsURL (expected %q, actual %q)", sp.AcsURL.String(), responseDom.Destination) - } - } - - return nil -} - // ParseResponse extracts the SAML IDP response received in req, resolves // artifacts when necessary, validates it, and returns the verified assertion. func (sp *ServiceProvider) ParseResponse(req *http.Request, possibleRequestIDs []string) (*Assertion, error) { - now := TimeNow() + if artifactID := req.Form.Get("SAMLart"); artifactID != "" { + return sp.handleArtifactRequest(req.Context(), artifactID, possibleRequestIDs) + } + return sp.parseResponseHTTP(req, possibleRequestIDs) +} - var assertion *Assertion +func (sp *ServiceProvider) handleArtifactRequest(ctx context.Context, artifactID string, possibleRequestIDs []string) (*Assertion, error) { + retErr := &InvalidResponseError{Now: TimeNow()} - retErr := &InvalidResponseError{ - Now: now, - Response: req.PostForm.Get("SAMLResponse"), + artifactResolveRequest, err := sp.MakeArtifactResolveRequest(artifactID) + if err != nil { + retErr.PrivateErr = fmt.Errorf("Cannot generate artifact resolution request: %s", err) + return nil, retErr } - if req.Form.Get("SAMLart") != "" { - retErr.Response = req.Form.Get("SAMLart") - - req, err := sp.MakeArtifactResolveRequest(req.Form.Get("SAMLart")) - if err != nil { - retErr.PrivateErr = fmt.Errorf("Cannot generate artifact resolution request: %s", err) - return nil, retErr - } - - doc := etree.NewDocument() - doc.SetRoot(req.SoapRequest()) - - var requestBuffer bytes.Buffer - doc.WriteTo(&requestBuffer) - response, err := http.Post(sp.GetArtifactBindingLocation(SOAPBinding), "text/xml", &requestBuffer) - if err != nil { - retErr.PrivateErr = fmt.Errorf("Error during artifact resolution: %s", err) - return nil, retErr - } - defer response.Body.Close() - if response.StatusCode != 200 { - retErr.PrivateErr = fmt.Errorf("Error during artifact resolution: HTTP status %d (%s)", response.StatusCode, response.Status) - return nil, retErr - } - rawResponseBuf, err := ioutil.ReadAll(response.Body) - if err != nil { - retErr.PrivateErr = fmt.Errorf("Error during artifact resolution: %s", err) - return nil, retErr - } - assertion, err = sp.ParseXMLArtifactResponse(rawResponseBuf, possibleRequestIDs, req.ID) - if err != nil { - return nil, err - } - } else { - rawResponseBuf, err := base64.StdEncoding.DecodeString(req.PostForm.Get("SAMLResponse")) - if err != nil { - retErr.PrivateErr = fmt.Errorf("cannot parse base64: %s", err) - return nil, retErr - } - retErr.Response = string(rawResponseBuf) - assertion, err = sp.ParseXMLResponse(rawResponseBuf, possibleRequestIDs) - if err != nil { - return nil, err - } + requestBody, err := elementToBytes(artifactResolveRequest.SoapRequest()) + if err != nil { + retErr.PrivateErr = err + return nil, retErr } + req, err := http.NewRequestWithContext(ctx, "POST", sp.GetArtifactBindingLocation(SOAPBinding), + bytes.NewReader(requestBody)) + if err != nil { + retErr.PrivateErr = err + return nil, retErr + } + req.Header.Set("Content-Type", "text/xml") + + httpClient := sp.HTTPClient + if httpClient == nil { + httpClient = http.DefaultClient + } + response, err := httpClient.Do(req) + if err != nil { + retErr.PrivateErr = fmt.Errorf("cannot resolve artifact: %s", err) + return nil, retErr + } + defer response.Body.Close() + if response.StatusCode != 200 { + retErr.PrivateErr = fmt.Errorf("Error during artifact resolution: HTTP status %d (%s)", response.StatusCode, response.Status) + return nil, retErr + } + responseBody, err := ioutil.ReadAll(response.Body) + if err != nil { + retErr.PrivateErr = fmt.Errorf("Error during artifact resolution: %s", err) + return nil, retErr + } + assertion, err := sp.ParseXMLArtifactResponse(responseBody, possibleRequestIDs, artifactResolveRequest.ID) + if err != nil { + return nil, err + } return assertion, nil +} +func (sp *ServiceProvider) parseResponseHTTP(req *http.Request, possibleRequestIDs []string) (*Assertion, error) { + retErr := &InvalidResponseError{ + Now: TimeNow(), + } + + rawResponseBuf, err := base64.StdEncoding.DecodeString(req.PostForm.Get("SAMLResponse")) + if err != nil { + retErr.PrivateErr = fmt.Errorf("cannot parse base64: %s", err) + return nil, retErr + } + + assertion, err := sp.ParseXMLResponse(rawResponseBuf, possibleRequestIDs) + if err != nil { + return nil, err + } + return assertion, nil } // ParseXMLArtifactResponse validates the SAML Artifact resolver response @@ -675,83 +678,99 @@ func (sp *ServiceProvider) ParseResponse(req *http.Request, possibleRequestIDs [ // properties are useful in describing which part of the parsing process // failed. However, to discourage inadvertent disclosure the diagnostic // information, the Error() method returns a static string. -func (sp *ServiceProvider) ParseXMLArtifactResponse(decodedResponseXML []byte, possibleRequestIDs []string, artifactRequestID string) (*Assertion, error) { +func (sp *ServiceProvider) ParseXMLArtifactResponse(soapResponseXML []byte, possibleRequestIDs []string, artifactRequestID string) (*Assertion, error) { now := TimeNow() - //var err error retErr := &InvalidResponseError{ + Response: string(soapResponseXML), Now: now, - Response: string(decodedResponseXML), } - // ensure that the response XML is well formed before we parse it - if err := xrv.Validate(bytes.NewReader(decodedResponseXML)); err != nil { + // ensure that the response XML is well-formed before we parse it + if err := xrv.Validate(bytes.NewReader(soapResponseXML)); err != nil { retErr.PrivateErr = fmt.Errorf("invalid xml: %s", err) return nil, retErr } - envelope := &struct { - XMLName xml.Name `xml:"http://schemas.xmlsoap.org/soap/envelope/ Envelope"` - Body struct { - ArtifactResponse ArtifactResponse - } `xml:"http://schemas.xmlsoap.org/soap/envelope/ Body"` - }{} - if err := xml.Unmarshal(decodedResponseXML, &envelope); err != nil { + doc := etree.NewDocument() + if err := doc.ReadFromBytes(soapResponseXML); err != nil { retErr.PrivateErr = fmt.Errorf("cannot unmarshal response: %s", err) return nil, retErr } - - resp := envelope.Body.ArtifactResponse - - // Validate ArtifactResponse - if resp.InResponseTo != artifactRequestID { - retErr.PrivateErr = fmt.Errorf("`InResponseTo` does not match the artifact request ID (expected %v)", artifactRequestID) + if doc.Root() == nil { + retErr.PrivateErr = errors.New("invalid xml: no root") return nil, retErr } - if resp.IssueInstant.Add(MaxIssueDelay).Before(now) { - retErr.PrivateErr = fmt.Errorf("response IssueInstant expired at %s", resp.IssueInstant.Add(MaxIssueDelay)) - return nil, retErr - } - if resp.Issuer != nil && resp.Issuer.Value != sp.IDPMetadata.EntityID { - retErr.PrivateErr = fmt.Errorf("response Issuer does not match the IDP metadata (expected %q)", sp.IDPMetadata.EntityID) - return nil, retErr - } - if resp.Status.StatusCode.Value != StatusSuccess { - retErr.PrivateErr = ErrBadStatus{Status: resp.Status.StatusCode.Value} + if doc.Root().NamespaceURI() != "http://schemas.xmlsoap.org/soap/envelope/" || + doc.Root().Tag != "Envelope" { + retErr.PrivateErr = fmt.Errorf("expected a SOAP Envelope") return nil, retErr } - doc := etree.NewDocument() - if err := doc.ReadFromBytes(decodedResponseXML); err != nil { - retErr.PrivateErr = err - return nil, retErr - } - - artifactEl := doc.FindElement("Envelope/Body/ArtifactResponse") - if artifactEl == nil { - retErr.PrivateErr = fmt.Errorf("missing ArtifactResponse") - return nil, retErr - } - responseEl := doc.FindElement("Envelope/Body/ArtifactResponse/Response") - if responseEl == nil { - retErr.PrivateErr = fmt.Errorf("missing inner Response") - return nil, retErr - } - - haveSignature := false - var err error - if err = sp.validateArtifactSigned(artifactEl); err != nil && err.Error() != "either the Response or Assertion must be signed" { - retErr.PrivateErr = err - return nil, retErr - } - if err == nil { - haveSignature = true - } - assertion, updatedResponse, err := sp.validateXMLResponse(&resp.Response, responseEl, possibleRequestIDs, now, !haveSignature) + soapBodyEl, err := findOneChild(doc.Root(), "http://schemas.xmlsoap.org/soap/envelope/", "Body") if err != nil { retErr.PrivateErr = err - if updatedResponse != nil { - retErr.Response = *updatedResponse + return nil, retErr + } + + artifactResponseEl, err := findOneChild(soapBodyEl, "urn:oasis:names:tc:SAML:2.0:protocol", "ArtifactResponse") + if err != nil { + retErr.PrivateErr = err + return nil, retErr + } + + return sp.parseArtifactResponse(artifactResponseEl, possibleRequestIDs, artifactRequestID, now) +} + +func (sp *ServiceProvider) parseArtifactResponse(artifactResponseEl *etree.Element, possibleRequestIDs []string, artifactRequestID string, now time.Time) (*Assertion, error) { + retErr := &InvalidResponseError{ + Now: now, + Response: elementToString(artifactResponseEl), + } + + { + var artifactResponse ArtifactResponse + if err := unmarshalElement(artifactResponseEl, &artifactResponse); err != nil { + retErr.PrivateErr = err + return nil, retErr } + if artifactResponse.InResponseTo != artifactRequestID { + retErr.PrivateErr = fmt.Errorf("`InResponseTo` does not match the artifact request ID (expected %s)", artifactRequestID) + return nil, retErr + } + if artifactResponse.IssueInstant.Add(MaxIssueDelay).Before(now) { + retErr.PrivateErr = fmt.Errorf("response IssueInstant expired at %s", artifactResponse.IssueInstant.Add(MaxIssueDelay)) + return nil, retErr + } + if artifactResponse.Issuer != nil && artifactResponse.Issuer.Value != sp.IDPMetadata.EntityID { + retErr.PrivateErr = fmt.Errorf("response Issuer does not match the IDP metadata (expected %q)", sp.IDPMetadata.EntityID) + return nil, retErr + } + if artifactResponse.Status.StatusCode.Value != StatusSuccess { + retErr.PrivateErr = ErrBadStatus{Status: artifactResponse.Status.StatusCode.Value} + return nil, retErr + } + } + + var signatureRequirement signatureRequirement + sigErr := sp.validateSignature(artifactResponseEl) + if sigErr == nil { + signatureRequirement = signatureNotRequired + } else if sigErr == errSignatureElementNotPresent { + signatureRequirement = signatureRequired + } else { + retErr.PrivateErr = sigErr + return nil, retErr + } + + responseEl, err := findOneChild(artifactResponseEl, "urn:oasis:names:tc:SAML:2.0:protocol", "Response") + if err != nil { + retErr.PrivateErr = err + return nil, retErr + } + + assertion, err := sp.parseResponse(responseEl, possibleRequestIDs, now, signatureRequirement) + if err != nil { + retErr.PrivateErr = err return nil, retErr } @@ -777,150 +796,217 @@ func (sp *ServiceProvider) ParseXMLResponse(decodedResponseXML []byte, possibleR Response: string(decodedResponseXML), } - // ensure that the response XML is well formed before we parse it + // ensure that the response XML is well-formed before we parse it if err := xrv.Validate(bytes.NewReader(decodedResponseXML)); err != nil { retErr.PrivateErr = fmt.Errorf("invalid xml: %s", err) return nil, retErr } - // do some validation first before we decrypt - resp := Response{} - if err := xml.Unmarshal(decodedResponseXML, &resp); err != nil { - retErr.PrivateErr = fmt.Errorf("cannot unmarshal response: %s", err) - return nil, retErr - } - doc := etree.NewDocument() if err := doc.ReadFromBytes(decodedResponseXML); err != nil { retErr.PrivateErr = err return nil, retErr } + if doc.Root() == nil { + retErr.PrivateErr = errors.New("invalid xml: no root") + return nil, retErr + } - assertion, updatedResponse, err := sp.validateXMLResponse(&resp, doc.Root(), possibleRequestIDs, now, true) + assertion, err := sp.parseResponse(doc.Root(), possibleRequestIDs, now, signatureRequired) if err != nil { retErr.PrivateErr = err - if updatedResponse != nil { - retErr.Response = *updatedResponse - } return nil, retErr } return assertion, nil } +type signatureRequirement int + +const ( + signatureRequired signatureRequirement = iota + signatureNotRequired +) + // validateXMLResponse validates the SAML IDP response and returns // the verified assertion. // // This function handles decrypting the message, verifying the digital // signature on the assertion, and verifying that the specified conditions // and properties are met. -func (sp *ServiceProvider) validateXMLResponse(resp *Response, responseEl *etree.Element, possibleRequestIDs []string, now time.Time, needSig bool) (*Assertion, *string, error) { - var err error - var updatedResponse *string - if err := sp.validateDestination(responseEl, resp); err != nil { - return nil, updatedResponse, err +func (sp *ServiceProvider) parseResponse(responseEl *etree.Element, possibleRequestIDs []string, now time.Time, signatureRequirement signatureRequirement) (*Assertion, error) { + var responseSignatureErr error + var responseHasSignature bool + if signatureRequirement == signatureRequired { + responseSignatureErr = sp.validateSignature(responseEl) + if responseSignatureErr != errSignatureElementNotPresent { + responseHasSignature = true + } + + // Note: we're deferring taking action on the signature validation until after we've + // processed the request attributes, because certain test cases seem to require this mis-feature. + // TODO(ross): adjust the test cases so that we can abort here if the Response signature is invalid. } - requestIDvalid := false + // validate request attributes + { + var response Response + if err := unmarshalElement(responseEl, &response); err != nil { + return nil, fmt.Errorf("cannot unmarshal response: %v", err) + } - if sp.AllowIDPInitiated { - requestIDvalid = true - } else { - for _, possibleRequestID := range possibleRequestIDs { - if resp.InResponseTo == possibleRequestID { - requestIDvalid = true + // If the response is *not* signed, the Destination may be omitted. + if responseHasSignature || response.Destination != "" { + if response.Destination != sp.AcsURL.String() { + return nil, fmt.Errorf("`Destination` does not match AcsURL (expected %q, actual %q)", sp.AcsURL.String(), response.Destination) } } - } - if !requestIDvalid { - return nil, updatedResponse, fmt.Errorf("`InResponseTo` does not match any of the possible request IDs (expected %v)", possibleRequestIDs) - } - - if resp.IssueInstant.Add(MaxIssueDelay).Before(now) { - return nil, updatedResponse, fmt.Errorf("response IssueInstant expired at %s", resp.IssueInstant.Add(MaxIssueDelay)) - } - if resp.Issuer != nil && resp.Issuer.Value != sp.IDPMetadata.EntityID { - return nil, updatedResponse, fmt.Errorf("response Issuer does not match the IDP metadata (expected %q)", sp.IDPMetadata.EntityID) - } - if resp.Status.StatusCode.Value != StatusSuccess { - return nil, updatedResponse, ErrBadStatus{Status: resp.Status.StatusCode.Value} - } - - var assertion *Assertion - if resp.EncryptedAssertion == nil { - // TODO(ross): verify that the namespace is urn:oasis:names:tc:SAML:2.0:protocol - if responseEl.Tag != "Response" { - return nil, updatedResponse, fmt.Errorf("expected to find a response object, not %s", responseEl.Tag) + requestIDvalid := false + if sp.AllowIDPInitiated { + requestIDvalid = true + } else { + for _, possibleRequestID := range possibleRequestIDs { + if response.InResponseTo == possibleRequestID { + requestIDvalid = true + } + } + } + if !requestIDvalid { + return nil, fmt.Errorf("`InResponseTo` does not match any of the possible request IDs (expected %v)", possibleRequestIDs) } - if err = sp.validateSigned(responseEl); err != nil && !(!needSig && err.Error() == "either the Response or Assertion must be signed") { - return nil, updatedResponse, err + if response.IssueInstant.Add(MaxIssueDelay).Before(now) { + return nil, fmt.Errorf("response IssueInstant expired at %s", response.IssueInstant.Add(MaxIssueDelay)) + } + if response.Issuer != nil && response.Issuer.Value != sp.IDPMetadata.EntityID { + return nil, fmt.Errorf("response Issuer does not match the IDP metadata (expected %q)", sp.IDPMetadata.EntityID) + } + if response.Status.StatusCode.Value != StatusSuccess { + return nil, ErrBadStatus{Status: response.Status.StatusCode.Value} } - - assertion = resp.Assertion } - // decrypt the response - if resp.EncryptedAssertion != nil { - // encrypted assertions are part of the signature - // before decrypting the response verify that - responseSigned, err := responseIsSigned(responseEl) + if signatureRequirement == signatureRequired { + if responseSignatureErr == nil { + // since the request has a signature, none of the Assertions need one + signatureRequirement = signatureNotRequired + } else if responseSignatureErr == errSignatureElementNotPresent { + // the request has no signature, so assertions must be signed + signatureRequirement = signatureRequired // nop + } else { + return nil, responseSignatureErr + } + } + + var errs []error + var assertions []Assertion + + // look for encrypted assertions + { + encryptedAssertionEls, err := findChildren(responseEl, "urn:oasis:names:tc:SAML:2.0:assertion", "EncryptedAssertion") if err != nil { - return nil, updatedResponse, err + return nil, err } - if responseSigned { - if err := sp.validateSigned(responseEl); err != nil { - return nil, updatedResponse, err - } - } - - var key interface{} = sp.Key - keyEl := responseEl.FindElement("//EncryptedAssertion/EncryptedKey") - if keyEl != nil { - key, err = xmlenc.Decrypt(sp.Key, keyEl) + for _, encryptedAssertionEl := range encryptedAssertionEls { + assertion, err := sp.parseEncryptedAssertion(encryptedAssertionEl, possibleRequestIDs, now, signatureRequirement) if err != nil { - return nil, updatedResponse, fmt.Errorf("failed to decrypt key from response: %s", err) + errs = append(errs, err) + continue } + assertions = append(assertions, *assertion) } + } - el := responseEl.FindElement("//EncryptedAssertion/EncryptedData") - plaintextAssertion, err := xmlenc.Decrypt(key, el) + // look for plaintext assertions + { + assertionEls, err := findChildren(responseEl, "urn:oasis:names:tc:SAML:2.0:assertion", "Assertion") if err != nil { - return nil, updatedResponse, fmt.Errorf("failed to decrypt response: %s", err) + return nil, err } - updatedResponse = new(string) - *updatedResponse = string(plaintextAssertion) - - // TODO(ross): add test case for this - if err := xrv.Validate(bytes.NewReader(plaintextAssertion)); err != nil { - return nil, updatedResponse, fmt.Errorf("plaintext response contains invalid XML: %s", err) - } - - doc := etree.NewDocument() - if err := doc.ReadFromBytes(plaintextAssertion); err != nil { - return nil, updatedResponse, fmt.Errorf("cannot parse plaintext response %v", err) - } - - // the decrypted assertion may be signed too - // otherwise, a signed response is sufficient - if err := sp.validateSigned(doc.Root()); err != nil && !((responseSigned || !needSig) && err.Error() == "either the Response or Assertion must be signed") { - return nil, updatedResponse, err - } - - assertion = &Assertion{} - // Note: plaintextAssertion is known to be safe to parse because - // plaintextAssertion is unmodified from when xrv.Validate() was called above. - if err := xml.Unmarshal(plaintextAssertion, assertion); err != nil { - return nil, updatedResponse, err + for _, assertionEl := range assertionEls { + assertion, err := sp.parseAssertion(assertionEl, possibleRequestIDs, now, signatureRequirement) + if err != nil { + errs = append(errs, err) + continue + } + assertions = append(assertions, *assertion) } } - if err := sp.validateAssertion(assertion, possibleRequestIDs, now); err != nil { - return nil, updatedResponse, fmt.Errorf("assertion invalid: %s", err) + if len(assertions) == 0 { + if len(errs) > 0 { + return nil, errs[0] + } + return nil, fmt.Errorf("expected at least one valid Assertion, none found") } - return assertion, updatedResponse, nil + // if we have at least one assertion, return the first one. It is almost universally true that valid responses + // contain only one assertion. This is less that fully correct, but we didn't realize that there could be more + // than one assertion at the time of establishing the public interface of ParseXMLResponse(), so for compatibility + // we return the first one. + return &assertions[0], nil +} + +func (sp *ServiceProvider) parseEncryptedAssertion(encryptedAssertionEl *etree.Element, possibleRequestIDs []string, now time.Time, signatureRequirement signatureRequirement) (*Assertion, error) { + assertionEl, err := sp.decryptElement(encryptedAssertionEl) + if err != nil { + return nil, fmt.Errorf("failed to decrypt EncryptedAssertion: %v", err) + } + return sp.parseAssertion(assertionEl, possibleRequestIDs, now, signatureRequirement) +} + +func (sp *ServiceProvider) decryptElement(encryptedEl *etree.Element) (*etree.Element, error) { + encryptedDataEl, err := findOneChild(encryptedEl, "http://www.w3.org/2001/04/xmlenc#", "EncryptedData") + if err != nil { + return nil, err + } + + var key interface{} = sp.Key + keyEl := encryptedEl.FindElement("./EncryptedKey") + if keyEl != nil { + var err error + key, err = xmlenc.Decrypt(sp.Key, keyEl) + if err != nil { + return nil, fmt.Errorf("failed to decrypt key from response: %s", err) + } + } + + plaintextEl, err := xmlenc.Decrypt(key, encryptedDataEl) + if err != nil { + return nil, err + } + + if err := xrv.Validate(bytes.NewReader(plaintextEl)); err != nil { + return nil, fmt.Errorf("plaintext response contains invalid XML: %s", err) + } + + doc := etree.NewDocument() + if err := doc.ReadFromBytes(plaintextEl); err != nil { + return nil, fmt.Errorf("cannot parse plaintext response %v", err) + } + return doc.Root(), nil +} + +func (sp *ServiceProvider) parseAssertion(assertionEl *etree.Element, possibleRequestIDs []string, now time.Time, signatureRequirement signatureRequirement) (*Assertion, error) { + if signatureRequirement == signatureRequired { + sigErr := sp.validateSignature(assertionEl) + if sigErr != nil { + return nil, sigErr + } + } + + // parse the assertion we just validated + var assertion Assertion + if err := unmarshalElement(assertionEl, &assertion); err != nil { + return nil, err + } + + if err := sp.validateAssertion(&assertion, possibleRequestIDs, now); err != nil { + return nil, err + } + + return &assertion, nil } // validateAssertion checks that the conditions specified in assertion match @@ -992,117 +1078,21 @@ func (sp *ServiceProvider) validateAssertion(assertion *Assertion, possibleReque return nil } -func findChild(parentEl *etree.Element, childNS string, childTag string) (*etree.Element, error) { - for _, childEl := range parentEl.ChildElements() { - if childEl.Tag != childTag { - continue - } +var errSignatureElementNotPresent = errors.New("Signature element not present") - ctx, err := etreeutils.NSBuildParentContext(childEl) - if err != nil { - return nil, err - } - ctx, err = ctx.SubContext(childEl) - if err != nil { - return nil, err - } - - ns, err := ctx.LookupPrefix(childEl.Space) - if err != nil { - return nil, fmt.Errorf("[%s]:%s cannot find prefix %s: %v", childNS, childTag, childEl.Space, err) - } - if ns != childNS { - continue - } - - return childEl, nil - } - return nil, nil -} - -// validateArtifactSigned returns a nil error iff each of the signatures on the ArtifactResponse, Response -// and Assertion elements are valid and there is at least one signature. -func (sp *ServiceProvider) validateArtifactSigned(artifactEl *etree.Element) error { - haveSignature := false - - sigEl, err := findChild(artifactEl, "http://www.w3.org/2000/09/xmldsig#", "Signature") - if err != nil { - return err - } - if sigEl != nil { - if err = sp.validateSignature(artifactEl); err != nil { - return fmt.Errorf("cannot validate signature on Response: %v", err) - } - haveSignature = true - } - - responseEl, err := findChild(artifactEl, "urn:oasis:names:tc:SAML:2.0:protocol", "Response") - if err != nil { - return err - } - if responseEl != nil { - err = sp.validateSigned(responseEl) - if err != nil && err.Error() != "either the Response or Assertion must be signed" { - return err - } - if err == nil { - haveSignature = true // guaranteed by validateSigned - } - } - - if !haveSignature { - return errors.New("either the ArtifactResponse, Response or Assertion must be signed") - } - return nil -} - -// validateSigned returns a nil error iff each of the signatures on the Response and Assertion elements -// are valid and there is at least one signature. -func (sp *ServiceProvider) validateSigned(responseEl *etree.Element) error { - haveSignature := false - - // Some SAML responses have the signature on the Response object, and some on the Assertion - // object, and some on both. We will require that at least one signature be present and that - // all signatures be valid - sigEl, err := findChild(responseEl, "http://www.w3.org/2000/09/xmldsig#", "Signature") - if err != nil { - return err - } - if sigEl != nil { - if err = sp.validateSignature(responseEl); err != nil { - return fmt.Errorf("cannot validate signature on Response: %v", err) - } - haveSignature = true - } - - assertionEl, err := findChild(responseEl, "urn:oasis:names:tc:SAML:2.0:assertion", "Assertion") - if err != nil { - return err - } - if assertionEl != nil { - sigEl, err := findChild(assertionEl, "http://www.w3.org/2000/09/xmldsig#", "Signature") - if err != nil { - return err - } - if sigEl != nil { - if err = sp.validateSignature(assertionEl); err != nil { - return fmt.Errorf("cannot validate signature on Response: %v", err) - } - haveSignature = true - } - } - - if !haveSignature { - return errors.New("either the Response or Assertion must be signed") - } - return nil -} - -// validateSignature returns nill iff the Signature embedded in the element is valid +// validateSignature returns nil iff the Signature embedded in the element is valid func (sp *ServiceProvider) validateSignature(el *etree.Element) error { + sigEl, err := findChild(el, "http://www.w3.org/2000/09/xmldsig#", "Signature") + if err != nil { + return err + } + if sigEl == nil { + return errSignatureElementNotPresent + } + certs, err := sp.getIDPSigningCerts() if err != nil { - return err + return fmt.Errorf("cannot validate signature on %s: %v", el.Tag, err) } certificateStore := dsig.MemoryX509CertificateStore{ @@ -1134,23 +1124,26 @@ func (sp *ServiceProvider) validateSignature(el *etree.Element) error { ctx, err := etreeutils.NSBuildParentContext(el) if err != nil { - return err + return fmt.Errorf("cannot validate signature on %s: %v", el.Tag, err) } ctx, err = ctx.SubContext(el) if err != nil { - return err + return fmt.Errorf("cannot validate signature on %s: %v", el.Tag, err) } el, err = etreeutils.NSDetatch(ctx, el) if err != nil { - return err + return fmt.Errorf("cannot validate signature on %s: %v", el.Tag, err) } if sp.SignatureVerifier != nil { return sp.SignatureVerifier.VerifySignature(validationContext, el) } - _, err = validationContext.Validate(el) - return err + if _, err := validationContext.Validate(el); err != nil { + return fmt.Errorf("cannot validate signature on %s: %v", el.Tag, err) + } + + return nil } // SignLogoutRequest adds the `Signature` element to the `LogoutRequest`. @@ -1477,32 +1470,42 @@ func (sp *ServiceProvider) ValidateLogoutResponseRequest(req *http.Request) erro // ValidateLogoutResponseForm returns a nil error if the logout response is valid. func (sp *ServiceProvider) ValidateLogoutResponseForm(postFormData string) error { + retErr := &InvalidResponseError{ + Now: TimeNow(), + } + rawResponseBuf, err := base64.StdEncoding.DecodeString(postFormData) if err != nil { - return fmt.Errorf("unable to parse base64: %s", err) + retErr.PrivateErr = fmt.Errorf("unable to parse base64: %s", err) + return retErr } + retErr.Response = string(rawResponseBuf) // TODO(ross): add test case for this (SLO does not have tests right now) if err := xrv.Validate(bytes.NewReader(rawResponseBuf)); err != nil { return fmt.Errorf("response contains invalid XML: %s", err) } - var resp LogoutResponse - if err := xml.Unmarshal(rawResponseBuf, &resp); err != nil { - return fmt.Errorf("cannot unmarshal response: %s", err) + doc := etree.NewDocument() + if err := doc.ReadFromBytes(rawResponseBuf); err != nil { + retErr.PrivateErr = err + return retErr } + if err := sp.validateSignature(doc.Root()); err != nil { + retErr.PrivateErr = err + return retErr + } + + var resp LogoutResponse + if err := unmarshalElement(doc.Root(), &resp); err != nil { + retErr.PrivateErr = err + return retErr + } if err := sp.validateLogoutResponse(&resp); err != nil { return err } - - doc := etree.NewDocument() - if err := doc.ReadFromBytes(rawResponseBuf); err != nil { - return err - } - - responseEl := doc.Root() - return sp.validateSigned(responseEl) + return nil } // ValidateLogoutResponseRedirect returns a nil error if the logout response is valid. @@ -1510,40 +1513,47 @@ func (sp *ServiceProvider) ValidateLogoutResponseForm(postFormData string) error // URL Binding appears to be gzip / flate encoded // See https://www.oasis-open.org/committees/download.php/20645/sstc-saml-tech-overview-2%200-draft-10.pdf 6.6 func (sp *ServiceProvider) ValidateLogoutResponseRedirect(queryParameterData string) error { - rawResponseBuf, err := base64.StdEncoding.DecodeString(queryParameterData) - if err != nil { - return fmt.Errorf("unable to parse base64: %s", err) + retErr := &InvalidResponseError{ + Now: TimeNow(), } - gr, err := ioutil.ReadAll(flate.NewReader(bytes.NewBuffer(rawResponseBuf))) + rawResponseBuf, err := base64.StdEncoding.DecodeString(queryParameterData) if err != nil { - return err + retErr.PrivateErr = fmt.Errorf("unable to parse base64: %s", err) + return retErr + } + retErr.Response = string(rawResponseBuf) + + gr, err := ioutil.ReadAll(newSaferFlateReader(bytes.NewBuffer(rawResponseBuf))) + if err != nil { + retErr.PrivateErr = err + return retErr } if err := xrv.Validate(bytes.NewReader(gr)); err != nil { return err } - decoder := xml.NewDecoder(bytes.NewReader(gr)) - - var resp LogoutResponse - - err = decoder.Decode(&resp) - if err != nil { - return fmt.Errorf("unable to flate decode: %s", err) + doc := etree.NewDocument() + if err := doc.ReadFromBytes(rawResponseBuf); err != nil { + retErr.PrivateErr = err + return retErr } + if err := sp.validateSignature(doc.Root()); err != nil { + retErr.PrivateErr = err + return retErr + } + + var resp LogoutResponse + if err := unmarshalElement(doc.Root(), &resp); err != nil { + retErr.PrivateErr = err + return retErr + } if err := sp.validateLogoutResponse(&resp); err != nil { return err } - - doc := etree.NewDocument() - if _, err := doc.ReadFrom(bytes.NewReader(gr)); err != nil { - return err - } - - responseEl := doc.Root() - return sp.validateSigned(responseEl) + return nil } // validateLogoutResponse validates the LogoutResponse fields. Returns a nil error if the LogoutResponse is valid. @@ -1572,3 +1582,101 @@ func firstSet(a, b string) string { } return a } + +// findChildren returns all the elements matching childNS/childTag that are direct children of parentEl. +func findChildren(parentEl *etree.Element, childNS string, childTag string) ([]*etree.Element, error) { + var rv []*etree.Element + for _, childEl := range parentEl.ChildElements() { + if childEl.Tag != childTag { + continue + } + + ctx, err := etreeutils.NSBuildParentContext(childEl) + if err != nil { + return nil, err + } + ctx, err = ctx.SubContext(childEl) + if err != nil { + return nil, err + } + + ns, err := ctx.LookupPrefix(childEl.Space) + if err != nil { + return nil, fmt.Errorf("[%s]:%s cannot find prefix %s: %v", childNS, childTag, childEl.Space, err) + } + if ns != childNS { + continue + } + + rv = append(rv, childEl) + } + + return rv, nil +} + +// findOneChild finds the specified child element. Returns an error if the element doesn't exist. +func findOneChild(parentEl *etree.Element, childNS string, childTag string) (*etree.Element, error) { + children, err := findChildren(parentEl, childNS, childTag) + if err != nil { + return nil, err + } + switch len(children) { + case 0: + return nil, fmt.Errorf("cannot find %s:%s element", childNS, childTag) + case 1: + return children[0], nil + default: + return nil, fmt.Errorf("expected exactly one %s:%s element", childNS, childTag) + } +} + +// findChild finds the specified child element. Returns (nil, nil) of the element doesn't exist. +func findChild(parentEl *etree.Element, childNS string, childTag string) (*etree.Element, error) { + children, err := findChildren(parentEl, childNS, childTag) + if err != nil { + return nil, err + } + switch len(children) { + case 0: + return nil, nil + case 1: + return children[0], nil + default: + return nil, fmt.Errorf("expected at most one %s:%s element", childNS, childTag) + } +} + +func elementToBytes(el *etree.Element) ([]byte, error) { + namespaces := map[string]string{} + for _, childEl := range el.FindElements("//*") { + ns := childEl.NamespaceURI() + if ns != "" { + namespaces[childEl.Space] = ns + } + } + + doc := etree.NewDocument() + doc.SetRoot(el.Copy()) + for space, uri := range namespaces { + doc.Root().CreateAttr("xmlns:"+space, uri) + } + + return doc.WriteToBytes() +} + +// unmarshalElement serializes el into v by serializing el and then parsing it with xml.Unmarshal. +func unmarshalElement(el *etree.Element, v interface{}) error { + buf, err := elementToBytes(el) + if err != nil { + return err + } + return xml.Unmarshal(buf, v) +} + +func elementToString(el *etree.Element) string { + buf, err := elementToBytes(el) + if err != nil { + return "" + } + return string(buf) +} diff --git a/server/vendor/github.com/golang-jwt/jwt/v4/README.md b/server/vendor/github.com/golang-jwt/jwt/v4/README.md index 01b21646e..30f2f2a6f 100644 --- a/server/vendor/github.com/golang-jwt/jwt/v4/README.md +++ b/server/vendor/github.com/golang-jwt/jwt/v4/README.md @@ -36,24 +36,39 @@ The part in the middle is the interesting bit. It's called the Claims and conta This library supports the parsing and verification as well as the generation and signing of JWTs. Current supported signing algorithms are HMAC SHA, RSA, RSA-PSS, and ECDSA, though hooks are present for adding your own. +## Installation Guidelines + +1. To install the jwt package, you first need to have [Go](https://go.dev/doc/install) installed, then you can use the command below to add `jwt-go` as a dependency in your Go program. + +```sh +go get -u github.com/golang-jwt/jwt/v4 +``` + +2. Import it in your code: + +```go +import "github.com/golang-jwt/jwt/v4" +``` + ## Examples -See [the project documentation](https://pkg.go.dev/github.com/golang-jwt/jwt) for examples of usage: +See [the project documentation](https://pkg.go.dev/github.com/golang-jwt/jwt/v4) for examples of usage: -* [Simple example of parsing and validating a token](https://pkg.go.dev/github.com/golang-jwt/jwt#example-Parse-Hmac) -* [Simple example of building and signing a token](https://pkg.go.dev/github.com/golang-jwt/jwt#example-New-Hmac) -* [Directory of Examples](https://pkg.go.dev/github.com/golang-jwt/jwt#pkg-examples) +* [Simple example of parsing and validating a token](https://pkg.go.dev/github.com/golang-jwt/jwt/v4#example-Parse-Hmac) +* [Simple example of building and signing a token](https://pkg.go.dev/github.com/golang-jwt/jwt/v4#example-New-Hmac) +* [Directory of Examples](https://pkg.go.dev/github.com/golang-jwt/jwt/v4#pkg-examples) ## Extensions -This library publishes all the necessary components for adding your own signing methods. Simply implement the `SigningMethod` interface and register a factory method using `RegisterSigningMethod`. +This library publishes all the necessary components for adding your own signing methods or key functions. Simply implement the `SigningMethod` interface and register a factory method using `RegisterSigningMethod` or provide a `jwt.Keyfunc`. -A common use case would be integrating with different 3rd party signature providers, like key management services from various cloud providers or Hardware Security Modules (HSMs). +A common use case would be integrating with different 3rd party signature providers, like key management services from various cloud providers or Hardware Security Modules (HSMs) or to implement additional standards. -| Extension | Purpose | Repo | -|-----------|----------------------------------------------------------------------------------------------|--------------------------------------------| -| GCP | Integrates with multiple Google Cloud Platform signing tools (AppEngine, IAM API, Cloud KMS) | https://github.com/someone1/gcp-jwt-go | -| AWS | Integrates with AWS Key Management Service, KMS | https://github.com/matelang/jwt-go-aws-kms | +| Extension | Purpose | Repo | +| --------- | -------------------------------------------------------------------------------------------------------- | ------------------------------------------ | +| GCP | Integrates with multiple Google Cloud Platform signing tools (AppEngine, IAM API, Cloud KMS) | https://github.com/someone1/gcp-jwt-go | +| AWS | Integrates with AWS Key Management Service, KMS | https://github.com/matelang/jwt-go-aws-kms | +| JWKS | Provides support for JWKS ([RFC 7517](https://datatracker.ietf.org/doc/html/rfc7517)) as a `jwt.Keyfunc` | https://github.com/MicahParks/keyfunc | *Disclaimer*: Unless otherwise specified, these integrations are maintained by third parties and should not be considered as a primary offer by any of the mentioned cloud providers @@ -81,7 +96,7 @@ A token is simply a JSON object that is signed by its author. this tells you exa * The author of the token was in the possession of the signing secret * The data has not been modified since it was signed -It's important to know that JWT does not provide encryption, which means anyone who has access to the token can read its contents. If you need to protect (encrypt) the data, there is a companion spec, `JWE`, that provides this functionality. JWE is currently outside the scope of this library. +It's important to know that JWT does not provide encryption, which means anyone who has access to the token can read its contents. If you need to protect (encrypt) the data, there is a companion spec, `JWE`, that provides this functionality. The companion project https://github.com/golang-jwt/jwe aims at a (very) experimental implementation of the JWE standard. ### Choosing a Signing Method @@ -95,10 +110,10 @@ Asymmetric signing methods, such as RSA, use different keys for signing and veri Each signing method expects a different object type for its signing keys. See the package documentation for details. Here are the most common ones: -* The [HMAC signing method](https://pkg.go.dev/github.com/golang-jwt/jwt#SigningMethodHMAC) (`HS256`,`HS384`,`HS512`) expect `[]byte` values for signing and validation -* The [RSA signing method](https://pkg.go.dev/github.com/golang-jwt/jwt#SigningMethodRSA) (`RS256`,`RS384`,`RS512`) expect `*rsa.PrivateKey` for signing and `*rsa.PublicKey` for validation -* The [ECDSA signing method](https://pkg.go.dev/github.com/golang-jwt/jwt#SigningMethodECDSA) (`ES256`,`ES384`,`ES512`) expect `*ecdsa.PrivateKey` for signing and `*ecdsa.PublicKey` for validation -* The [EdDSA signing method](https://pkg.go.dev/github.com/golang-jwt/jwt#SigningMethodEd25519) (`Ed25519`) expect `ed25519.PrivateKey` for signing and `ed25519.PublicKey` for validation +* The [HMAC signing method](https://pkg.go.dev/github.com/golang-jwt/jwt/v4#SigningMethodHMAC) (`HS256`,`HS384`,`HS512`) expect `[]byte` values for signing and validation +* The [RSA signing method](https://pkg.go.dev/github.com/golang-jwt/jwt/v4#SigningMethodRSA) (`RS256`,`RS384`,`RS512`) expect `*rsa.PrivateKey` for signing and `*rsa.PublicKey` for validation +* The [ECDSA signing method](https://pkg.go.dev/github.com/golang-jwt/jwt/v4#SigningMethodECDSA) (`ES256`,`ES384`,`ES512`) expect `*ecdsa.PrivateKey` for signing and `*ecdsa.PublicKey` for validation +* The [EdDSA signing method](https://pkg.go.dev/github.com/golang-jwt/jwt/v4#SigningMethodEd25519) (`Ed25519`) expect `ed25519.PrivateKey` for signing and `ed25519.PublicKey` for validation ### JWT and OAuth @@ -116,7 +131,7 @@ This library uses descriptive error messages whenever possible. If you are not g ## More -Documentation can be found [on pkg.go.dev](https://pkg.go.dev/github.com/golang-jwt/jwt). +Documentation can be found [on pkg.go.dev](https://pkg.go.dev/github.com/golang-jwt/jwt/v4). The command line utility included in this project (cmd/jwt) provides a straightforward example of token creation and parsing as well as a useful tool for debugging your own integration. You'll also find several implementation examples in the documentation. diff --git a/server/vendor/github.com/golang-jwt/jwt/v4/SECURITY.md b/server/vendor/github.com/golang-jwt/jwt/v4/SECURITY.md new file mode 100644 index 000000000..b08402c34 --- /dev/null +++ b/server/vendor/github.com/golang-jwt/jwt/v4/SECURITY.md @@ -0,0 +1,19 @@ +# Security Policy + +## Supported Versions + +As of February 2022 (and until this document is updated), the latest version `v4` is supported. + +## Reporting a Vulnerability + +If you think you found a vulnerability, and even if you are not sure, please report it to jwt-go-security@googlegroups.com or one of the other [golang-jwt maintainers](https://github.com/orgs/golang-jwt/people). Please try be explicit, describe steps to reproduce the security issue with code example(s). + +You will receive a response within a timely manner. If the issue is confirmed, we will do our best to release a patch as soon as possible given the complexity of the problem. + +## Public Discussions + +Please avoid publicly discussing a potential security vulnerability. + +Let's take this offline and find a solution first, this limits the potential impact as much as possible. + +We appreciate your help! diff --git a/server/vendor/github.com/golang-jwt/jwt/v4/claims.go b/server/vendor/github.com/golang-jwt/jwt/v4/claims.go index 9d95cad2b..364cec877 100644 --- a/server/vendor/github.com/golang-jwt/jwt/v4/claims.go +++ b/server/vendor/github.com/golang-jwt/jwt/v4/claims.go @@ -265,9 +265,5 @@ func verifyIss(iss string, cmp string, required bool) bool { if iss == "" { return !required } - if subtle.ConstantTimeCompare([]byte(iss), []byte(cmp)) != 0 { - return true - } else { - return false - } + return subtle.ConstantTimeCompare([]byte(iss), []byte(cmp)) != 0 } diff --git a/server/vendor/github.com/golang-jwt/jwt/v4/parser.go b/server/vendor/github.com/golang-jwt/jwt/v4/parser.go index 2f61a69d7..c0a6f6927 100644 --- a/server/vendor/github.com/golang-jwt/jwt/v4/parser.go +++ b/server/vendor/github.com/golang-jwt/jwt/v4/parser.go @@ -42,6 +42,13 @@ func (p *Parser) Parse(tokenString string, keyFunc Keyfunc) (*Token, error) { return p.ParseWithClaims(tokenString, MapClaims{}, keyFunc) } +// ParseWithClaims parses, validates, and verifies like Parse, but supplies a default object implementing the Claims +// interface. This provides default values which can be overridden and allows a caller to use their own type, rather +// than the default MapClaims implementation of Claims. +// +// Note: If you provide a custom claim implementation that embeds one of the standard claims (such as RegisteredClaims), +// make sure that a) you either embed a non-pointer version of the claims or b) if you are using a pointer, allocate the +// proper memory for it before passing in the overall claims, otherwise you might run into a panic. func (p *Parser) ParseWithClaims(tokenString string, claims Claims, keyFunc Keyfunc) (*Token, error) { token, parts, err := p.ParseUnverified(tokenString, claims) if err != nil { diff --git a/server/vendor/github.com/golang-jwt/jwt/v4/rsa_pss.go b/server/vendor/github.com/golang-jwt/jwt/v4/rsa_pss.go index 5a8502feb..4fd6f9e61 100644 --- a/server/vendor/github.com/golang-jwt/jwt/v4/rsa_pss.go +++ b/server/vendor/github.com/golang-jwt/jwt/v4/rsa_pss.go @@ -1,3 +1,4 @@ +//go:build go1.4 // +build go1.4 package jwt diff --git a/server/vendor/github.com/golang-jwt/jwt/v4/token.go b/server/vendor/github.com/golang-jwt/jwt/v4/token.go index 09b4cde5a..71e909ea6 100644 --- a/server/vendor/github.com/golang-jwt/jwt/v4/token.go +++ b/server/vendor/github.com/golang-jwt/jwt/v4/token.go @@ -7,7 +7,6 @@ import ( "time" ) - // DecodePaddingAllowed will switch the codec used for decoding JWTs respectively. Note that the JWS RFC7515 // states that the tokens will utilize a Base64url encoding with no padding. Unfortunately, some implementations // of JWT are producing non-standard tokens, and thus require support for decoding. Note that this is a global @@ -100,6 +99,11 @@ func Parse(tokenString string, keyFunc Keyfunc, options ...ParserOption) (*Token return NewParser(options...).Parse(tokenString, keyFunc) } +// ParseWithClaims is a shortcut for NewParser().ParseWithClaims(). +// +// Note: If you provide a custom claim implementation that embeds one of the standard claims (such as RegisteredClaims), +// make sure that a) you either embed a non-pointer version of the claims or b) if you are using a pointer, allocate the +// proper memory for it before passing in the overall claims, otherwise you might run into a panic. func ParseWithClaims(tokenString string, claims Claims, keyFunc Keyfunc, options ...ParserOption) (*Token, error) { return NewParser(options...).ParseWithClaims(tokenString, claims, keyFunc) } diff --git a/server/vendor/github.com/golang-jwt/jwt/v4/types.go b/server/vendor/github.com/golang-jwt/jwt/v4/types.go index 2c647fd2e..ac8e140eb 100644 --- a/server/vendor/github.com/golang-jwt/jwt/v4/types.go +++ b/server/vendor/github.com/golang-jwt/jwt/v4/types.go @@ -53,9 +53,23 @@ func (date NumericDate) MarshalJSON() (b []byte, err error) { if TimePrecision < time.Second { prec = int(math.Log10(float64(time.Second) / float64(TimePrecision))) } - f := float64(date.Truncate(TimePrecision).UnixNano()) / float64(time.Second) + truncatedDate := date.Truncate(TimePrecision) - return []byte(strconv.FormatFloat(f, 'f', prec, 64)), nil + // For very large timestamps, UnixNano would overflow an int64, but this + // function requires nanosecond level precision, so we have to use the + // following technique to get round the issue: + // 1. Take the normal unix timestamp to form the whole number part of the + // output, + // 2. Take the result of the Nanosecond function, which retuns the offset + // within the second of the particular unix time instance, to form the + // decimal part of the output + // 3. Concatenate them to produce the final result + seconds := strconv.FormatInt(truncatedDate.Unix(), 10) + nanosecondsOffset := strconv.FormatFloat(float64(truncatedDate.Nanosecond())/float64(time.Second), 'f', prec, 64) + + output := append([]byte(seconds), []byte(nanosecondsOffset)[1:]...) + + return output, nil } // UnmarshalJSON is an implementation of the json.RawMessage interface and deserializses a diff --git a/server/vendor/github.com/russellhaering/goxmldsig/README.md b/server/vendor/github.com/russellhaering/goxmldsig/README.md index 9464e61ec..a77588751 100644 --- a/server/vendor/github.com/russellhaering/goxmldsig/README.md +++ b/server/vendor/github.com/russellhaering/goxmldsig/README.md @@ -1,6 +1,6 @@ # goxmldsig -[](https://travis-ci.org/russellhaering/goxmldsig) + [](https://godoc.org/github.com/russellhaering/goxmldsig) XML Digital Signatures implemented in pure Go. diff --git a/server/vendor/github.com/russellhaering/goxmldsig/canonicalize.go b/server/vendor/github.com/russellhaering/goxmldsig/canonicalize.go index 05655ebc4..75392d135 100644 --- a/server/vendor/github.com/russellhaering/goxmldsig/canonicalize.go +++ b/server/vendor/github.com/russellhaering/goxmldsig/canonicalize.go @@ -26,11 +26,12 @@ func (c *NullCanonicalizer) Algorithm() AlgorithmID { func (c *NullCanonicalizer) Canonicalize(el *etree.Element) ([]byte, error) { scope := make(map[string]struct{}) - return canonicalSerialize(canonicalPrep(el, scope, false)) + return canonicalSerialize(canonicalPrep(el, scope, false, true)) } type c14N10ExclusiveCanonicalizer struct { prefixList string + comments bool } // MakeC14N10ExclusiveCanonicalizerWithPrefixList constructs an exclusive Canonicalizer @@ -38,12 +39,22 @@ type c14N10ExclusiveCanonicalizer struct { func MakeC14N10ExclusiveCanonicalizerWithPrefixList(prefixList string) Canonicalizer { return &c14N10ExclusiveCanonicalizer{ prefixList: prefixList, + comments: false, + } +} + +// MakeC14N10ExclusiveWithCommentsCanonicalizerWithPrefixList constructs an exclusive Canonicalizer +// from a PrefixList in NMTOKENS format (a white space separated list). +func MakeC14N10ExclusiveWithCommentsCanonicalizerWithPrefixList(prefixList string) Canonicalizer { + return &c14N10ExclusiveCanonicalizer{ + prefixList: prefixList, + comments: true, } } // Canonicalize transforms the input Element into a serialized XML document in canonical form. func (c *c14N10ExclusiveCanonicalizer) Canonicalize(el *etree.Element) ([]byte, error) { - err := etreeutils.TransformExcC14n(el, c.prefixList) + err := etreeutils.TransformExcC14n(el, c.prefixList, c.comments) if err != nil { return nil, err } @@ -52,58 +63,73 @@ func (c *c14N10ExclusiveCanonicalizer) Canonicalize(el *etree.Element) ([]byte, } func (c *c14N10ExclusiveCanonicalizer) Algorithm() AlgorithmID { + if c.comments { + return CanonicalXML10ExclusiveWithCommentsAlgorithmId + } return CanonicalXML10ExclusiveAlgorithmId } -type c14N11Canonicalizer struct{} +type c14N11Canonicalizer struct { + comments bool +} // MakeC14N11Canonicalizer constructs an inclusive canonicalizer. func MakeC14N11Canonicalizer() Canonicalizer { - return &c14N11Canonicalizer{} + return &c14N11Canonicalizer{ + comments: false, + } +} + +// MakeC14N11WithCommentsCanonicalizer constructs an inclusive canonicalizer. +func MakeC14N11WithCommentsCanonicalizer() Canonicalizer { + return &c14N11Canonicalizer{ + comments: true, + } } // Canonicalize transforms the input Element into a serialized XML document in canonical form. func (c *c14N11Canonicalizer) Canonicalize(el *etree.Element) ([]byte, error) { scope := make(map[string]struct{}) - return canonicalSerialize(canonicalPrep(el, scope, true)) + return canonicalSerialize(canonicalPrep(el, scope, true, c.comments)) } func (c *c14N11Canonicalizer) Algorithm() AlgorithmID { + if c.comments { + return CanonicalXML11WithCommentsAlgorithmId + } return CanonicalXML11AlgorithmId } -type c14N10RecCanonicalizer struct{} +type c14N10RecCanonicalizer struct { + comments bool +} // MakeC14N10RecCanonicalizer constructs an inclusive canonicalizer. func MakeC14N10RecCanonicalizer() Canonicalizer { - return &c14N10RecCanonicalizer{} + return &c14N10RecCanonicalizer{ + comments: false, + } +} + +// MakeC14N10WithCommentsCanonicalizer constructs an inclusive canonicalizer. +func MakeC14N10WithCommentsCanonicalizer() Canonicalizer { + return &c14N10RecCanonicalizer{ + comments: true, + } } // Canonicalize transforms the input Element into a serialized XML document in canonical form. func (c *c14N10RecCanonicalizer) Canonicalize(el *etree.Element) ([]byte, error) { scope := make(map[string]struct{}) - return canonicalSerialize(canonicalPrep(el, scope, true)) + return canonicalSerialize(canonicalPrep(el, scope, true, c.comments)) } func (c *c14N10RecCanonicalizer) Algorithm() AlgorithmID { + if c.comments { + return CanonicalXML10WithCommentsAlgorithmId + } return CanonicalXML10RecAlgorithmId -} -type c14N10CommentCanonicalizer struct{} - -// MakeC14N10CommentCanonicalizer constructs an inclusive canonicalizer. -func MakeC14N10CommentCanonicalizer() Canonicalizer { - return &c14N10CommentCanonicalizer{} -} - -// Canonicalize transforms the input Element into a serialized XML document in canonical form. -func (c *c14N10CommentCanonicalizer) Canonicalize(el *etree.Element) ([]byte, error) { - scope := make(map[string]struct{}) - return canonicalSerialize(canonicalPrep(el, scope, true)) -} - -func (c *c14N10CommentCanonicalizer) Algorithm() AlgorithmID { - return CanonicalXML10CommentAlgorithmId } func composeAttr(space, key string) string { @@ -132,7 +158,7 @@ const nsSpace = "xmlns" // // TODO(russell_h): This is very similar to excCanonicalPrep - perhaps they should // be unified into one parameterized function? -func canonicalPrep(el *etree.Element, seenSoFar map[string]struct{}, strip bool) *etree.Element { +func canonicalPrep(el *etree.Element, seenSoFar map[string]struct{}, strip bool, comments bool) *etree.Element { _seenSoFar := make(map[string]struct{}) for k, v := range seenSoFar { _seenSoFar[k] = v @@ -140,16 +166,29 @@ func canonicalPrep(el *etree.Element, seenSoFar map[string]struct{}, strip bool) ne := el.Copy() sort.Sort(etreeutils.SortedAttrs(ne.Attr)) - if len(ne.Attr) != 0 { - for _, attr := range ne.Attr { - if attr.Space != nsSpace { - continue - } - key := attr.Space + ":" + attr.Key - if _, seen := _seenSoFar[key]; seen { - ne.RemoveAttr(attr.Space + ":" + attr.Key) + n := 0 + for _, attr := range ne.Attr { + if attr.Space != nsSpace { + ne.Attr[n] = attr + n++ + continue + } + key := attr.Space + ":" + attr.Key + if _, seen := _seenSoFar[key]; !seen { + ne.Attr[n] = attr + n++ + _seenSoFar[key] = struct{}{} + } + } + ne.Attr = ne.Attr[:n] + + if !comments { + c := 0 + for c < len(ne.Child) { + if _, ok := ne.Child[c].(*etree.Comment); ok { + ne.RemoveChildAt(c) } else { - _seenSoFar[key] = struct{}{} + c++ } } } @@ -157,7 +196,7 @@ func canonicalPrep(el *etree.Element, seenSoFar map[string]struct{}, strip bool) for i, token := range ne.Child { childElement, ok := token.(*etree.Element) if ok { - ne.Child[i] = canonicalPrep(childElement, _seenSoFar, strip) + ne.Child[i] = canonicalPrep(childElement, _seenSoFar, strip, comments) } } diff --git a/server/vendor/github.com/russellhaering/goxmldsig/etreeutils/canonicalize.go b/server/vendor/github.com/russellhaering/goxmldsig/etreeutils/canonicalize.go index e9f8deb18..8437fe403 100644 --- a/server/vendor/github.com/russellhaering/goxmldsig/etreeutils/canonicalize.go +++ b/server/vendor/github.com/russellhaering/goxmldsig/etreeutils/canonicalize.go @@ -8,7 +8,7 @@ import ( ) // TransformExcC14n transforms the passed element into xml-exc-c14n form. -func TransformExcC14n(el *etree.Element, inclusiveNamespacesPrefixList string) error { +func TransformExcC14n(el *etree.Element, inclusiveNamespacesPrefixList string, comments bool) error { prefixes := strings.Fields(inclusiveNamespacesPrefixList) prefixSet := make(map[string]struct{}, len(prefixes)) @@ -16,7 +16,7 @@ func TransformExcC14n(el *etree.Element, inclusiveNamespacesPrefixList string) e prefixSet[prefix] = struct{}{} } - err := transformExcC14n(DefaultNSContext, DefaultNSContext, el, prefixSet) + err := transformExcC14n(DefaultNSContext, DefaultNSContext, el, prefixSet, comments) if err != nil { return err } @@ -24,7 +24,7 @@ func TransformExcC14n(el *etree.Element, inclusiveNamespacesPrefixList string) e return nil } -func transformExcC14n(ctx, declared NSContext, el *etree.Element, inclusiveNamespaces map[string]struct{}) error { +func transformExcC14n(ctx, declared NSContext, el *etree.Element, inclusiveNamespaces map[string]struct{}, comments bool) error { scope, err := ctx.SubContext(el) if err != nil { return err @@ -86,9 +86,20 @@ func transformExcC14n(ctx, declared NSContext, el *etree.Element, inclusiveNames sort.Sort(SortedAttrs(el.Attr)) + if !comments { + c := 0 + for c < len(el.Child) { + if _, ok := el.Child[c].(*etree.Comment); ok { + el.RemoveChildAt(c) + } else { + c++ + } + } + } + // Transform child elements for _, child := range el.ChildElements() { - err := transformExcC14n(scope, declared, child, inclusiveNamespaces) + err := transformExcC14n(scope, declared, child, inclusiveNamespaces, comments) if err != nil { return err } diff --git a/server/vendor/github.com/russellhaering/goxmldsig/validate.go b/server/vendor/github.com/russellhaering/goxmldsig/validate.go index 840458588..2c65ca1c1 100644 --- a/server/vendor/github.com/russellhaering/goxmldsig/validate.go +++ b/server/vendor/github.com/russellhaering/goxmldsig/validate.go @@ -21,7 +21,7 @@ var ( // ErrMissingSignature indicates that no enveloped signature was found referencing // the top level element passed for signature verification. ErrMissingSignature = errors.New("Missing signature referencing the top-level element") - ErrInvalidSignature = errors.New( "Invalid Signature") + ErrInvalidSignature = errors.New("Invalid Signature") ) type ValidationContext struct { @@ -70,7 +70,7 @@ func mapPathToElement(tree, el *etree.Element) []int { for i, child := range tree.Child { if childElement, ok := child.(*etree.Element); ok { childPath := mapPathToElement(childElement, el) - if childElement != nil { + if childPath != nil { return append([]int{i}, childPath...) } } @@ -138,14 +138,25 @@ func (ctx *ValidationContext) transform( canonicalizer = MakeC14N10ExclusiveCanonicalizerWithPrefixList(prefixList) + case CanonicalXML10ExclusiveWithCommentsAlgorithmId: + var prefixList string + if transform.InclusiveNamespaces != nil { + prefixList = transform.InclusiveNamespaces.PrefixList + } + + canonicalizer = MakeC14N10ExclusiveWithCommentsCanonicalizerWithPrefixList(prefixList) + case CanonicalXML11AlgorithmId: canonicalizer = MakeC14N11Canonicalizer() + case CanonicalXML11WithCommentsAlgorithmId: + canonicalizer = MakeC14N11WithCommentsCanonicalizer() + case CanonicalXML10RecAlgorithmId: canonicalizer = MakeC14N10RecCanonicalizer() - case CanonicalXML10CommentAlgorithmId: - canonicalizer = MakeC14N10CommentCanonicalizer() + case CanonicalXML10WithCommentsAlgorithmId: + canonicalizer = MakeC14N10WithCommentsCanonicalizer() default: return nil, nil, errors.New("Unknown Transform Algorithm: " + algo) @@ -353,9 +364,9 @@ func (ctx *ValidationContext) findSignature(root *etree.Element) (*types.Signatu var canonicalSignedInfo *etree.Element - switch AlgorithmID(c14NAlgorithm) { - case CanonicalXML10ExclusiveAlgorithmId: - err := etreeutils.TransformExcC14n(detachedSignedInfo, "") + switch alg := AlgorithmID(c14NAlgorithm); alg { + case CanonicalXML10ExclusiveAlgorithmId, CanonicalXML10ExclusiveWithCommentsAlgorithmId: + err := etreeutils.TransformExcC14n(detachedSignedInfo, "", alg == CanonicalXML10ExclusiveWithCommentsAlgorithmId) if err != nil { return err } @@ -366,14 +377,11 @@ func (ctx *ValidationContext) findSignature(root *etree.Element) (*types.Signatu // removing of elements below. canonicalSignedInfo = detachedSignedInfo - case CanonicalXML11AlgorithmId: - canonicalSignedInfo = canonicalPrep(detachedSignedInfo, map[string]struct{}{}, true) + case CanonicalXML11AlgorithmId, CanonicalXML10RecAlgorithmId: + canonicalSignedInfo = canonicalPrep(detachedSignedInfo, map[string]struct{}{}, true, false) - case CanonicalXML10RecAlgorithmId: - canonicalSignedInfo = canonicalPrep(detachedSignedInfo, map[string]struct{}{}, true) - - case CanonicalXML10CommentAlgorithmId: - canonicalSignedInfo = canonicalPrep(detachedSignedInfo, map[string]struct{}{}, true) + case CanonicalXML11WithCommentsAlgorithmId, CanonicalXML10WithCommentsAlgorithmId: + canonicalSignedInfo = canonicalPrep(detachedSignedInfo, map[string]struct{}{}, true, true) default: return fmt.Errorf("invalid CanonicalizationMethod on Signature: %s", c14NAlgorithm) diff --git a/server/vendor/github.com/russellhaering/goxmldsig/xml_constants.go b/server/vendor/github.com/russellhaering/goxmldsig/xml_constants.go index c4b815b29..d2b98e257 100644 --- a/server/vendor/github.com/russellhaering/goxmldsig/xml_constants.go +++ b/server/vendor/github.com/russellhaering/goxmldsig/xml_constants.go @@ -47,11 +47,14 @@ const ( //Well-known signature algorithms const ( // Supported canonicalization algorithms - CanonicalXML10ExclusiveAlgorithmId AlgorithmID = "http://www.w3.org/2001/10/xml-exc-c14n#" - CanonicalXML11AlgorithmId AlgorithmID = "http://www.w3.org/2006/12/xml-c14n11" + CanonicalXML10ExclusiveAlgorithmId AlgorithmID = "http://www.w3.org/2001/10/xml-exc-c14n#" + CanonicalXML10ExclusiveWithCommentsAlgorithmId AlgorithmID = "http://www.w3.org/2001/10/xml-exc-c14n#WithComments" - CanonicalXML10RecAlgorithmId AlgorithmID = "http://www.w3.org/TR/2001/REC-xml-c14n-20010315" - CanonicalXML10CommentAlgorithmId AlgorithmID = "http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments" + CanonicalXML11AlgorithmId AlgorithmID = "http://www.w3.org/2006/12/xml-c14n11" + CanonicalXML11WithCommentsAlgorithmId AlgorithmID = "http://www.w3.org/2006/12/xml-c14n11#WithComments" + + CanonicalXML10RecAlgorithmId AlgorithmID = "http://www.w3.org/TR/2001/REC-xml-c14n-20010315" + CanonicalXML10WithCommentsAlgorithmId AlgorithmID = "http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments" EnvelopedSignatureAltorithmId AlgorithmID = "http://www.w3.org/2000/09/xmldsig#enveloped-signature" ) diff --git a/server/vendor/github.com/steinfletcher/apitest/template.go b/server/vendor/github.com/steinfletcher/apitest/template.go index 63cbd2987..43c590ad0 100644 --- a/server/vendor/github.com/steinfletcher/apitest/template.go +++ b/server/vendor/github.com/steinfletcher/apitest/template.go @@ -35,7 +35,7 @@ const reportTemplate = ` } .copy-to-clipboard-button { - background-color: #FFFFFF; + background-color: #fff; border: 1px solid #eee; cursor: pointer; font-size: 12px; diff --git a/server/vendor/github.com/stretchr/testify/assert/assertion_compare.go b/server/vendor/github.com/stretchr/testify/assert/assertion_compare.go index 3bb22a971..95d8e59da 100644 --- a/server/vendor/github.com/stretchr/testify/assert/assertion_compare.go +++ b/server/vendor/github.com/stretchr/testify/assert/assertion_compare.go @@ -1,6 +1,7 @@ package assert import ( + "bytes" "fmt" "reflect" "time" @@ -32,7 +33,8 @@ var ( stringType = reflect.TypeOf("") - timeType = reflect.TypeOf(time.Time{}) + timeType = reflect.TypeOf(time.Time{}) + bytesType = reflect.TypeOf([]byte{}) ) func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) { @@ -323,6 +325,26 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) { return compare(timeObj1.UnixNano(), timeObj2.UnixNano(), reflect.Int64) } + case reflect.Slice: + { + // We only care about the []byte type. + if !canConvert(obj1Value, bytesType) { + break + } + + // []byte can be compared! + bytesObj1, ok := obj1.([]byte) + if !ok { + bytesObj1 = obj1Value.Convert(bytesType).Interface().([]byte) + + } + bytesObj2, ok := obj2.([]byte) + if !ok { + bytesObj2 = obj2Value.Convert(bytesType).Interface().([]byte) + } + + return CompareType(bytes.Compare(bytesObj1, bytesObj2)), true + } } return compareEqual, false diff --git a/server/vendor/github.com/stretchr/testify/assert/assertion_compare_can_convert.go b/server/vendor/github.com/stretchr/testify/assert/assertion_compare_can_convert.go index df22c47fc..da867903e 100644 --- a/server/vendor/github.com/stretchr/testify/assert/assertion_compare_can_convert.go +++ b/server/vendor/github.com/stretchr/testify/assert/assertion_compare_can_convert.go @@ -9,7 +9,7 @@ package assert import "reflect" -// Wrapper around reflect.Value.CanConvert, for compatability +// Wrapper around reflect.Value.CanConvert, for compatibility // reasons. func canConvert(value reflect.Value, to reflect.Type) bool { return value.CanConvert(to) diff --git a/server/vendor/github.com/stretchr/testify/assert/assertion_format.go b/server/vendor/github.com/stretchr/testify/assert/assertion_format.go index 27e2420ed..7880b8f94 100644 --- a/server/vendor/github.com/stretchr/testify/assert/assertion_format.go +++ b/server/vendor/github.com/stretchr/testify/assert/assertion_format.go @@ -736,6 +736,16 @@ func WithinDurationf(t TestingT, expected time.Time, actual time.Time, delta tim return WithinDuration(t, expected, actual, delta, append([]interface{}{msg}, args...)...) } +// WithinRangef asserts that a time is within a time range (inclusive). +// +// assert.WithinRangef(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second), "error message %s", "formatted") +func WithinRangef(t TestingT, actual time.Time, start time.Time, end time.Time, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return WithinRange(t, actual, start, end, append([]interface{}{msg}, args...)...) +} + // YAMLEqf asserts that two YAML strings are equivalent. func YAMLEqf(t TestingT, expected string, actual string, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { diff --git a/server/vendor/github.com/stretchr/testify/assert/assertion_forward.go b/server/vendor/github.com/stretchr/testify/assert/assertion_forward.go index d9ea368d0..339515b8b 100644 --- a/server/vendor/github.com/stretchr/testify/assert/assertion_forward.go +++ b/server/vendor/github.com/stretchr/testify/assert/assertion_forward.go @@ -1461,6 +1461,26 @@ func (a *Assertions) WithinDurationf(expected time.Time, actual time.Time, delta return WithinDurationf(a.t, expected, actual, delta, msg, args...) } +// WithinRange asserts that a time is within a time range (inclusive). +// +// a.WithinRange(time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second)) +func (a *Assertions) WithinRange(actual time.Time, start time.Time, end time.Time, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return WithinRange(a.t, actual, start, end, msgAndArgs...) +} + +// WithinRangef asserts that a time is within a time range (inclusive). +// +// a.WithinRangef(time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second), "error message %s", "formatted") +func (a *Assertions) WithinRangef(actual time.Time, start time.Time, end time.Time, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return WithinRangef(a.t, actual, start, end, msg, args...) +} + // YAMLEq asserts that two YAML strings are equivalent. func (a *Assertions) YAMLEq(expected string, actual string, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { diff --git a/server/vendor/github.com/stretchr/testify/assert/assertions.go b/server/vendor/github.com/stretchr/testify/assert/assertions.go index 0357b2231..fa1245b18 100644 --- a/server/vendor/github.com/stretchr/testify/assert/assertions.go +++ b/server/vendor/github.com/stretchr/testify/assert/assertions.go @@ -8,6 +8,7 @@ import ( "fmt" "math" "os" + "path/filepath" "reflect" "regexp" "runtime" @@ -144,7 +145,8 @@ func CallerInfo() []string { if len(parts) > 1 { dir := parts[len(parts)-2] if (dir != "assert" && dir != "mock" && dir != "require") || file == "mock_test.go" { - callers = append(callers, fmt.Sprintf("%s:%d", file, line)) + path, _ := filepath.Abs(file) + callers = append(callers, fmt.Sprintf("%s:%d", path, line)) } } @@ -563,16 +565,17 @@ func isEmpty(object interface{}) bool { switch objValue.Kind() { // collection types are empty when they have no element - case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice: + case reflect.Chan, reflect.Map, reflect.Slice: return objValue.Len() == 0 - // pointers are empty if nil or if the value they point to is empty + // pointers are empty if nil or if the value they point to is empty case reflect.Ptr: if objValue.IsNil() { return true } deref := objValue.Elem().Interface() return isEmpty(deref) - // for all other types, compare against the zero value + // for all other types, compare against the zero value + // array types are empty when they match their zero-initialized state default: zero := reflect.Zero(objValue.Type()) return reflect.DeepEqual(object, zero.Interface()) @@ -815,7 +818,6 @@ func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok return true // we consider nil to be equal to the nil set } - subsetValue := reflect.ValueOf(subset) defer func() { if e := recover(); e != nil { ok = false @@ -825,14 +827,32 @@ func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok listKind := reflect.TypeOf(list).Kind() subsetKind := reflect.TypeOf(subset).Kind() - if listKind != reflect.Array && listKind != reflect.Slice { + if listKind != reflect.Array && listKind != reflect.Slice && listKind != reflect.Map { return Fail(t, fmt.Sprintf("%q has an unsupported type %s", list, listKind), msgAndArgs...) } - if subsetKind != reflect.Array && subsetKind != reflect.Slice { + if subsetKind != reflect.Array && subsetKind != reflect.Slice && listKind != reflect.Map { return Fail(t, fmt.Sprintf("%q has an unsupported type %s", subset, subsetKind), msgAndArgs...) } + subsetValue := reflect.ValueOf(subset) + if subsetKind == reflect.Map && listKind == reflect.Map { + listValue := reflect.ValueOf(list) + subsetKeys := subsetValue.MapKeys() + + for i := 0; i < len(subsetKeys); i++ { + subsetKey := subsetKeys[i] + subsetElement := subsetValue.MapIndex(subsetKey).Interface() + listElement := listValue.MapIndex(subsetKey).Interface() + + if !ObjectsAreEqual(subsetElement, listElement) { + return Fail(t, fmt.Sprintf("\"%s\" does not contain \"%s\"", list, subsetElement), msgAndArgs...) + } + } + + return true + } + for i := 0; i < subsetValue.Len(); i++ { element := subsetValue.Index(i).Interface() ok, found := containsElement(list, element) @@ -859,7 +879,6 @@ func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) return Fail(t, "nil is the empty set which is a subset of every set", msgAndArgs...) } - subsetValue := reflect.ValueOf(subset) defer func() { if e := recover(); e != nil { ok = false @@ -869,14 +888,32 @@ func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) listKind := reflect.TypeOf(list).Kind() subsetKind := reflect.TypeOf(subset).Kind() - if listKind != reflect.Array && listKind != reflect.Slice { + if listKind != reflect.Array && listKind != reflect.Slice && listKind != reflect.Map { return Fail(t, fmt.Sprintf("%q has an unsupported type %s", list, listKind), msgAndArgs...) } - if subsetKind != reflect.Array && subsetKind != reflect.Slice { + if subsetKind != reflect.Array && subsetKind != reflect.Slice && listKind != reflect.Map { return Fail(t, fmt.Sprintf("%q has an unsupported type %s", subset, subsetKind), msgAndArgs...) } + subsetValue := reflect.ValueOf(subset) + if subsetKind == reflect.Map && listKind == reflect.Map { + listValue := reflect.ValueOf(list) + subsetKeys := subsetValue.MapKeys() + + for i := 0; i < len(subsetKeys); i++ { + subsetKey := subsetKeys[i] + subsetElement := subsetValue.MapIndex(subsetKey).Interface() + listElement := listValue.MapIndex(subsetKey).Interface() + + if !ObjectsAreEqual(subsetElement, listElement) { + return true + } + } + + return Fail(t, fmt.Sprintf("%q is a subset of %q", subset, list), msgAndArgs...) + } + for i := 0; i < subsetValue.Len(); i++ { element := subsetValue.Index(i).Interface() ok, found := containsElement(list, element) @@ -1109,6 +1146,27 @@ func WithinDuration(t TestingT, expected, actual time.Time, delta time.Duration, return true } +// WithinRange asserts that a time is within a time range (inclusive). +// +// assert.WithinRange(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second)) +func WithinRange(t TestingT, actual, start, end time.Time, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + + if end.Before(start) { + return Fail(t, "Start should be before end", msgAndArgs...) + } + + if actual.Before(start) { + return Fail(t, fmt.Sprintf("Time %v expected to be in time range %v to %v, but is before the range", actual, start, end), msgAndArgs...) + } else if actual.After(end) { + return Fail(t, fmt.Sprintf("Time %v expected to be in time range %v to %v, but is after the range", actual, start, end), msgAndArgs...) + } + + return true +} + func toFloat(x interface{}) (float64, bool) { var xf float64 xok := true diff --git a/server/vendor/github.com/stretchr/testify/require/require.go b/server/vendor/github.com/stretchr/testify/require/require.go index 59c48277a..880853f5a 100644 --- a/server/vendor/github.com/stretchr/testify/require/require.go +++ b/server/vendor/github.com/stretchr/testify/require/require.go @@ -1864,6 +1864,32 @@ func WithinDurationf(t TestingT, expected time.Time, actual time.Time, delta tim t.FailNow() } +// WithinRange asserts that a time is within a time range (inclusive). +// +// assert.WithinRange(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second)) +func WithinRange(t TestingT, actual time.Time, start time.Time, end time.Time, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.WithinRange(t, actual, start, end, msgAndArgs...) { + return + } + t.FailNow() +} + +// WithinRangef asserts that a time is within a time range (inclusive). +// +// assert.WithinRangef(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second), "error message %s", "formatted") +func WithinRangef(t TestingT, actual time.Time, start time.Time, end time.Time, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.WithinRangef(t, actual, start, end, msg, args...) { + return + } + t.FailNow() +} + // YAMLEq asserts that two YAML strings are equivalent. func YAMLEq(t TestingT, expected string, actual string, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { diff --git a/server/vendor/github.com/stretchr/testify/require/require_forward.go b/server/vendor/github.com/stretchr/testify/require/require_forward.go index 5bb07c89c..960bf6f2c 100644 --- a/server/vendor/github.com/stretchr/testify/require/require_forward.go +++ b/server/vendor/github.com/stretchr/testify/require/require_forward.go @@ -1462,6 +1462,26 @@ func (a *Assertions) WithinDurationf(expected time.Time, actual time.Time, delta WithinDurationf(a.t, expected, actual, delta, msg, args...) } +// WithinRange asserts that a time is within a time range (inclusive). +// +// a.WithinRange(time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second)) +func (a *Assertions) WithinRange(actual time.Time, start time.Time, end time.Time, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + WithinRange(a.t, actual, start, end, msgAndArgs...) +} + +// WithinRangef asserts that a time is within a time range (inclusive). +// +// a.WithinRangef(time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second), "error message %s", "formatted") +func (a *Assertions) WithinRangef(actual time.Time, start time.Time, end time.Time, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + WithinRangef(a.t, actual, start, end, msg, args...) +} + // YAMLEq asserts that two YAML strings are equivalent. func (a *Assertions) YAMLEq(expected string, actual string, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { diff --git a/server/vendor/gopkg.in/yaml.v3/decode.go b/server/vendor/gopkg.in/yaml.v3/decode.go index df36e3a30..0173b6982 100644 --- a/server/vendor/gopkg.in/yaml.v3/decode.go +++ b/server/vendor/gopkg.in/yaml.v3/decode.go @@ -100,7 +100,10 @@ func (p *parser) peek() yaml_event_type_t { if p.event.typ != yaml_NO_EVENT { return p.event.typ } - if !yaml_parser_parse(&p.parser, &p.event) { + // It's curious choice from the underlying API to generally return a + // positive result on success, but on this case return true in an error + // scenario. This was the source of bugs in the past (issue #666). + if !yaml_parser_parse(&p.parser, &p.event) || p.parser.error != yaml_NO_ERROR { p.fail() } return p.event.typ @@ -320,6 +323,8 @@ type decoder struct { decodeCount int aliasCount int aliasDepth int + + mergedFields map[interface{}]bool } var ( @@ -808,6 +813,11 @@ func (d *decoder) mapping(n *Node, out reflect.Value) (good bool) { } } + mergedFields := d.mergedFields + d.mergedFields = nil + + var mergeNode *Node + mapIsNew := false if out.IsNil() { out.Set(reflect.MakeMap(outt)) @@ -815,11 +825,18 @@ func (d *decoder) mapping(n *Node, out reflect.Value) (good bool) { } for i := 0; i < l; i += 2 { if isMerge(n.Content[i]) { - d.merge(n.Content[i+1], out) + mergeNode = n.Content[i+1] continue } k := reflect.New(kt).Elem() if d.unmarshal(n.Content[i], k) { + if mergedFields != nil { + ki := k.Interface() + if mergedFields[ki] { + continue + } + mergedFields[ki] = true + } kkind := k.Kind() if kkind == reflect.Interface { kkind = k.Elem().Kind() @@ -833,6 +850,12 @@ func (d *decoder) mapping(n *Node, out reflect.Value) (good bool) { } } } + + d.mergedFields = mergedFields + if mergeNode != nil { + d.merge(n, mergeNode, out) + } + d.stringMapType = stringMapType d.generalMapType = generalMapType return true @@ -844,7 +867,8 @@ func isStringMap(n *Node) bool { } l := len(n.Content) for i := 0; i < l; i += 2 { - if n.Content[i].ShortTag() != strTag { + shortTag := n.Content[i].ShortTag() + if shortTag != strTag && shortTag != mergeTag { return false } } @@ -861,7 +885,6 @@ func (d *decoder) mappingStruct(n *Node, out reflect.Value) (good bool) { var elemType reflect.Type if sinfo.InlineMap != -1 { inlineMap = out.Field(sinfo.InlineMap) - inlineMap.Set(reflect.New(inlineMap.Type()).Elem()) elemType = inlineMap.Type().Elem() } @@ -870,6 +893,9 @@ func (d *decoder) mappingStruct(n *Node, out reflect.Value) (good bool) { d.prepare(n, field) } + mergedFields := d.mergedFields + d.mergedFields = nil + var mergeNode *Node var doneFields []bool if d.uniqueKeys { doneFields = make([]bool, len(sinfo.FieldsList)) @@ -879,13 +905,20 @@ func (d *decoder) mappingStruct(n *Node, out reflect.Value) (good bool) { for i := 0; i < l; i += 2 { ni := n.Content[i] if isMerge(ni) { - d.merge(n.Content[i+1], out) + mergeNode = n.Content[i+1] continue } if !d.unmarshal(ni, name) { continue } - if info, ok := sinfo.FieldsMap[name.String()]; ok { + sname := name.String() + if mergedFields != nil { + if mergedFields[sname] { + continue + } + mergedFields[sname] = true + } + if info, ok := sinfo.FieldsMap[sname]; ok { if d.uniqueKeys { if doneFields[info.Id] { d.terrors = append(d.terrors, fmt.Sprintf("line %d: field %s already set in type %s", ni.Line, name.String(), out.Type())) @@ -911,6 +944,11 @@ func (d *decoder) mappingStruct(n *Node, out reflect.Value) (good bool) { d.terrors = append(d.terrors, fmt.Sprintf("line %d: field %s not found in type %s", ni.Line, name.String(), out.Type())) } } + + d.mergedFields = mergedFields + if mergeNode != nil { + d.merge(n, mergeNode, out) + } return true } @@ -918,19 +956,29 @@ func failWantMap() { failf("map merge requires map or sequence of maps as the value") } -func (d *decoder) merge(n *Node, out reflect.Value) { - switch n.Kind { +func (d *decoder) merge(parent *Node, merge *Node, out reflect.Value) { + mergedFields := d.mergedFields + if mergedFields == nil { + d.mergedFields = make(map[interface{}]bool) + for i := 0; i < len(parent.Content); i += 2 { + k := reflect.New(ifaceType).Elem() + if d.unmarshal(parent.Content[i], k) { + d.mergedFields[k.Interface()] = true + } + } + } + + switch merge.Kind { case MappingNode: - d.unmarshal(n, out) + d.unmarshal(merge, out) case AliasNode: - if n.Alias != nil && n.Alias.Kind != MappingNode { + if merge.Alias != nil && merge.Alias.Kind != MappingNode { failWantMap() } - d.unmarshal(n, out) + d.unmarshal(merge, out) case SequenceNode: - // Step backwards as earlier nodes take precedence. - for i := len(n.Content) - 1; i >= 0; i-- { - ni := n.Content[i] + for i := 0; i < len(merge.Content); i++ { + ni := merge.Content[i] if ni.Kind == AliasNode { if ni.Alias != nil && ni.Alias.Kind != MappingNode { failWantMap() @@ -943,6 +991,8 @@ func (d *decoder) merge(n *Node, out reflect.Value) { default: failWantMap() } + + d.mergedFields = mergedFields } func isMerge(n *Node) bool { diff --git a/server/vendor/gopkg.in/yaml.v3/parserc.go b/server/vendor/gopkg.in/yaml.v3/parserc.go index ac66fccc0..268558a0d 100644 --- a/server/vendor/gopkg.in/yaml.v3/parserc.go +++ b/server/vendor/gopkg.in/yaml.v3/parserc.go @@ -687,6 +687,9 @@ func yaml_parser_parse_node(parser *yaml_parser_t, event *yaml_event_t, block, i func yaml_parser_parse_block_sequence_entry(parser *yaml_parser_t, event *yaml_event_t, first bool) bool { if first { token := peek_token(parser) + if token == nil { + return false + } parser.marks = append(parser.marks, token.start_mark) skip_token(parser) } @@ -786,7 +789,7 @@ func yaml_parser_split_stem_comment(parser *yaml_parser_t, stem_len int) { } token := peek_token(parser) - if token.typ != yaml_BLOCK_SEQUENCE_START_TOKEN && token.typ != yaml_BLOCK_MAPPING_START_TOKEN { + if token == nil || token.typ != yaml_BLOCK_SEQUENCE_START_TOKEN && token.typ != yaml_BLOCK_MAPPING_START_TOKEN { return } @@ -813,6 +816,9 @@ func yaml_parser_split_stem_comment(parser *yaml_parser_t, stem_len int) { func yaml_parser_parse_block_mapping_key(parser *yaml_parser_t, event *yaml_event_t, first bool) bool { if first { token := peek_token(parser) + if token == nil { + return false + } parser.marks = append(parser.marks, token.start_mark) skip_token(parser) } @@ -922,6 +928,9 @@ func yaml_parser_parse_block_mapping_value(parser *yaml_parser_t, event *yaml_ev func yaml_parser_parse_flow_sequence_entry(parser *yaml_parser_t, event *yaml_event_t, first bool) bool { if first { token := peek_token(parser) + if token == nil { + return false + } parser.marks = append(parser.marks, token.start_mark) skip_token(parser) } diff --git a/server/vendor/modules.txt b/server/vendor/modules.txt index 59a0045fe..89d8423ac 100644 --- a/server/vendor/modules.txt +++ b/server/vendor/modules.txt @@ -49,8 +49,8 @@ github.com/cespare/xxhash/v2 # github.com/crewjam/httperr v0.2.0 ## explicit; go 1.13 github.com/crewjam/httperr -# github.com/crewjam/saml v0.4.6 -## explicit; go 1.13 +# github.com/crewjam/saml v0.4.13 +## explicit; go 1.16 github.com/crewjam/saml github.com/crewjam/saml/logger github.com/crewjam/saml/samlsp @@ -199,7 +199,7 @@ github.com/goccy/go-json/internal/runtime # github.com/golang-jwt/jwt v3.2.2+incompatible ## explicit github.com/golang-jwt/jwt -# github.com/golang-jwt/jwt/v4 v4.4.1 +# github.com/golang-jwt/jwt/v4 v4.4.3 ## explicit; go 1.16 github.com/golang-jwt/jwt/v4 # github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe @@ -407,7 +407,7 @@ github.com/prometheus/common/model github.com/prometheus/procfs github.com/prometheus/procfs/internal/fs github.com/prometheus/procfs/internal/util -# github.com/russellhaering/goxmldsig v1.1.1 +# github.com/russellhaering/goxmldsig v1.2.0 ## explicit; go 1.15 github.com/russellhaering/goxmldsig github.com/russellhaering/goxmldsig/etreeutils @@ -444,9 +444,7 @@ github.com/steinfletcher/apitest/difflib github.com/steinfletcher/apitest-jsonpath github.com/steinfletcher/apitest-jsonpath/http github.com/steinfletcher/apitest-jsonpath/jsonpath -# github.com/stretchr/objx v0.2.0 -## explicit; go 1.12 -# github.com/stretchr/testify v1.7.1 +# github.com/stretchr/testify v1.8.1 ## explicit; go 1.13 github.com/stretchr/testify/assert github.com/stretchr/testify/require @@ -663,7 +661,7 @@ gopkg.in/mail.v2 gopkg.in/square/go-jose.v2 gopkg.in/square/go-jose.v2/cipher gopkg.in/square/go-jose.v2/json -# gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b +# gopkg.in/yaml.v3 v3.0.1 ## explicit gopkg.in/yaml.v3 # moul.io/zapfilter v1.7.0