diff --git a/config/proxy-example.json b/config/proxy-example.json index e8de52cd85..21e0a8c084 100644 --- a/config/proxy-example.json +++ b/config/proxy-example.json @@ -2,6 +2,12 @@ "HTTP": { "Namespace": "com.owncloud" }, + "oidc": { + "endpoint": "https://localhost:9200", + "realm": "", + "signing_algs": ["RS256", "PS256"], + "insecure": true + }, "policy_selector": { "static": {"policy" : "reva"} }, diff --git a/go.sum b/go.sum index 5c251d9f33..9e5ddac773 100644 --- a/go.sum +++ b/go.sum @@ -243,6 +243,7 @@ github.com/go-acme/lego/v3 v3.1.0/go.mod h1:074uqt+JS6plx+c9Xaiz6+L+GBb+7itGtzfc github.com/go-acme/lego/v3 v3.3.0/go.mod h1:iGSY2vQrvQs3WezicSB/oVbO2eCrD88dpWPwb1qLqu0= github.com/go-chi/chi v4.0.2+incompatible h1:maB6vn6FqCxrpz4FqWdh4+lwpyZIQS7YEAUcHlgXVRs= github.com/go-chi/chi v4.0.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= +github.com/go-chi/chi v4.1.0+incompatible h1:ETj3cggsVIY2Xao5ExCu6YhEh5MD6JTfcBzS37R260w= github.com/go-chi/chi v4.1.0+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= github.com/go-chi/render v1.0.1/go.mod h1:pq4Rr7HbnsdaeHagklXub+p6Wd16Af5l9koip1OvJns= github.com/go-cmd/cmd v1.0.5/go.mod h1:y8q8qlK5wQibcw63djSl/ntiHUHXHGdCkPk0j4QeW4s= @@ -308,6 +309,7 @@ github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0 h1:oOuy+ugB+P/kBdUnG5QaMXSIyJ1q38wWSojYCb3z5VQ= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= @@ -1073,6 +1075,7 @@ google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLY google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0 h1:qdOKuR/EIArgaWNjetjgTzgVTAZ+S/WXVrq9HW9zimw= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= gopkg.in/DataDog/dd-trace-go.v1 v1.19.0/go.mod h1:DVp8HmDh8PuTu2Z0fVVlBsyWaC++fzwVCaGWylTe3tg= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= diff --git a/pkg/command/server.go b/pkg/command/server.go index 64888a2f79..085c301900 100644 --- a/pkg/command/server.go +++ b/pkg/command/server.go @@ -7,6 +7,7 @@ import ( "strings" "time" + "github.com/justinas/alice" "github.com/owncloud/ocis-pkg/v2/log" "github.com/owncloud/ocis-pkg/v2/oidc" "github.com/owncloud/ocis-proxy/pkg/middleware" @@ -154,7 +155,7 @@ func Server(cfg *config.Config) *cli.Command { proxyHTTP.Metrics(metrics), proxyHTTP.Flags(flagset.RootWithConfig(config.New())), proxyHTTP.Flags(flagset.ServerWithConfig(config.New())), - proxyHTTP.Middlewares(loadMiddlewares(cfg, logger)...), + proxyHTTP.Middlewares(loadMiddlewares(cfg, logger)), ) if err != nil { @@ -234,11 +235,7 @@ func Server(cfg *config.Config) *cli.Command { } } -func loadMiddlewares(cfg *config.Config, l log.Logger) []middleware.M { - var configuredMiddlewares = make([]middleware.M, 0) - - configuredMiddlewares = append(configuredMiddlewares, middleware.RedirectToHTTPS) - +func loadMiddlewares(cfg *config.Config, l log.Logger) alice.Chain { if cfg.OIDC != nil { l.Info().Msg("Loading OIDC-Middleware") l.Debug().Interface("oidc_config", cfg.OIDC).Msg("OIDC-Config") @@ -250,8 +247,9 @@ func loadMiddlewares(cfg *config.Config, l log.Logger) []middleware.M { oidc.Logger(l), ) - configuredMiddlewares = append(configuredMiddlewares, oidcMW) + // configuredMiddlewares = append(configuredMiddlewares, oidcMW, middleware.AccountUUID) + return alice.New(middleware.RedirectToHTTPS, oidcMW, middleware.AccountUUID) } - return configuredMiddlewares + return alice.New(middleware.RedirectToHTTPS) } diff --git a/pkg/middleware/account_uuid.go b/pkg/middleware/account_uuid.go new file mode 100644 index 0000000000..5343572323 --- /dev/null +++ b/pkg/middleware/account_uuid.go @@ -0,0 +1,57 @@ +package middleware + +import ( + "context" + "net/http" + + mclient "github.com/micro/go-micro/v2/client" + acc "github.com/owncloud/ocis-accounts/pkg/proto/v0" + ocisoidc "github.com/owncloud/ocis-pkg/v2/oidc" +) + +// AccountUUID fetches the ocis account uuid from the oidc standard claims +func AccountUUID(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + claims := r.Context().Value(ClaimsKey) + if claims == nil { + next.ServeHTTP(w, r) + return + } + + entry, err := svcCache.Get(AccountsKey, claims.(ocisoidc.StandardClaims).Email) + if err != nil { + c := acc.NewSettingsService("com.owncloud.accounts", mclient.DefaultClient) // TODO this won't work with a registry other than mdns. Look into Micro's client initialization. + resp, err := c.Get(context.Background(), &acc.Query{ + Key: "200~a54bf154-e6a5-4e96-851b-a56c9f6c1fce", + // Email: claims.Email // depends on https://github.com/owncloud/ocis-accounts/pull/28 + }) + if err != nil { + // placeholder. Add more meaningful response + w.WriteHeader(http.StatusInternalServerError) + return + } + + err = svcCache.Set(AccountsKey, claims.(ocisoidc.StandardClaims).Email, resp.Payload.Account.Uuid) + if err != nil { + // placeholder. Add more meaningful response + w.WriteHeader(http.StatusInternalServerError) + return + } + + // TODO: build JWT and set it, instead of the uuid on that header. + w.Header().Set("x-ocis-accounts-uuid", resp.Payload.Account.Uuid) + } + + uuid, ok := entry.V.(string) + if !ok { + // placeholder. Add more meaningful response + w.WriteHeader(http.StatusInternalServerError) + return + } + + // TODO: build JWT and set it, instead of the uuid on that header. + w.Header().Set("x-ocis-accounts-uuid", uuid) + + next.ServeHTTP(w, r) + }) +} diff --git a/pkg/middleware/openidconnect.go b/pkg/middleware/openidconnect.go index d53576ce67..890bd189fc 100644 --- a/pkg/middleware/openidconnect.go +++ b/pkg/middleware/openidconnect.go @@ -28,6 +28,9 @@ var ( // UUIDKey works as a context key UUIDKey interface{} = "uuid" + + // ClaimsKey works as a context key for user claims + ClaimsKey interface{} = "claims" ) // newOIDCOptions initializes the available default options. @@ -42,7 +45,7 @@ func newOIDCOptions(opts ...ocisoidc.Option) ocisoidc.Options { } // OpenIDConnect provides a middleware to check access secured by a static token. -func OpenIDConnect(opts ...ocisoidc.Option) M { +func OpenIDConnect(opts ...ocisoidc.Option) func(next http.Handler) http.Handler { opt := newOIDCOptions(opts...) // set defaults @@ -60,14 +63,6 @@ func OpenIDConnect(opts ...ocisoidc.Option) M { header := r.Header.Get("Authorization") path := r.URL.Path - uuid, err := uuidFromClaims(ocisoidc.StandardClaims{}) - if err != nil { - // stop the auth flow altogether? - } - - withUUID := context.WithValue(r.Context(), UUIDKey, uuid) - r = r.WithContext(withUUID) - // Ignore request to "/konnect/v1/userinfo" as this will cause endless loop when getting userinfo // needs a better idea on how to not hardcode this if header == "" || !strings.HasPrefix(header, "Bearer ") || path == "/konnect/v1/userinfo" { @@ -122,10 +117,13 @@ func OpenIDConnect(opts ...ocisoidc.Option) M { return } + // inject claims to the request context for the account_uuid middleware. + ctxWithClaims := context.WithValue(r.Context(), ClaimsKey, claims) + r = r.WithContext(ctxWithClaims) + // add UUID to the request context for the handler to deal with // void call for correct staticchecks. _, err = uuidFromClaims(claims) - if err != nil { opt.Logger.Error().Err(err).Interface("account uuid", userInfo).Msg("failed to unmarshal userinfo claims") w.WriteHeader(http.StatusInternalServerError) @@ -164,7 +162,7 @@ func uuidFromClaims(claims ocisoidc.StandardClaims) (string, error) { if err != nil { c := acc.NewSettingsService("com.owncloud.accounts", mclient.DefaultClient) // TODO this won't work with a registry other than mdns. Look into Micro's client initialization. resp, err := c.Get(context.Background(), &acc.Query{ - Key: "200~a54bf154-e6a5-4e96-851b-a56c9f6c1fce", // use hardcoded key... + Key: "200~a54bf154-e6a5-4e96-851b-a56c9f6c1fce", // Email: claims.Email // depends on https://github.com/owncloud/ocis-accounts/pull/28 }) if err != nil { diff --git a/pkg/server/http/option.go b/pkg/server/http/option.go index fb5464e9d9..0a93bab2e9 100644 --- a/pkg/server/http/option.go +++ b/pkg/server/http/option.go @@ -4,11 +4,11 @@ import ( "context" "net/http" + "github.com/justinas/alice" "github.com/micro/cli/v2" "github.com/owncloud/ocis-pkg/v2/log" "github.com/owncloud/ocis-proxy/pkg/config" "github.com/owncloud/ocis-proxy/pkg/metrics" - "github.com/owncloud/ocis-proxy/pkg/middleware" ) // Option defines a single option function. @@ -23,7 +23,7 @@ type Options struct { Metrics *metrics.Metrics Flags []cli.Flag Namespace string - Middlewares []middleware.M + Middlewares alice.Chain } // newOptions initializes the available default options. @@ -87,7 +87,7 @@ func Handler(h http.Handler) Option { } // Middlewares provides a function to register middlewares -func Middlewares(val ...middleware.M) Option { +func Middlewares(val alice.Chain) Option { return func(o *Options) { o.Middlewares = val } diff --git a/pkg/server/http/server.go b/pkg/server/http/server.go index 2a346edde6..8442bf09ca 100644 --- a/pkg/server/http/server.go +++ b/pkg/server/http/server.go @@ -2,12 +2,10 @@ package http import ( "crypto/tls" - "net/http" "os" svc "github.com/owncloud/ocis-pkg/v2/service/http" "github.com/owncloud/ocis-proxy/pkg/crypto" - "github.com/owncloud/ocis-proxy/pkg/middleware" "github.com/owncloud/ocis-proxy/pkg/version" ) @@ -40,6 +38,7 @@ func Server(opts ...Option) (svc.Service, error) { } tlsConfig := &tls.Config{Certificates: []tls.Certificate{cer}} + chain := options.Middlewares.Then(options.Handler) service := svc.NewService( svc.Name("web.proxy"), @@ -50,9 +49,7 @@ func Server(opts ...Option) (svc.Service, error) { svc.Address(options.Config.HTTP.Addr), svc.Context(options.Context), svc.Flags(options.Flags...), - svc.Handler( - applyMiddlewares(options.Handler, options.Middlewares...), - ), + svc.Handler(chain), ) if err := service.Init(); err != nil { @@ -61,12 +58,3 @@ func Server(opts ...Option) (svc.Service, error) { return service, nil } - -func applyMiddlewares(next http.Handler, mws ...middleware.M) http.Handler { - var h = next - for _, mw := range mws { - h = mw(h) - } - - return h -}