From 348c54f2e78623ce967fb4f5b411180b4cb270a0 Mon Sep 17 00:00:00 2001 From: "A.Unger" Date: Tue, 1 Dec 2020 16:57:36 +0100 Subject: [PATCH] write www-authenticate and delegate to reva --- ocis/go.mod | 2 + ocis/go.sum | 2 + proxy/pkg/command/server.go | 2 +- proxy/pkg/middleware/authentication.go | 53 ++++++++++++++++++-------- proxy/pkg/middleware/basic_auth.go | 11 ++++-- proxy/pkg/middleware/oidc_auth.go | 15 ++++++-- storage/pkg/command/frontend.go | 5 +++ 7 files changed, 67 insertions(+), 23 deletions(-) diff --git a/ocis/go.mod b/ocis/go.mod index 1f8ad8dcc..fca8aa036 100644 --- a/ocis/go.mod +++ b/ocis/go.mod @@ -7,6 +7,7 @@ require ( contrib.go.opencensus.io/exporter/ocagent v0.7.0 contrib.go.opencensus.io/exporter/zipkin v0.1.2 github.com/UnnoTed/fileb0x v1.1.4 + github.com/cs3org/reva v1.4.1-0.20201201074212-8b4cc174b708 // indirect github.com/go-test/deep v1.0.6 // indirect github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00 // indirect github.com/micro/cli/v2 v2.1.2 @@ -37,6 +38,7 @@ require ( ) replace ( + github.com/cs3org/reva => ../../../reva github.com/gomodule/redigo => github.com/gomodule/redigo v1.8.2 github.com/owncloud/ocis/accounts => ../accounts github.com/owncloud/ocis/glauth => ../glauth diff --git a/ocis/go.sum b/ocis/go.sum index 8b5747747..02495531b 100644 --- a/ocis/go.sum +++ b/ocis/go.sum @@ -311,6 +311,8 @@ github.com/cs3org/reva v1.4.1-0.20201127111856-e6a6212c1b7b h1:Bypxdf3vXwyEeL86M github.com/cs3org/reva v1.4.1-0.20201127111856-e6a6212c1b7b/go.mod h1:MTBlfobTE8W2hgXQ9+r+75jpJa1TxD04IZm5TpS9H48= github.com/cs3org/reva v1.4.1-0.20201130061320-ac85e68e0600 h1:4CKU+R4UNvILzsPrcAFwEbk/8Hc6vJqwO7SxK0gAm4I= github.com/cs3org/reva v1.4.1-0.20201130061320-ac85e68e0600/go.mod h1:MTBlfobTE8W2hgXQ9+r+75jpJa1TxD04IZm5TpS9H48= +github.com/cs3org/reva v1.4.1-0.20201201074212-8b4cc174b708 h1:uiz1kb5iR6V7GvpX5CW3xeZcbPTvR8P1tlvODTD0zio= +github.com/cs3org/reva v1.4.1-0.20201201074212-8b4cc174b708/go.mod h1:MTBlfobTE8W2hgXQ9+r+75jpJa1TxD04IZm5TpS9H48= github.com/cznic/b v0.0.0-20181122101859-a26611c4d92d h1:SwD98825d6bdB+pEuTxWOXiSjBrHdOl/UVp75eI7JT8= github.com/cznic/b v0.0.0-20181122101859-a26611c4d92d/go.mod h1:URriBxXwVq5ijiJ12C7iIZqlA69nTlI+LgI6/pwftG8= github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548 h1:iwZdTE0PVqJCos1vaoKsclOGD3ADKpshg3SRtYBbwso= diff --git a/proxy/pkg/command/server.go b/proxy/pkg/command/server.go index 2f5cb8619..1d4636679 100644 --- a/proxy/pkg/command/server.go +++ b/proxy/pkg/command/server.go @@ -285,7 +285,7 @@ func loadMiddlewares(ctx context.Context, l log.Logger, cfg *config.Config) alic // basic Options middleware.Logger(l), - middleware.EnableBasicAuth(true), + middleware.EnableBasicAuth(cfg.EnableBasicAuth), middleware.AccountsClient(accountsClient), middleware.OIDCIss(cfg.OIDC.Issuer), ), diff --git a/proxy/pkg/middleware/authentication.go b/proxy/pkg/middleware/authentication.go index 00d0efcbe..ebc053510 100644 --- a/proxy/pkg/middleware/authentication.go +++ b/proxy/pkg/middleware/authentication.go @@ -3,12 +3,32 @@ package middleware import ( "fmt" "net/http" + "strings" "time" ) -// Authentication is a higher level authentication middleware. +var SupportedAuthStrategies []string + +type statusRecorder struct { + http.ResponseWriter + status int +} + +func (rec *statusRecorder) WriteHeader(code int) { + rec.status = code + rec.ResponseWriter.WriteHeader(code) +} + +// Authentication is a higher order authentication middleware. func Authentication(opts ...Option) func(next http.Handler) http.Handler { options := newOptions(opts...) + if options.OIDCIss != "" { + SupportedAuthStrategies = append(SupportedAuthStrategies, "bearer") + } + + if options.EnableBasicAuth { + SupportedAuthStrategies = append(SupportedAuthStrategies, "basic") + } oidc := OIDCAuth( Logger(options.Logger), @@ -28,21 +48,24 @@ func Authentication(opts ...Option) func(next http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - // here we multiplex depending on the use agent - userAgent := r.Header.Get("User-Agent") - fmt.Printf("\n\nUser-Agent:\t%s\n\n", userAgent) - switch userAgent { - case "a": - oidc(next).ServeHTTP(w, r) - return - case "b": - basic(next).ServeHTTP(w, r) - return - default: - oidc(next).ServeHTTP(w, r) - basic(next).ServeHTTP(w, r) - return + if options.OIDCIss != "" && options.EnableBasicAuth { + oidc(basic(next)).ServeHTTP(w, r) } + + if options.OIDCIss != "" && !options.EnableBasicAuth { + oidc(next).ServeHTTP(w, r) + } + + if options.OIDCIss == "" && options.EnableBasicAuth { + basic(next).ServeHTTP(w, r) + } + }) } } + +func writeSupportedAuthenticateHeader(w http.ResponseWriter, r *http.Request) { + for i := 0; i < len(SupportedAuthStrategies); i++ { + w.Header().Add("WWW-Authenticate", fmt.Sprintf("%v realm=\"%s\", charset=\"UTF-8\"", strings.Title(SupportedAuthStrategies[i]), r.Host)) + } +} diff --git a/proxy/pkg/middleware/basic_auth.go b/proxy/pkg/middleware/basic_auth.go index f766dcd5d..48055f557 100644 --- a/proxy/pkg/middleware/basic_auth.go +++ b/proxy/pkg/middleware/basic_auth.go @@ -32,15 +32,18 @@ func BasicAuth(optionSetters ...Option) func(next http.Handler) http.Handler { return http.HandlerFunc( func(w http.ResponseWriter, req *http.Request) { if h.isPublicLink(req) || !h.isBasicAuth(req) { + // if we want to prevent duplicated Www-Authenticate headers coming from Reva consider using w.Header().Del("Www-Authenticate") + // but this will require the proxy being aware of endpoints which authentication fallback to Reva. + if !h.isPublicLink(req) { + w.Header().Add("Www-Authenticate", fmt.Sprintf("%v realm=\"%s\", charset=\"UTF-8\"", "Basic", req.Host)) + } next.ServeHTTP(w, req) return } account, ok := h.getAccount(req) - if !ok { - // TODO need correct hostname - w.Header().Add("WWW-Authenticate", "Basic realm=\"Access to localhost\", charset=\"UTF-8\"") + w.Header().Add("Www-Authenticate", fmt.Sprintf("%v realm=\"%s\", charset=\"UTF-8\"", "Basic", req.Host)) w.WriteHeader(http.StatusUnauthorized) return } @@ -50,6 +53,8 @@ func BasicAuth(optionSetters ...Option) func(next http.Handler) http.Handler { Iss: oidcIss, } + fmt.Printf("\n\nHGAHAHAHA\n\n") + next.ServeHTTP(w, req.WithContext(oidc.NewContext(req.Context(), claims))) }, ) diff --git a/proxy/pkg/middleware/oidc_auth.go b/proxy/pkg/middleware/oidc_auth.go index 1de7ecc50..112a86f58 100644 --- a/proxy/pkg/middleware/oidc_auth.go +++ b/proxy/pkg/middleware/oidc_auth.go @@ -3,6 +3,7 @@ package middleware import ( "context" "encoding/json" + "fmt" "net/http" "strings" "time" @@ -37,11 +38,17 @@ func OIDCAuth(optionSetters ...Option) func(next http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + // there is no bearer token on the request, if !h.shouldServe(req) { - // TODO need correct hostname - w.Header().Add("WWW-Authenticate", "Bearer realm=\"Access to localhost\", charset=\"UTF-8\"") - //w.WriteHeader(http.StatusUnauthorized) - //next.ServeHTTP(w, req) + // oidc supported but token not present, add header and handover to the next middleware. + + // TODO for this logic to work and we don't return superfluous Www-Authenticate headers we would need to + // add Www-Authenticate only on selected endpoints, because Reva won't cleanup already written headers. + // this means that requests such as: + // curl -v -k -u admin:admin -H "depth: 0" -X PROPFIND https://localhost:9200/remote.php/dav/files | xmllint --format - + // even when succeeding, will contain a Www-Authenticate header. + w.Header().Add("Www-Authenticate", fmt.Sprintf("%v realm=\"%s\", charset=\"UTF-8\"", "Bearer", req.Host)) + next.ServeHTTP(w, req) return } diff --git a/storage/pkg/command/frontend.go b/storage/pkg/command/frontend.go index 7bb726597..9dd742a0c 100644 --- a/storage/pkg/command/frontend.go +++ b/storage/pkg/command/frontend.go @@ -115,6 +115,11 @@ func Frontend(cfg *config.Config) *cli.Command { "cors": map[string]interface{}{ "allow_credentials": true, }, + "auth": map[string]interface{}{ + "credentials_by_user_agent": map[string]string{ + "mirall": "basic", + }, + }, }, // TODO build services dynamically "services": map[string]interface{}{