From 5713fe81a4e2758bef12fc164c1fdd9e21373af9 Mon Sep 17 00:00:00 2001 From: David Christofas Date: Thu, 23 Sep 2021 16:37:46 +0200 Subject: [PATCH 1/5] add public share auth middleware --- .../public-share-auth-middleware.md | 6 ++ proxy/pkg/command/server.go | 28 +++++---- proxy/pkg/middleware/public_share_auth.go | 58 +++++++++++++++++++ 3 files changed, 80 insertions(+), 12 deletions(-) create mode 100644 changelog/unreleased/public-share-auth-middleware.md create mode 100644 proxy/pkg/middleware/public_share_auth.go diff --git a/changelog/unreleased/public-share-auth-middleware.md b/changelog/unreleased/public-share-auth-middleware.md new file mode 100644 index 000000000..566bcb845 --- /dev/null +++ b/changelog/unreleased/public-share-auth-middleware.md @@ -0,0 +1,6 @@ +Enhancement: Add a middleware to authenticate public share requests + +Added a new middleware to authenticate public share requests. This makes it possible to use APIs which require an authenticated context with public shares. + +https://github.com/owncloud/ocis/pull/2536 +https://github.com/owncloud/ocis/issues/2479 diff --git a/proxy/pkg/command/server.go b/proxy/pkg/command/server.go index 01cdb6bbf..cd4a7ed14 100644 --- a/proxy/pkg/command/server.go +++ b/proxy/pkg/command/server.go @@ -147,7 +147,7 @@ func Server(cfg *config.Config) *cli.Command { } } -func loadMiddlewares(ctx context.Context, l log.Logger, cfg *config.Config) alice.Chain { +func loadMiddlewares(ctx context.Context, logger log.Logger, cfg *config.Config) alice.Chain { rolesClient := settings.NewRoleService("com.owncloud.api.settings", grpc.DefaultClient) revaClient, err := cs3.GetGatewayServiceClient(cfg.Reva.Address) var userProvider backend.UserBackend @@ -158,7 +158,7 @@ func loadMiddlewares(ctx context.Context, l log.Logger, cfg *config.Config) alic "expires": int64(24 * 60 * 60), }) if err != nil { - l.Error().Err(err). + logger.Error().Err(err). Msg("Failed to create token manager") } userProvider = backend.NewAccountsServiceUserBackend( @@ -166,17 +166,17 @@ func loadMiddlewares(ctx context.Context, l log.Logger, cfg *config.Config) alic rolesClient, cfg.OIDC.Issuer, tokenManager, - l, + logger, ) case "cs3": - userProvider = backend.NewCS3UserBackend(rolesClient, revaClient, cfg.MachineAuthAPIKey, l) + userProvider = backend.NewCS3UserBackend(rolesClient, revaClient, cfg.MachineAuthAPIKey, logger) default: - l.Fatal().Msgf("Invalid accounts backend type '%s'", cfg.AccountBackend) + logger.Fatal().Msgf("Invalid accounts backend type '%s'", cfg.AccountBackend) } storeClient := storepb.NewStoreService("com.owncloud.api.store", grpc.DefaultClient) if err != nil { - l.Error().Err(err). + logger.Error().Err(err). Str("gateway", cfg.Reva.Address). Msg("Failed to create reva gateway service client") } @@ -196,7 +196,7 @@ func loadMiddlewares(ctx context.Context, l log.Logger, cfg *config.Config) alic pkgmiddleware.TraceContext, chimiddleware.RealIP, chimiddleware.RequestID, - middleware.AccessLog(l), + middleware.AccessLog(logger), middleware.HTTPSRedirect, // now that we established the basics, on with authentication middleware @@ -216,20 +216,24 @@ func loadMiddlewares(ctx context.Context, l log.Logger, cfg *config.Config) alic middleware.TokenCacheTTL(time.Second*time.Duration(cfg.OIDC.UserinfoCache.TTL)), // basic Options - middleware.Logger(l), + middleware.Logger(logger), middleware.EnableBasicAuth(cfg.EnableBasicAuth), middleware.UserProvider(userProvider), middleware.OIDCIss(cfg.OIDC.Issuer), middleware.CredentialsByUserAgent(cfg.Reva.Middleware.Auth.CredentialsByUserAgent), ), + middleware.PublicShareAuth( + middleware.Logger(logger), + middleware.RevaGatewayClient(revaClient), + ), middleware.SignedURLAuth( - middleware.Logger(l), + middleware.Logger(logger), middleware.PreSignedURLConfig(cfg.PreSignedURL), middleware.UserProvider(userProvider), middleware.Store(storeClient), ), middleware.AccountResolver( - middleware.Logger(l), + middleware.Logger(logger), middleware.UserProvider(userProvider), middleware.TokenManagerConfig(cfg.TokenManager), middleware.UserOIDCClaim(cfg.UserOIDCClaim), @@ -238,14 +242,14 @@ func loadMiddlewares(ctx context.Context, l log.Logger, cfg *config.Config) alic ), middleware.SelectorCookie( - middleware.Logger(l), + middleware.Logger(logger), middleware.UserProvider(userProvider), middleware.PolicySelectorConfig(*cfg.PolicySelector), ), // finally, trigger home creation when a user logs in middleware.CreateHome( - middleware.Logger(l), + middleware.Logger(logger), middleware.TokenManagerConfig(cfg.TokenManager), middleware.RevaGatewayClient(revaClient), ), diff --git a/proxy/pkg/middleware/public_share_auth.go b/proxy/pkg/middleware/public_share_auth.go new file mode 100644 index 000000000..80c470eba --- /dev/null +++ b/proxy/pkg/middleware/public_share_auth.go @@ -0,0 +1,58 @@ +package middleware + +import ( + "net/http" + "strings" + + gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1" +) + +const ( + headerRevaAccessToken = "x-access-token" + headerShareToken = "public-token" + appProviderPathPrefix = "/app/open" + basicAuthPasswordPrefix = "basic|" + authenticationType = "publicshares" +) + +// PublicShareAuth ... +func PublicShareAuth(opts ...Option) func(next http.Handler) http.Handler { + options := newOptions(opts...) + logger := options.Logger + + return func(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + // Currently we only want to authenticate app open request coming from public shares. + shareToken := r.Header.Get(headerShareToken) + if shareToken == "" || !strings.HasPrefix(appProviderPathPrefix, r.URL.Path) { + // Don't authenticate + next.ServeHTTP(w, r) + return + } + + // We can ignore the username since it is always set to "public" in public shares. + _, password, ok := r.BasicAuth() + + sharePassword := basicAuthPasswordPrefix + if ok { + sharePassword += password + } + + authResp, err := options.RevaGatewayClient.Authenticate(r.Context(), &gateway.AuthenticateRequest{ + Type: authenticationType, + ClientId: shareToken, + ClientSecret: sharePassword, + }) + + if err != nil { + logger.Debug().Err(err).Str("public_share_token", shareToken).Msg("could not authenticate public share") + // try another middleware + next.ServeHTTP(w, r) + return + } + + r.Header.Add(headerRevaAccessToken, authResp.Token) + next.ServeHTTP(w, r) + }) + } +} From c88824e7a0a941a57b9eb69a3b1eea5a882938bd Mon Sep 17 00:00:00 2001 From: David Christofas Date: Thu, 23 Sep 2021 19:28:47 +0200 Subject: [PATCH 2/5] run the public share auth middleware after the create home middleware The x-access-token for public shares is scoped for only the share. That means create home would fail because the token is not valid. --- proxy/pkg/command/server.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/proxy/pkg/command/server.go b/proxy/pkg/command/server.go index cd4a7ed14..8b4341103 100644 --- a/proxy/pkg/command/server.go +++ b/proxy/pkg/command/server.go @@ -222,10 +222,6 @@ func loadMiddlewares(ctx context.Context, logger log.Logger, cfg *config.Config) middleware.OIDCIss(cfg.OIDC.Issuer), middleware.CredentialsByUserAgent(cfg.Reva.Middleware.Auth.CredentialsByUserAgent), ), - middleware.PublicShareAuth( - middleware.Logger(logger), - middleware.RevaGatewayClient(revaClient), - ), middleware.SignedURLAuth( middleware.Logger(logger), middleware.PreSignedURLConfig(cfg.PreSignedURL), @@ -253,6 +249,10 @@ func loadMiddlewares(ctx context.Context, logger log.Logger, cfg *config.Config) middleware.TokenManagerConfig(cfg.TokenManager), middleware.RevaGatewayClient(revaClient), ), + middleware.PublicShareAuth( + middleware.Logger(logger), + middleware.RevaGatewayClient(revaClient), + ), ) } From fb0380820ce0a2e34cd0ed33ef353aa182440e95 Mon Sep 17 00:00:00 2001 From: David Christofas Date: Fri, 1 Oct 2021 16:41:36 +0200 Subject: [PATCH 3/5] enable archiver for public shares --- graph/pkg/middleware/auth.go | 2 +- ocis-pkg/middleware/account.go | 2 +- proxy/pkg/middleware/public_share_auth.go | 4 +--- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/graph/pkg/middleware/auth.go b/graph/pkg/middleware/auth.go index dca9fafa9..ed4a2ecf6 100644 --- a/graph/pkg/middleware/auth.go +++ b/graph/pkg/middleware/auth.go @@ -61,7 +61,7 @@ func Auth(opts ...account.Option) func(http.Handler) http.Handler { errorcode.InvalidAuthenticationToken.Render(w, r, http.StatusUnauthorized, "invalid token") return } - if ok, err := scope.VerifyScope(tokenScope, r); err != nil || !ok { + if ok, err := scope.VerifyScope(ctx, tokenScope, r); err != nil || !ok { opt.Logger.Error().Err(err).Msg("verifying scope failed") errorcode.InvalidAuthenticationToken.Render(w, r, http.StatusUnauthorized, "verifying scope failed") return diff --git a/ocis-pkg/middleware/account.go b/ocis-pkg/middleware/account.go index 9d7c26d4e..562a9ce45 100644 --- a/ocis-pkg/middleware/account.go +++ b/ocis-pkg/middleware/account.go @@ -60,7 +60,7 @@ func ExtractAccountUUID(opts ...account.Option) func(http.Handler) http.Handler opt.Logger.Error().Err(err) return } - if ok, err := scope.VerifyScope(tokenScope, r); err != nil || !ok { + if ok, err := scope.VerifyScope(r.Context(), tokenScope, r); err != nil || !ok { opt.Logger.Error().Err(err).Msg("verifying scope failed") return } diff --git a/proxy/pkg/middleware/public_share_auth.go b/proxy/pkg/middleware/public_share_auth.go index 80c470eba..32e0fc004 100644 --- a/proxy/pkg/middleware/public_share_auth.go +++ b/proxy/pkg/middleware/public_share_auth.go @@ -2,7 +2,6 @@ package middleware import ( "net/http" - "strings" gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1" ) @@ -10,7 +9,6 @@ import ( const ( headerRevaAccessToken = "x-access-token" headerShareToken = "public-token" - appProviderPathPrefix = "/app/open" basicAuthPasswordPrefix = "basic|" authenticationType = "publicshares" ) @@ -24,7 +22,7 @@ func PublicShareAuth(opts ...Option) func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // Currently we only want to authenticate app open request coming from public shares. shareToken := r.Header.Get(headerShareToken) - if shareToken == "" || !strings.HasPrefix(appProviderPathPrefix, r.URL.Path) { + if shareToken == "" { // Don't authenticate next.ServeHTTP(w, r) return From 600c7ca35121e142a6f56f132322549d6723de04 Mon Sep 17 00:00:00 2001 From: David Christofas Date: Thu, 7 Oct 2021 14:55:11 +0200 Subject: [PATCH 4/5] use updated VerifyScope --- graph/pkg/middleware/auth.go | 2 +- ocis-pkg/middleware/account.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/graph/pkg/middleware/auth.go b/graph/pkg/middleware/auth.go index ed4a2ecf6..27cde835f 100644 --- a/graph/pkg/middleware/auth.go +++ b/graph/pkg/middleware/auth.go @@ -61,7 +61,7 @@ func Auth(opts ...account.Option) func(http.Handler) http.Handler { errorcode.InvalidAuthenticationToken.Render(w, r, http.StatusUnauthorized, "invalid token") return } - if ok, err := scope.VerifyScope(ctx, tokenScope, r); err != nil || !ok { + if ok, err := scope.VerifyScope(ctx, tokenScope, r, nil, tokenManager); err != nil || !ok { opt.Logger.Error().Err(err).Msg("verifying scope failed") errorcode.InvalidAuthenticationToken.Render(w, r, http.StatusUnauthorized, "verifying scope failed") return diff --git a/ocis-pkg/middleware/account.go b/ocis-pkg/middleware/account.go index 562a9ce45..5ee98ac7b 100644 --- a/ocis-pkg/middleware/account.go +++ b/ocis-pkg/middleware/account.go @@ -60,7 +60,7 @@ func ExtractAccountUUID(opts ...account.Option) func(http.Handler) http.Handler opt.Logger.Error().Err(err) return } - if ok, err := scope.VerifyScope(r.Context(), tokenScope, r); err != nil || !ok { + if ok, err := scope.VerifyScope(r.Context(), tokenScope, r, nil, tokenManager); err != nil || !ok { opt.Logger.Error().Err(err).Msg("verifying scope failed") return } From f5aabe1a87c514c1e05d2de8723786c63f720d6f Mon Sep 17 00:00:00 2001 From: David Christofas Date: Fri, 15 Oct 2021 17:26:48 +0200 Subject: [PATCH 5/5] update reva to latest master --- changelog/unreleased/update-reva.md | 10 ++++++++++ go.mod | 2 +- go.sum | 2 ++ graph/pkg/middleware/auth.go | 2 +- ocis-pkg/middleware/account.go | 2 +- 5 files changed, 15 insertions(+), 3 deletions(-) create mode 100644 changelog/unreleased/update-reva.md diff --git a/changelog/unreleased/update-reva.md b/changelog/unreleased/update-reva.md new file mode 100644 index 000000000..1448f5149 --- /dev/null +++ b/changelog/unreleased/update-reva.md @@ -0,0 +1,10 @@ +Enhancement: Update reva to v1.14.1-0.20211015144112-cddbdd4c560f + +Updated reva to v1.14.1-0.20211015144112-cddbdd4c560f +This update includes: + * Enhancement [cs3org/reva#2170](https://github.com/cs3org/reva/pull/2170): Handle propfind requests for existing files + * Enhancement [cs3org/reva#2166](https://github.com/cs3org/reva/pull/2166): Allow nil quota in decomposedfs + * Enhancement [cs3org/reva#2152](https://github.com/cs3org/reva/pull/2152): Report quota per storage space + * Enhancement [cs3org/reva#2143](https://github.com/cs3org/reva/pull/2143): Enabling apps to work in public shares + +https://github.com/owncloud/ocis/pull/2536 diff --git a/go.mod b/go.mod index 351db26ae..c1eaae965 100644 --- a/go.mod +++ b/go.mod @@ -20,7 +20,7 @@ require ( github.com/blevesearch/bleve/v2 v2.1.0 github.com/coreos/go-oidc/v3 v3.0.0 github.com/cs3org/go-cs3apis v0.0.0-20211007101428-6d142794ec11 - github.com/cs3org/reva v1.14.1-0.20211015081146-2e9bc0c7714a + github.com/cs3org/reva v1.14.1-0.20211015144112-cddbdd4c560f github.com/disintegration/imaging v1.6.2 github.com/glauth/glauth v1.1.3-0.20210729125545-b9aecdfcac31 github.com/go-chi/chi/v5 v5.0.4 diff --git a/go.sum b/go.sum index 787356f79..abeede699 100644 --- a/go.sum +++ b/go.sum @@ -293,6 +293,8 @@ github.com/cs3org/go-cs3apis v0.0.0-20211007101428-6d142794ec11 h1:cc/8fdzWdr/wA github.com/cs3org/go-cs3apis v0.0.0-20211007101428-6d142794ec11/go.mod h1:UXha4TguuB52H14EMoSsCqDj7k8a/t7g4gVP+bgY5LY= github.com/cs3org/reva v1.14.1-0.20211015081146-2e9bc0c7714a h1:xauop9DkHYtOA3qLGmohOi0rt6WqN8+1BCWu5i/4cL4= github.com/cs3org/reva v1.14.1-0.20211015081146-2e9bc0c7714a/go.mod h1:uENdZEtDFmTRt6+d4+Ro4P5XnNL+9I6gwftHEBJzHQw= +github.com/cs3org/reva v1.14.1-0.20211015144112-cddbdd4c560f h1:DVy9daUTEgnAP35Q5Vn5rt0FXiHwTr4vp0pecHFHA4Q= +github.com/cs3org/reva v1.14.1-0.20211015144112-cddbdd4c560f/go.mod h1:uENdZEtDFmTRt6+d4+Ro4P5XnNL+9I6gwftHEBJzHQw= github.com/cubewise-code/go-mime v0.0.0-20200519001935-8c5762b177d8 h1:Z9lwXumT5ACSmJ7WGnFl+OMLLjpz5uR2fyz7dC255FI= github.com/cubewise-code/go-mime v0.0.0-20200519001935-8c5762b177d8/go.mod h1:4abs/jPXcmJzYoYGF91JF9Uq9s/KL5n1jvFDix8KcqY= github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4= diff --git a/graph/pkg/middleware/auth.go b/graph/pkg/middleware/auth.go index 27cde835f..ed4a2ecf6 100644 --- a/graph/pkg/middleware/auth.go +++ b/graph/pkg/middleware/auth.go @@ -61,7 +61,7 @@ func Auth(opts ...account.Option) func(http.Handler) http.Handler { errorcode.InvalidAuthenticationToken.Render(w, r, http.StatusUnauthorized, "invalid token") return } - if ok, err := scope.VerifyScope(ctx, tokenScope, r, nil, tokenManager); err != nil || !ok { + if ok, err := scope.VerifyScope(ctx, tokenScope, r); err != nil || !ok { opt.Logger.Error().Err(err).Msg("verifying scope failed") errorcode.InvalidAuthenticationToken.Render(w, r, http.StatusUnauthorized, "verifying scope failed") return diff --git a/ocis-pkg/middleware/account.go b/ocis-pkg/middleware/account.go index 5ee98ac7b..562a9ce45 100644 --- a/ocis-pkg/middleware/account.go +++ b/ocis-pkg/middleware/account.go @@ -60,7 +60,7 @@ func ExtractAccountUUID(opts ...account.Option) func(http.Handler) http.Handler opt.Logger.Error().Err(err) return } - if ok, err := scope.VerifyScope(r.Context(), tokenScope, r, nil, tokenManager); err != nil || !ok { + if ok, err := scope.VerifyScope(r.Context(), tokenScope, r); err != nil || !ok { opt.Logger.Error().Err(err).Msg("verifying scope failed") return }