From f602c50ff92d8257b1a5589f07f2dcd6704040b5 Mon Sep 17 00:00:00 2001 From: David Christofas Date: Fri, 7 Jan 2022 16:32:45 +0100 Subject: [PATCH] fix thumbnailer for the new spaces approach --- thumbnails/pkg/config/config.go | 2 -- thumbnails/pkg/config/mappings.go | 4 ---- thumbnails/pkg/service/v0/service.go | 21 +++++++++------- webdav/pkg/config/config.go | 4 +++- webdav/pkg/config/mappings.go | 4 ++++ webdav/pkg/dav/requests/thumbnail.go | 15 +++++++----- webdav/pkg/server/http/server.go | 5 +++- webdav/pkg/service/v0/service.go | 36 ++++++++++++++++++++++++---- 8 files changed, 64 insertions(+), 27 deletions(-) diff --git a/thumbnails/pkg/config/config.go b/thumbnails/pkg/config/config.go index 5126a2d60d..0e92f4ce66 100644 --- a/thumbnails/pkg/config/config.go +++ b/thumbnails/pkg/config/config.go @@ -65,7 +65,6 @@ type Thumbnail struct { WebdavAllowInsecure bool `ocisConfig:"webdav_allow_insecure"` CS3AllowInsecure bool `ocisConfig:"cs3_allow_insecure"` RevaGateway string `ocisConfig:"reva_gateway"` - WebdavNamespace string `ocisConfig:"webdav_namespace"` FontMapFile string `ocisConfig:"font_map_file"` } @@ -101,7 +100,6 @@ func DefaultConfig() *Config { }, WebdavAllowInsecure: true, RevaGateway: "127.0.0.1:9142", - WebdavNamespace: "/home", CS3AllowInsecure: false, }, } diff --git a/thumbnails/pkg/config/mappings.go b/thumbnails/pkg/config/mappings.go index 6f6ef0d2fa..a826dd7692 100644 --- a/thumbnails/pkg/config/mappings.go +++ b/thumbnails/pkg/config/mappings.go @@ -111,9 +111,5 @@ func structMappings(cfg *Config) []shared.EnvBinding { EnvVars: []string{"OCIS_INSECURE", "THUMBNAILS_CS3SOURCE_INSECURE"}, Destination: &cfg.Thumbnail.CS3AllowInsecure, }, - { - EnvVars: []string{"STORAGE_WEBDAV_NAMESPACE"}, - Destination: &cfg.Thumbnail.WebdavNamespace, - }, } } diff --git a/thumbnails/pkg/service/v0/service.go b/thumbnails/pkg/service/v0/service.go index 8914aebbce..f15211fe28 100644 --- a/thumbnails/pkg/service/v0/service.go +++ b/thumbnails/pkg/service/v0/service.go @@ -8,6 +8,7 @@ import ( "strings" gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1" + userv1beta1 "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1" provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" revactx "github.com/cs3org/reva/pkg/ctx" @@ -30,8 +31,7 @@ func NewService(opts ...Option) v0proto.ThumbnailServiceHandler { logger.Fatal().Err(err).Msg("resolutions not configured correctly") } svc := Thumbnail{ - serviceID: options.Config.Server.Namespace + "." + options.Config.Server.Name, - webdavNamespace: options.Config.Thumbnail.WebdavNamespace, + serviceID: options.Config.Server.Namespace + "." + options.Config.Server.Name, manager: thumbnail.NewSimpleManager( resolutions, options.ThumbnailStorage, @@ -52,7 +52,6 @@ func NewService(opts ...Option) v0proto.ThumbnailServiceHandler { // Thumbnail implements the GRPC handler. type Thumbnail struct { serviceID string - webdavNamespace string manager thumbnail.Manager webdavSource imgsource.Source cs3Source imgsource.Source @@ -100,7 +99,7 @@ func (g Thumbnail) GetThumbnail(ctx context.Context, req *v0proto.GetThumbnailRe func (g Thumbnail) handleCS3Source(ctx context.Context, req *v0proto.GetThumbnailRequest, encoder thumbnail.Encoder) ([]byte, error) { src := req.GetCs3Source() - sRes, err := g.stat(src.Path, src.Authorization) + sRes, err := g.stat(src.Path, src.Authorization, nil) if err != nil { return nil, err } @@ -144,7 +143,11 @@ func (g Thumbnail) handleWebdavSource(ctx context.Context, req *v0proto.GetThumb return nil, errors.Wrap(err, "source url is invalid") } - var auth, statPath string + var ( + auth, statPath string + user *userv1beta1.User + ) + if src.IsPublicLink { q := imgURL.Query() var rsp *gateway.AuthenticateResponse @@ -171,12 +174,14 @@ func (g Thumbnail) handleWebdavSource(ctx context.Context, req *v0proto.GetThumb return nil, merrors.InternalServerError(g.serviceID, "could not authenticate: %s", err.Error()) } auth = rsp.Token + user = rsp.User statPath = path.Join("/public", src.PublicLinkToken, req.Filepath) } else { auth = src.RevaAuthorization - statPath = path.Join(g.webdavNamespace, req.Filepath) + statPath = req.Filepath + user = revactx.ContextMustGetUser(ctx) } - sRes, err := g.stat(statPath, auth) + sRes, err := g.stat(statPath, auth, user) if err != nil { return nil, err } @@ -214,7 +219,7 @@ func (g Thumbnail) handleWebdavSource(ctx context.Context, req *v0proto.GetThumb return thumb, nil } -func (g Thumbnail) stat(path, auth string) (*provider.StatResponse, error) { +func (g Thumbnail) stat(path, auth string, user *userv1beta1.User) (*provider.StatResponse, error) { ctx := metadata.AppendToOutgoingContext(context.Background(), revactx.TokenHeader, auth) req := &provider.StatRequest{ diff --git a/webdav/pkg/config/config.go b/webdav/pkg/config/config.go index c08c8ea0f9..722c8ed2f1 100644 --- a/webdav/pkg/config/config.go +++ b/webdav/pkg/config/config.go @@ -57,6 +57,7 @@ type Config struct { Service Service `ocisConfig:"service"` OcisPublicURL string `ocisConfig:"ocis_public_url"` WebdavNamespace string `ocisConfig:"webdav_namespace"` + RevaGateway string `ocisConfig:"reva_gateway"` Context context.Context Supervised bool @@ -97,6 +98,7 @@ func DefaultConfig() *Config { Namespace: "com.owncloud.web", }, OcisPublicURL: "https://127.0.0.1:9200", - WebdavNamespace: "/home", + WebdavNamespace: "/users/{{.Id.OpaqueId}}", + RevaGateway: "127.0.0.1:9142", } } diff --git a/webdav/pkg/config/mappings.go b/webdav/pkg/config/mappings.go index 7fa86211ef..df4abfde08 100644 --- a/webdav/pkg/config/mappings.go +++ b/webdav/pkg/config/mappings.go @@ -103,5 +103,9 @@ func structMappings(cfg *Config) []shared.EnvBinding { EnvVars: []string{"STORAGE_WEBDAV_NAMESPACE"}, Destination: &cfg.WebdavNamespace, }, + { + EnvVars: []string{"REVA_GATEWAY"}, + Destination: &cfg.RevaGateway, + }, } } diff --git a/webdav/pkg/dav/requests/thumbnail.go b/webdav/pkg/dav/requests/thumbnail.go index 6ce7b3d38a..be1e7d43c4 100644 --- a/webdav/pkg/dav/requests/thumbnail.go +++ b/webdav/pkg/dav/requests/thumbnail.go @@ -33,11 +33,13 @@ type ThumbnailRequest struct { Height int32 // In case of a public share the public link token. PublicLinkToken string + // The username from the requested URL + Username string } // ParseThumbnailRequest extracts all required parameters from a http request. func ParseThumbnailRequest(r *http.Request) (*ThumbnailRequest, error) { - fp, err := extractFilePath(r) + fp, username, err := extractFilePath(r) if err != nil { return nil, err } @@ -55,6 +57,7 @@ func ParseThumbnailRequest(r *http.Request) (*ThumbnailRequest, error) { Width: int32(width), Height: int32(height), PublicLinkToken: chi.URLParam(r, "token"), + Username: username, }, nil } @@ -64,23 +67,23 @@ func ParseThumbnailRequest(r *http.Request) (*ThumbnailRequest, error) { // // User and filepath are dynamic and filepath can contain slashes // So using the URLParam function is not possible. -func extractFilePath(r *http.Request) (string, error) { +func extractFilePath(r *http.Request) (string, string, error) { user := chi.URLParam(r, "user") user, err := url.QueryUnescape(user) if err != nil { - return "", errors.New("could not unescape user") + return "", "", errors.New("could not unescape user") } if user != "" { parts := strings.SplitN(r.URL.Path, user, 2) - return parts[1], nil + return parts[1], user, nil } token := chi.URLParam(r, "token") if token != "" { parts := strings.SplitN(r.URL.Path, token, 2) - return parts[1], nil + return parts[1], "", nil } - return "", errors.New("could not extract file path") + return "", "", errors.New("could not extract file path") } func parseDimensions(q url.Values) (int64, int64, error) { diff --git a/webdav/pkg/server/http/server.go b/webdav/pkg/server/http/server.go index 1b183af5ed..8d8344c87d 100644 --- a/webdav/pkg/server/http/server.go +++ b/webdav/pkg/server/http/server.go @@ -23,7 +23,7 @@ func Server(opts ...Option) (http.Service, error) { http.Flags(options.Flags...), ) - handle := svc.NewService( + handle, err := svc.NewService( svc.Logger(options.Logger), svc.Config(options.Config), svc.Middleware( @@ -47,6 +47,9 @@ func Server(opts ...Option) (http.Service, error) { ), ), ) + if err != nil { + return http.Service{}, err + } { handle = svc.NewInstrument(handle, options.Metrics) diff --git a/webdav/pkg/service/v0/service.go b/webdav/pkg/service/v0/service.go index 962942e6e9..64ed700d4e 100644 --- a/webdav/pkg/service/v0/service.go +++ b/webdav/pkg/service/v0/service.go @@ -3,11 +3,17 @@ package svc import ( "encoding/xml" "net/http" - "path" + "path/filepath" "strings" + gatewayv1beta1 "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1" + userv1beta1 "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" + rpcv1beta1 "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1" + "github.com/cs3org/reva/pkg/rgrpc/todo/pool" + "github.com/cs3org/reva/pkg/storage/utils/templates" "github.com/go-chi/render" merrors "go-micro.dev/v4/errors" + "google.golang.org/grpc/metadata" "github.com/owncloud/ocis/ocis-pkg/log" "github.com/owncloud/ocis/ocis-pkg/service/grpc" @@ -38,17 +44,24 @@ type Service interface { } // NewService returns a service implementation for Service. -func NewService(opts ...Option) Service { +func NewService(opts ...Option) (Service, error) { options := newOptions(opts...) + conf := options.Config m := chi.NewMux() m.Use(options.Middleware...) + gwc, err := pool.GetGatewayServiceClient(conf.RevaGateway) + if err != nil { + return nil, err + } + svc := Webdav{ - config: options.Config, + config: conf, log: options.Logger, mux: m, thumbnailsClient: thumbnails.NewThumbnailService("com.owncloud.api.thumbnails", grpc.DefaultClient), + revaClient: gwc, } m.Route(options.Config.HTTP.Root, func(r chi.Router) { @@ -57,7 +70,7 @@ func NewService(opts ...Option) Service { r.Head("/remote.php/dav/public-files/{token}/*", svc.PublicThumbnailHead) }) - return svc + return svc, nil } // Webdav defines implements the business logic for Service. @@ -66,6 +79,7 @@ type Webdav struct { log log.Logger mux *chi.Mux thumbnailsClient thumbnails.ThumbnailService + revaClient gatewayv1beta1.GatewayAPIClient } // ServeHTTP implements the Service interface. @@ -83,6 +97,18 @@ func (g Webdav) Thumbnail(w http.ResponseWriter, r *http.Request) { } t := r.Header.Get(TokenHeader) + ctx := metadata.AppendToOutgoingContext(r.Context(), TokenHeader, t) + userRes, err := g.revaClient.GetUserByClaim(ctx, &userv1beta1.GetUserByClaimRequest{ + Claim: "username", + Value: tr.Username, + }) + if err != nil || userRes.Status.Code != rpcv1beta1.Code_CODE_OK { + g.log.Error().Err(err).Msg("could not get user") + renderError(w, r, errInternalError("could not get user")) + return + } + + fullPath := filepath.Join(templates.WithUser(userRes.User, g.config.WebdavNamespace), tr.Filepath) rsp, err := g.thumbnailsClient.GetThumbnail(r.Context(), &thumbnails.GetThumbnailRequest{ Filepath: strings.TrimLeft(tr.Filepath, "/"), ThumbnailType: extensionToThumbnailType(strings.TrimLeft(tr.Extension, ".")), @@ -90,7 +116,7 @@ func (g Webdav) Thumbnail(w http.ResponseWriter, r *http.Request) { Height: tr.Height, Source: &thumbnails.GetThumbnailRequest_Cs3Source{ Cs3Source: &thumbnails.CS3Source{ - Path: path.Join(g.config.WebdavNamespace, tr.Filepath), + Path: fullPath, Authorization: t, }, },