From 50cdb03f1b21ce81c1f95578870b447c113067c5 Mon Sep 17 00:00:00 2001 From: David Christofas Date: Thu, 23 Sep 2021 16:37:46 +0200 Subject: [PATCH] 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 00000000000..566bcb845a0 --- /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 01cdb6bbfa0..cd4a7ed14de 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 00000000000..80c470ebab5 --- /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) + }) + } +}