From 9db3fd028e9dad546085cb9ca331c318c1beac84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= <1005065+DeepDiver1975@users.noreply.github.com> Date: Fri, 3 May 2024 12:20:27 +0200 Subject: [PATCH] feat: add maximum image dimension to be processed by the thumbnailer (#9035) * feat: add maximum image dimension to be processed by the thumbnailer * chore: make golangci-lint happy --- changelog/unreleased/max-input-image.md | 5 ++ services/thumbnails/pkg/command/server.go | 12 ++-- services/thumbnails/pkg/command/version.go | 2 +- services/thumbnails/pkg/config/config.go | 2 + .../pkg/config/defaults/defaultconfig.go | 2 + .../thumbnails/pkg/config/parser/parse.go | 3 +- services/thumbnails/pkg/errors/error.go | 18 +++++ .../thumbnails/pkg/preprocessor/fontloader.go | 13 ++-- .../pkg/preprocessor/preprocessor.go | 7 +- .../thumbnails/pkg/server/debug/server.go | 8 +-- services/thumbnails/pkg/server/http/option.go | 3 +- .../pkg/service/grpc/v0/decorators/base.go | 6 +- .../pkg/service/grpc/v0/decorators/logging.go | 6 +- .../pkg/service/grpc/v0/decorators/tracing.go | 8 +-- .../thumbnails/pkg/service/grpc/v0/option.go | 7 -- .../thumbnails/pkg/service/grpc/v0/service.go | 52 +++++++------- .../thumbnails/pkg/service/http/v0/option.go | 11 --- .../thumbnails/pkg/service/http/v0/service.go | 15 ++-- services/thumbnails/pkg/service/jwt/jwt.go | 1 + services/thumbnails/pkg/thumbnail/encoding.go | 24 +++---- .../thumbnails/pkg/thumbnail/generator.go | 9 +-- .../thumbnails/pkg/thumbnail/imgsource/cs3.go | 22 +++--- .../pkg/thumbnail/imgsource/webdav.go | 2 +- .../pkg/thumbnail/storage/filesystem.go | 3 + .../pkg/thumbnail/storage/inmemory.go | 50 -------------- .../pkg/thumbnail/storage/inmemory_test.go | 65 ------------------ .../pkg/thumbnail/storage/storage.go | 10 +-- .../thumbnails/pkg/thumbnail/thumbnail.go | 36 +++++++--- .../pkg/thumbnail/thumbnail_test.go | 61 +++++++++++++++- services/thumbnails/testdata/oc.gif | Bin 0 -> 25602 bytes services/thumbnails/testdata/test.ggs | Bin 0 -> 59794 bytes 31 files changed, 219 insertions(+), 244 deletions(-) create mode 100644 changelog/unreleased/max-input-image.md create mode 100644 services/thumbnails/pkg/errors/error.go delete mode 100644 services/thumbnails/pkg/thumbnail/storage/inmemory.go delete mode 100644 services/thumbnails/pkg/thumbnail/storage/inmemory_test.go create mode 100644 services/thumbnails/testdata/oc.gif create mode 100644 services/thumbnails/testdata/test.ggs diff --git a/changelog/unreleased/max-input-image.md b/changelog/unreleased/max-input-image.md new file mode 100644 index 0000000000..595279d9c0 --- /dev/null +++ b/changelog/unreleased/max-input-image.md @@ -0,0 +1,5 @@ +Change: Define maximum input image dimensions when generating previews + +This is a general hardening change to limit processing time and resources of the thumbnailer. + +https://github.com/owncloud/ocis/pull/9035 diff --git a/services/thumbnails/pkg/command/server.go b/services/thumbnails/pkg/command/server.go index 580e70e885..d62095dd28 100644 --- a/services/thumbnails/pkg/command/server.go +++ b/services/thumbnails/pkg/command/server.go @@ -26,10 +26,10 @@ func Server(cfg *config.Config) *cli.Command { Name: "server", Usage: fmt.Sprintf("start the %s service without runtime (unsupervised mode)", cfg.Service.Name), Category: "server", - Before: func(c *cli.Context) error { + Before: func(_ *cli.Context) error { return configlog.ReturnFatal(parser.ParseConfig(cfg)) }, - Action: func(c *cli.Context) error { + Action: func(_ *cli.Context) error { logger := logging.Configure(cfg.Service.Name, cfg.Log) traceProvider, err := tracing.GetServiceTraceProvider(cfg.Tracing, cfg.Service.Name) @@ -49,12 +49,12 @@ func Server(cfg *config.Config) *cli.Command { } return context.WithCancel(cfg.Context) }() - metrics = metrics.New() + m = metrics.New() ) defer cancel() - metrics.BuildInfo.WithLabelValues(version.GetString()).Set(1) + m.BuildInfo.WithLabelValues(version.GetString()).Set(1) service := grpc.NewService( grpc.Logger(logger), @@ -63,7 +63,7 @@ func Server(cfg *config.Config) *cli.Command { grpc.Name(cfg.Service.Name), grpc.Namespace(cfg.GRPC.Namespace), grpc.Address(cfg.GRPC.Addr), - grpc.Metrics(metrics), + grpc.Metrics(m), grpc.TraceProvider(traceProvider), ) @@ -95,7 +95,7 @@ func Server(cfg *config.Config) *cli.Command { http.Logger(logger), http.Context(ctx), http.Config(cfg), - http.Metrics(metrics), + http.Metrics(m), http.Namespace(cfg.HTTP.Namespace), http.TraceProvider(traceProvider), ) diff --git a/services/thumbnails/pkg/command/version.go b/services/thumbnails/pkg/command/version.go index b52381f04a..4d7377b378 100644 --- a/services/thumbnails/pkg/command/version.go +++ b/services/thumbnails/pkg/command/version.go @@ -18,7 +18,7 @@ func Version(cfg *config.Config) *cli.Command { Name: "version", Usage: "print the version of this binary and the running service instances", Category: "info", - Action: func(c *cli.Context) error { + Action: func(_ *cli.Context) error { fmt.Println("Version: " + version.GetString()) fmt.Printf("Compiled: %s\n", version.Compiled()) fmt.Println("") diff --git a/services/thumbnails/pkg/config/config.go b/services/thumbnails/pkg/config/config.go index 85e882a29e..59d2032cf8 100644 --- a/services/thumbnails/pkg/config/config.go +++ b/services/thumbnails/pkg/config/config.go @@ -44,4 +44,6 @@ type Thumbnail struct { FontMapFile string `yaml:"font_map_file" env:"THUMBNAILS_TXT_FONTMAP_FILE" desc:"The path to a font file for txt thumbnails." introductionVersion:"pre5.0"` TransferSecret string `yaml:"transfer_secret" env:"THUMBNAILS_TRANSFER_TOKEN" desc:"The secret to sign JWT to download the actual thumbnail file." introductionVersion:"pre5.0"` DataEndpoint string `yaml:"data_endpoint" env:"THUMBNAILS_DATA_ENDPOINT" desc:"The HTTP endpoint where the actual thumbnail file can be downloaded." introductionVersion:"pre5.0"` + MaxInputWidth int `yaml:"max_input_width" env:"THUMBNAILS_MAX_INPUT_WIDTH" desc:"The maximum width of an input image which is being processed." introductionVersion:"6.0"` + MaxInputHeight int `yaml:"max_input_height" env:"THUMBNAILS_MAX_INPUT_HEIGHT" desc:"The maximum height of an input image which is being processed." introductionVersion:"6.0"` } diff --git a/services/thumbnails/pkg/config/defaults/defaultconfig.go b/services/thumbnails/pkg/config/defaults/defaultconfig.go index 84c1011ef6..a0465a7efc 100644 --- a/services/thumbnails/pkg/config/defaults/defaultconfig.go +++ b/services/thumbnails/pkg/config/defaults/defaultconfig.go @@ -48,6 +48,8 @@ func DefaultConfig() *config.Config { RevaGateway: shared.DefaultRevaConfig().Address, CS3AllowInsecure: false, DataEndpoint: "http://127.0.0.1:9186/thumbnails/data", + MaxInputWidth: 7680, + MaxInputHeight: 4320, }, } } diff --git a/services/thumbnails/pkg/config/parser/parse.go b/services/thumbnails/pkg/config/parser/parse.go index 6334261084..4ec11019dd 100644 --- a/services/thumbnails/pkg/config/parser/parse.go +++ b/services/thumbnails/pkg/config/parser/parse.go @@ -33,6 +33,7 @@ func ParseConfig(cfg *config.Config) error { return Validate(cfg) } -func Validate(cfg *config.Config) error { +// Validate can validate the configuration +func Validate(_ *config.Config) error { return nil } diff --git a/services/thumbnails/pkg/errors/error.go b/services/thumbnails/pkg/errors/error.go new file mode 100644 index 0000000000..e8bbe30889 --- /dev/null +++ b/services/thumbnails/pkg/errors/error.go @@ -0,0 +1,18 @@ +package errors + +import "errors" + +var ( + // ErrImageTooLarge defines an error when an input image is too large + ErrImageTooLarge = errors.New("thumbnails: image is too large") + // ErrInvalidType represents the error when a type can't be encoded. + ErrInvalidType = errors.New("thumbnails: can't encode this type") + // ErrNoEncoderForType represents the error when an encoder couldn't be found for a type. + ErrNoEncoderForType = errors.New("thumbnails: no encoder for this type found") + // ErrNoImageFromAudioFile defines an error when an image cannot be extracted from an audio file + ErrNoImageFromAudioFile = errors.New("thumbnails: could not extract image from audio file") + // ErrNoConverterForExtractedImageFromAudioFile defines an error when the extracted image from an audio file could not be converted + ErrNoConverterForExtractedImageFromAudioFile = errors.New("thumbnails: could not find converter for image extracted from audio file") + // ErrCS3AuthorizationMissing defines an error when the CS3 authorization is missing + ErrCS3AuthorizationMissing = errors.New("thumbnails: cs3source - authorization missing") +) diff --git a/services/thumbnails/pkg/preprocessor/fontloader.go b/services/thumbnails/pkg/preprocessor/fontloader.go index 672e62593c..69f979e61b 100644 --- a/services/thumbnails/pkg/preprocessor/fontloader.go +++ b/services/thumbnails/pkg/preprocessor/fontloader.go @@ -25,28 +25,28 @@ type FontMap struct { DefaultFont string `json:"defaultFont"` } -// It contains the location of the loaded file (in FLoc) and the FontMap loaded +// FontMapData contains the location of the loaded file (in FLoc) and the FontMap loaded // from the file type FontMapData struct { FMap *FontMap FLoc string } -// It contains the location of the font used, and the loaded face (font.Face) +// LoadedFace contains the location of the font used, and the loaded face (font.Face) // ready to be used type LoadedFace struct { FontFile string Face font.Face } -// Represents a FontLoader. Use the "NewFontLoader" to get a instance +// FontLoader represents a FontLoader. Use the "NewFontLoader" to get a instance type FontLoader struct { faceCache sync.Cache fontMapData *FontMapData faceOpts *opentype.FaceOptions } -// Create a new FontLoader based on the fontMapFile. The FaceOptions will +// NewFontLoader creates a new FontLoader based on the fontMapFile. The FaceOptions will // be the same for all the font loaded by this instance. // Note that only the fonts described in the fontMapFile will be used. // @@ -92,7 +92,7 @@ func NewFontLoader(fontMapFile string, faceOpts *opentype.FaceOptions) (*FontLoa }, nil } -// Load and return the font face to be used for that script according to the +// LoadFaceForScript loads and returns the font face to be used for that script according to the // FontMap set when the FontLoader was created. If the script doesn't have // an associated font, a default font will be used. Note that the default font // might not be able to handle properly the script @@ -146,14 +146,17 @@ func (fl *FontLoader) LoadFaceForScript(script string) (*LoadedFace, error) { return loadedFace, nil } +// GetFaceOptSize returns face opt size func (fl *FontLoader) GetFaceOptSize() float64 { return fl.faceOpts.Size } +// GetFaceOptDPI returns face opt DPI func (fl *FontLoader) GetFaceOptDPI() float64 { return fl.faceOpts.DPI } +// GetScriptList returns script list func (fl *FontLoader) GetScriptList() []string { fontMap := fl.fontMapData.FMap.FontMap diff --git a/services/thumbnails/pkg/preprocessor/preprocessor.go b/services/thumbnails/pkg/preprocessor/preprocessor.go index d8addd3a70..4254c1a68b 100644 --- a/services/thumbnails/pkg/preprocessor/preprocessor.go +++ b/services/thumbnails/pkg/preprocessor/preprocessor.go @@ -19,6 +19,7 @@ import ( "golang.org/x/image/math/fixed" "github.com/dhowden/tag" + thumbnailerErrors "github.com/owncloud/ocis/v2/services/thumbnails/pkg/errors" ) // FileConverter is the interface for the file converter @@ -98,12 +99,12 @@ func (i AudioDecoder) Convert(r io.Reader) (interface{}, error) { picture := m.Picture() if picture == nil { - return nil, errors.New(`could not extract image from audio file`) + return nil, thumbnailerErrors.ErrNoImageFromAudioFile } converter := ForType(picture.MIMEType, nil) if converter == nil { - return nil, errors.New(`could not find converter for image extraced from audio file`) + return nil, thumbnailerErrors.ErrNoConverterForExtractedImageFromAudioFile } return converter.Convert(bytes.NewReader(picture.Data)) @@ -259,7 +260,7 @@ func ForType(mimeType string, opts map[string]interface{}) FileConverter { fontLoader, err := NewFontLoader(fontFileMap, fontFaceOpts) if err != nil { - // if couldn't create the FontLoader with the specified fontFileMap, + // if it couldn't create the FontLoader with the specified fontFileMap, // try to use the default font fontLoader, _ = NewFontLoader("", fontFaceOpts) } diff --git a/services/thumbnails/pkg/server/debug/server.go b/services/thumbnails/pkg/server/debug/server.go index 55ad0121d4..0f912967d8 100644 --- a/services/thumbnails/pkg/server/debug/server.go +++ b/services/thumbnails/pkg/server/debug/server.go @@ -27,8 +27,8 @@ func Server(opts ...Option) (*http.Server, error) { } // health implements the health check. -func health(cfg *config.Config) func(http.ResponseWriter, *http.Request) { - return func(w http.ResponseWriter, r *http.Request) { +func health(_ *config.Config) func(http.ResponseWriter, *http.Request) { + return func(w http.ResponseWriter, _ *http.Request) { w.Header().Set("Content-Type", "text/plain") w.WriteHeader(http.StatusOK) @@ -43,8 +43,8 @@ func health(cfg *config.Config) func(http.ResponseWriter, *http.Request) { } // ready implements the ready check. -func ready(cfg *config.Config) func(http.ResponseWriter, *http.Request) { - return func(w http.ResponseWriter, r *http.Request) { +func ready(_ *config.Config) func(http.ResponseWriter, *http.Request) { + return func(w http.ResponseWriter, _ *http.Request) { w.Header().Set("Content-Type", "text/plain") w.WriteHeader(http.StatusOK) diff --git a/services/thumbnails/pkg/server/http/option.go b/services/thumbnails/pkg/server/http/option.go index b77014b159..6bbf4627be 100644 --- a/services/thumbnails/pkg/server/http/option.go +++ b/services/thumbnails/pkg/server/http/option.go @@ -8,6 +8,7 @@ import ( "github.com/owncloud/ocis/v2/services/thumbnails/pkg/metrics" "github.com/urfave/cli/v2" "go.opentelemetry.io/otel/trace" + "go.opentelemetry.io/otel/trace/noop" ) // Option defines a single option function. @@ -76,7 +77,7 @@ func TraceProvider(traceProvider trace.TracerProvider) Option { if traceProvider != nil { o.TraceProvider = traceProvider } else { - o.TraceProvider = trace.NewNoopTracerProvider() + o.TraceProvider = noop.NewTracerProvider() } } } diff --git a/services/thumbnails/pkg/service/grpc/v0/decorators/base.go b/services/thumbnails/pkg/service/grpc/v0/decorators/base.go index c43d393dec..5a1f6d3c0d 100644 --- a/services/thumbnails/pkg/service/grpc/v0/decorators/base.go +++ b/services/thumbnails/pkg/service/grpc/v0/decorators/base.go @@ -6,7 +6,7 @@ import ( thumbnailssvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/thumbnails/v0" ) -// Interface acting as facade, holding all the interfaces that this +// DecoratedService is an interface acting as facade, holding all the interfaces that this // thumbnails microservice is expecting to implement. // For now, only the thumbnailssvc.ThumbnailServiceHandler is present, // but a future configsvc.ConfigServiceHandler is expected to be added here @@ -17,7 +17,7 @@ type DecoratedService interface { thumbnailssvc.ThumbnailServiceHandler } -// Base type to implement the decorators. It will provide a basic implementation +// Decorator is the base type to implement the decorators. It will provide a basic implementation // by delegating to the decoratedService // // Expected implementations will be like: @@ -43,7 +43,7 @@ type Decorator struct { next DecoratedService } -// Base implementation for the GetThumbnail (for the thumbnailssvc). +// GetThumbnail is the base implementation for the thumbnailssvc.GetThumbnail. // It will just delegate to the underlying decoratedService // // Your custom decorator is expected to overwrite this function, diff --git a/services/thumbnails/pkg/service/grpc/v0/decorators/logging.go b/services/thumbnails/pkg/service/grpc/v0/decorators/logging.go index e8174d40ee..c998af1b2d 100644 --- a/services/thumbnails/pkg/service/grpc/v0/decorators/logging.go +++ b/services/thumbnails/pkg/service/grpc/v0/decorators/logging.go @@ -34,11 +34,11 @@ func (l logging) GetThumbnail(ctx context.Context, req *thumbnailssvc.GetThumbna Logger() if err != nil { - merror := merrors.FromError(err) - switch merror.Code { + fromError := merrors.FromError(err) + switch fromError.GetCode() { case http.StatusNotFound: logger.Debug(). - Str("error_detail", merror.Detail). + Str("error_detail", fromError.GetDetail()). Msg("no thumbnail found") default: logger.Warn(). diff --git a/services/thumbnails/pkg/service/grpc/v0/decorators/tracing.go b/services/thumbnails/pkg/service/grpc/v0/decorators/tracing.go index f34166e3e6..ba77846c0d 100644 --- a/services/thumbnails/pkg/service/grpc/v0/decorators/tracing.go +++ b/services/thumbnails/pkg/service/grpc/v0/decorators/tracing.go @@ -35,10 +35,10 @@ func (t tracing) GetThumbnail(ctx context.Context, req *thumbnailssvc.GetThumbna defer span.End() span.SetAttributes( - attribute.KeyValue{Key: "filepath", Value: attribute.StringValue(req.Filepath)}, - attribute.KeyValue{Key: "thumbnail_type", Value: attribute.StringValue(req.ThumbnailType.String())}, - attribute.KeyValue{Key: "width", Value: attribute.IntValue(int(req.Width))}, - attribute.KeyValue{Key: "height", Value: attribute.IntValue(int(req.Height))}, + attribute.KeyValue{Key: "filepath", Value: attribute.StringValue(req.GetFilepath())}, + attribute.KeyValue{Key: "thumbnail_type", Value: attribute.StringValue(req.GetThumbnailType().String())}, + attribute.KeyValue{Key: "width", Value: attribute.IntValue(int(req.GetWidth()))}, + attribute.KeyValue{Key: "height", Value: attribute.IntValue(int(req.GetHeight()))}, ) } diff --git a/services/thumbnails/pkg/service/grpc/v0/option.go b/services/thumbnails/pkg/service/grpc/v0/option.go index 00cf0eeab3..07c1556e41 100644 --- a/services/thumbnails/pkg/service/grpc/v0/option.go +++ b/services/thumbnails/pkg/service/grpc/v0/option.go @@ -50,13 +50,6 @@ func Config(val *config.Config) Option { } } -// Middleware provides a function to set the middleware option. -func Middleware(val ...func(http.Handler) http.Handler) Option { - return func(o *Options) { - o.Middleware = val - } -} - // ThumbnailStorage provides a function to set the thumbnail storage option. func ThumbnailStorage(val storage.Storage) Option { return func(o *Options) { diff --git a/services/thumbnails/pkg/service/grpc/v0/service.go b/services/thumbnails/pkg/service/grpc/v0/service.go index 156b46e0ef..5982b1ca40 100644 --- a/services/thumbnails/pkg/service/grpc/v0/service.go +++ b/services/thumbnails/pkg/service/grpc/v0/service.go @@ -43,6 +43,8 @@ func NewService(opts ...Option) decorators.DecoratedService { resolutions, options.ThumbnailStorage, logger, + options.Config.Thumbnail.MaxInputWidth, + options.Config.Thumbnail.MaxInputHeight, ), webdavSource: options.ImageSource, cs3Source: options.CS3Source, @@ -116,7 +118,7 @@ func (g Thumbnail) GetThumbnail(ctx context.Context, req *thumbnailssvc.GetThumb func (g Thumbnail) handleCS3Source(ctx context.Context, req *thumbnailssvc.GetThumbnailRequest) (string, error) { src := req.GetCs3Source() - sRes, err := g.stat(src.Path, src.Authorization) + sRes, err := g.stat(src.GetPath(), src.GetAuthorization()) if err != nil { return "", err } @@ -125,7 +127,7 @@ func (g Thumbnail) handleCS3Source(ctx context.Context, req *thumbnailssvc.GetTh if tType == "" { tType = req.GetThumbnailType().String() } - tr, err := thumbnail.PrepareRequest(int(req.Width), int(req.Height), tType, sRes.GetInfo().GetChecksum().GetSum(), req.Processor) + tr, err := thumbnail.PrepareRequest(int(req.GetWidth()), int(req.GetHeight()), tType, sRes.GetInfo().GetChecksum().GetSum(), req.GetProcessor()) if err != nil { return "", merrors.BadRequest(g.serviceID, err.Error()) } @@ -134,12 +136,12 @@ func (g Thumbnail) handleCS3Source(ctx context.Context, req *thumbnailssvc.GetTh return key, nil } - ctx = imgsource.ContextSetAuthorization(ctx, src.Authorization) - r, err := g.cs3Source.Get(ctx, src.Path) + ctx = imgsource.ContextSetAuthorization(ctx, src.GetAuthorization()) + r, err := g.cs3Source.Get(ctx, src.GetPath()) if err != nil { return "", merrors.InternalServerError(g.serviceID, "could not get image from source: %s", err.Error()) } - defer r.Close() // nolint:errcheck + defer r.Close() ppOpts := map[string]interface{}{ "fontFileMap": g.preprocessorOpts.TxtFontFileMap, } @@ -158,13 +160,13 @@ func (g Thumbnail) handleCS3Source(ctx context.Context, req *thumbnailssvc.GetTh func (g Thumbnail) handleWebdavSource(ctx context.Context, req *thumbnailssvc.GetThumbnailRequest) (string, error) { src := req.GetWebdavSource() - imgURL, err := url.Parse(src.Url) + imgURL, err := url.Parse(src.GetUrl()) if err != nil { return "", errors.Wrap(err, "source url is invalid") } var auth, statPath string - if src.IsPublicLink { + if src.GetIsPublicLink() { q := imgURL.Query() var rsp *gateway.AuthenticateResponse client, err := g.selector.Next() @@ -177,13 +179,13 @@ func (g Thumbnail) handleWebdavSource(ctx context.Context, req *thumbnailssvc.Ge exp := q.Get("expiration") rsp, err = client.Authenticate(ctx, &gateway.AuthenticateRequest{ Type: "publicshares", - ClientId: src.PublicLinkToken, + ClientId: src.GetPublicLinkToken(), ClientSecret: strings.Join([]string{"signature", sig, exp}, "|"), }) } else { rsp, err = client.Authenticate(ctx, &gateway.AuthenticateRequest{ Type: "publicshares", - ClientId: src.PublicLinkToken, + ClientId: src.GetPublicLinkToken(), // We pass an empty password because we expect non pre-signed public links // to not be password protected ClientSecret: "password|", @@ -193,11 +195,11 @@ func (g Thumbnail) handleWebdavSource(ctx context.Context, req *thumbnailssvc.Ge if err != nil { return "", merrors.InternalServerError(g.serviceID, "could not authenticate: %s", err.Error()) } - auth = rsp.Token - statPath = path.Join("/public", src.PublicLinkToken, req.Filepath) + auth = rsp.GetToken() + statPath = path.Join("/public", src.GetPublicLinkToken(), req.GetFilepath()) } else { - auth = src.RevaAuthorization - statPath = req.Filepath + auth = src.GetRevaAuthorization() + statPath = req.GetFilepath() } sRes, err := g.stat(statPath, auth) if err != nil { @@ -208,7 +210,7 @@ func (g Thumbnail) handleWebdavSource(ctx context.Context, req *thumbnailssvc.Ge if tType == "" { tType = req.GetThumbnailType().String() } - tr, err := thumbnail.PrepareRequest(int(req.Width), int(req.Height), tType, sRes.GetInfo().GetChecksum().GetSum(), req.Processor) + tr, err := thumbnail.PrepareRequest(int(req.GetWidth()), int(req.GetHeight()), tType, sRes.GetInfo().GetChecksum().GetSum(), req.GetProcessor()) if err != nil { return "", merrors.BadRequest(g.serviceID, err.Error()) } @@ -217,8 +219,8 @@ func (g Thumbnail) handleWebdavSource(ctx context.Context, req *thumbnailssvc.Ge return key, nil } - if src.WebdavAuthorization != "" { - ctx = imgsource.ContextSetAuthorization(ctx, src.WebdavAuthorization) + if src.GetWebdavAuthorization() != "" { + ctx = imgsource.ContextSetAuthorization(ctx, src.GetWebdavAuthorization()) } // add signature and expiration to webdav url @@ -232,7 +234,7 @@ func (g Thumbnail) handleWebdavSource(ctx context.Context, req *thumbnailssvc.Ge if err != nil { return "", merrors.InternalServerError(g.serviceID, "could not get image from source: %s", err.Error()) } - defer r.Close() // nolint:errcheck + defer r.Close() ppOpts := map[string]interface{}{ "fontFileMap": g.preprocessorOpts.TxtFontFileMap, } @@ -272,16 +274,16 @@ func (g Thumbnail) stat(path, auth string) (*provider.StatResponse, error) { return nil, merrors.InternalServerError(g.serviceID, "could not stat file: %s", err.Error()) } - if rsp.Status.Code != rpc.Code_CODE_OK { - switch rsp.Status.Code { + if rsp.GetStatus().GetCode() != rpc.Code_CODE_OK { + switch rsp.GetStatus().GetCode() { case rpc.Code_CODE_NOT_FOUND: - return nil, merrors.NotFound(g.serviceID, "could not stat file: %s", rsp.Status.Message) + return nil, merrors.NotFound(g.serviceID, "could not stat file: %s", rsp.GetStatus().GetMessage()) default: - g.logger.Error().Str("status_message", rsp.Status.Message).Str("path", path).Msg("could not stat file") - return nil, merrors.InternalServerError(g.serviceID, "could not stat file: %s", rsp.Status.Message) + g.logger.Error().Str("status_message", rsp.GetStatus().GetMessage()).Str("path", path).Msg("could not stat file") + return nil, merrors.InternalServerError(g.serviceID, "could not stat file: %s", rsp.GetStatus().GetMessage()) } } - if rsp.Info.Type != provider.ResourceType_RESOURCE_TYPE_FILE { + if rsp.GetInfo().GetType() != provider.ResourceType_RESOURCE_TYPE_FILE { return nil, merrors.BadRequest(g.serviceID, "Unsupported file type") } if utils.ReadPlainFromOpaque(rsp.GetInfo().GetOpaque(), "status") == "processing" { @@ -292,11 +294,11 @@ func (g Thumbnail) stat(path, auth string) (*provider.StatResponse, error) { Status: http.StatusText(http.StatusTooEarly), } } - if rsp.Info.GetChecksum().GetSum() == "" { + if rsp.GetInfo().GetChecksum().GetSum() == "" { g.logger.Error().Msg("resource info is missing checksum") return nil, merrors.NotFound(g.serviceID, "resource info is missing a checksum") } - if !thumbnail.IsMimeTypeSupported(rsp.Info.MimeType) { + if !thumbnail.IsMimeTypeSupported(rsp.GetInfo().GetMimeType()) { return nil, merrors.NotFound(g.serviceID, "Unsupported file type") } return rsp, nil diff --git a/services/thumbnails/pkg/service/http/v0/option.go b/services/thumbnails/pkg/service/http/v0/option.go index aa630843ec..d7ba389e3e 100644 --- a/services/thumbnails/pkg/service/http/v0/option.go +++ b/services/thumbnails/pkg/service/http/v0/option.go @@ -59,14 +59,3 @@ func ThumbnailStorage(storage storage.Storage) Option { o.ThumbnailStorage = storage } } - -// TraceProvider provides a function to configure the trace provider -func TraceProvider(traceProvider trace.TracerProvider) Option { - return func(o *Options) { - if traceProvider != nil { - o.TraceProvider = traceProvider - } else { - o.TraceProvider = trace.NewNoopTracerProvider() - } - } -} diff --git a/services/thumbnails/pkg/service/http/v0/service.go b/services/thumbnails/pkg/service/http/v0/service.go index 8e36a3727d..7207507a7e 100644 --- a/services/thumbnails/pkg/service/http/v0/service.go +++ b/services/thumbnails/pkg/service/http/v0/service.go @@ -25,8 +25,8 @@ const ( // Service defines the service handlers. type Service interface { - ServeHTTP(http.ResponseWriter, *http.Request) - GetThumbnail(http.ResponseWriter, *http.Request) + ServeHTTP(w http.ResponseWriter, r *http.Request) + GetThumbnail(w http.ResponseWriter, r *http.Request) } // NewService returns a service implementation for Service. @@ -58,6 +58,8 @@ func NewService(opts ...Option) Service { resolutions, options.ThumbnailStorage, logger, + options.Config.Thumbnail.MaxInputWidth, + options.Config.Thumbnail.MaxInputHeight, ), } @@ -66,7 +68,7 @@ func NewService(opts ...Option) Service { r.Get("/data", svc.GetThumbnail) }) - _ = chi.Walk(m, func(method string, route string, handler http.Handler, middlewares ...func(http.Handler) http.Handler) error { + _ = chi.Walk(m, func(method string, route string, _ http.Handler, middlewares ...func(http.Handler) http.Handler) error { options.Logger.Debug().Str("method", method).Str("route", route).Int("middlewares", len(middlewares)).Msg("serving endpoint") return nil }) @@ -92,7 +94,7 @@ func (s Thumbnails) GetThumbnail(w http.ResponseWriter, r *http.Request) { logger := s.logger.SubloggerWithRequestID(r.Context()) key := r.Context().Value(keyContextKey).(string) - thumbnail, err := s.manager.GetThumbnail(key) + thumbnailBytes, err := s.manager.GetThumbnail(key) if err != nil { logger.Debug(). Err(err). @@ -103,8 +105,8 @@ func (s Thumbnails) GetThumbnail(w http.ResponseWriter, r *http.Request) { } w.WriteHeader(http.StatusOK) - w.Header().Set("Content-Length", strconv.Itoa(len(thumbnail))) - if _, err = w.Write(thumbnail); err != nil { + w.Header().Set("Content-Length", strconv.Itoa(len(thumbnailBytes))) + if _, err = w.Write(thumbnailBytes); err != nil { logger.Error(). Err(err). Str("key", key). @@ -112,6 +114,7 @@ func (s Thumbnails) GetThumbnail(w http.ResponseWriter, r *http.Request) { } } +// TransferTokenValidator validates a transfer token func (s Thumbnails) TransferTokenValidator(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { logger := s.logger.SubloggerWithRequestID(r.Context()) diff --git a/services/thumbnails/pkg/service/jwt/jwt.go b/services/thumbnails/pkg/service/jwt/jwt.go index bbe46bb94d..86e1ff02cc 100644 --- a/services/thumbnails/pkg/service/jwt/jwt.go +++ b/services/thumbnails/pkg/service/jwt/jwt.go @@ -2,6 +2,7 @@ package jwt import "github.com/golang-jwt/jwt/v4" +// ThumbnailClaims defines the claims for thumb-nailing type ThumbnailClaims struct { jwt.RegisteredClaims Key string `json:"key"` diff --git a/services/thumbnails/pkg/thumbnail/encoding.go b/services/thumbnails/pkg/thumbnail/encoding.go index 65b5b17373..2cf22ba360 100644 --- a/services/thumbnails/pkg/thumbnail/encoding.go +++ b/services/thumbnails/pkg/thumbnail/encoding.go @@ -1,13 +1,14 @@ package thumbnail import ( - "errors" "image" "image/gif" "image/jpeg" "image/png" "io" "strings" + + "github.com/owncloud/ocis/v2/services/thumbnails/pkg/errors" ) const ( @@ -18,17 +19,10 @@ const ( typeGgs = "ggs" ) -var ( - // ErrInvalidType represents the error when a type can't be encoded. - ErrInvalidType = errors.New("can't encode this type") - // ErrNoEncoderForType represents the error when an encoder couldn't be found for a type. - ErrNoEncoderForType = errors.New("no encoder for this type found") -) - // Encoder encodes the thumbnail to a specific format. type Encoder interface { // Encode encodes the image to a format. - Encode(io.Writer, interface{}) error + Encode(w io.Writer, img interface{}) error // Types returns the formats suffixes. Types() []string // MimeType returns the mimetype used by the encoder. @@ -42,7 +36,7 @@ type PngEncoder struct{} func (e PngEncoder) Encode(w io.Writer, img interface{}) error { m, ok := img.(image.Image) if !ok { - return ErrInvalidType + return errors.ErrInvalidType } return png.Encode(w, m) } @@ -57,14 +51,14 @@ func (e PngEncoder) MimeType() string { return "image/png" } -// JpegEncoder encodes to jpg. +// JpegEncoder encodes to jpg type JpegEncoder struct{} // Encode encodes to jpg func (e JpegEncoder) Encode(w io.Writer, img interface{}) error { m, ok := img.(image.Image) if !ok { - return ErrInvalidType + return errors.ErrInvalidType } return jpeg.Encode(w, m, nil) } @@ -79,17 +73,19 @@ func (e JpegEncoder) MimeType() string { return "image/jpeg" } +// GifEncoder encodes to gif type GifEncoder struct{} // Encode encodes the image to a gif format func (e GifEncoder) Encode(w io.Writer, img interface{}) error { g, ok := img.(*gif.GIF) if !ok { - return ErrInvalidType + return errors.ErrInvalidType } return gif.EncodeAll(w, g) } +// Types returns the supported types of the GifEncoder func (e GifEncoder) Types() []string { return []string{typeGif} } @@ -110,7 +106,7 @@ func EncoderForType(fileType string) (Encoder, error) { case typeGif: return GifEncoder{}, nil default: - return nil, ErrNoEncoderForType + return nil, errors.ErrNoEncoderForType } } diff --git a/services/thumbnails/pkg/thumbnail/generator.go b/services/thumbnails/pkg/thumbnail/generator.go index 80796ad7ce..177aad17a4 100644 --- a/services/thumbnails/pkg/thumbnail/generator.go +++ b/services/thumbnails/pkg/thumbnail/generator.go @@ -8,11 +8,12 @@ import ( "strings" "github.com/kovidgoyal/imaging" + "github.com/owncloud/ocis/v2/services/thumbnails/pkg/errors" ) // Generator generates a web friendly file version. type Generator interface { - Generate(image.Rectangle, interface{}, Processor) (interface{}, error) + Generate(size image.Rectangle, img interface{}, processor Processor) (interface{}, error) } // SimpleGenerator is the default image generator and is used for all image types expect gif. @@ -22,7 +23,7 @@ type SimpleGenerator struct{} func (g SimpleGenerator) Generate(size image.Rectangle, img interface{}, processor Processor) (interface{}, error) { m, ok := img.(image.Image) if !ok { - return nil, ErrInvalidType + return nil, errors.ErrInvalidType } return processor.Process(m, size.Dx(), size.Dy(), imaging.Lanczos), nil @@ -37,7 +38,7 @@ func (g GifGenerator) Generate(size image.Rectangle, img interface{}, processor m, ok := img.(*gif.GIF) if !ok { - return nil, ErrInvalidType + return nil, errors.ErrInvalidType } // Create a new RGBA image to hold the incremental frames. srcX, srcY := m.Config.Width, m.Config.Height @@ -80,6 +81,6 @@ func GeneratorForType(fileType string) (Generator, error) { case typeGif: return GifGenerator{}, nil default: - return nil, ErrNoEncoderForType + return nil, errors.ErrNoEncoderForType } } diff --git a/services/thumbnails/pkg/thumbnail/imgsource/cs3.go b/services/thumbnails/pkg/thumbnail/imgsource/cs3.go index d17648e5f3..082ed60b0c 100644 --- a/services/thumbnails/pkg/thumbnail/imgsource/cs3.go +++ b/services/thumbnails/pkg/thumbnail/imgsource/cs3.go @@ -15,13 +15,13 @@ import ( "github.com/cs3org/reva/v2/pkg/rhttp" "github.com/cs3org/reva/v2/pkg/storagespace" "github.com/owncloud/ocis/v2/services/thumbnails/pkg/config" - "github.com/pkg/errors" + "github.com/owncloud/ocis/v2/services/thumbnails/pkg/errors" "google.golang.org/grpc/metadata" ) const ( - // "github.com/cs3org/reva/v2/internal/http/services/datagateway" is internal so we redeclare it here // TokenTransportHeader holds the header key for the reva transfer token + // "github.com/cs3org/reva/v2/internal/http/services/datagateway" is internal so we redeclare it here TokenTransportHeader = "X-Reva-Transfer" ) @@ -44,7 +44,7 @@ func NewCS3Source(cfg config.Thumbnail, gatewaySelector pool.Selectable[gateway. func (s CS3) Get(ctx context.Context, path string) (io.ReadCloser, error) { auth, ok := ContextGetAuthorization(ctx) if !ok { - return nil, errors.New("cs3source: authorization missing") + return nil, errors.ErrCS3AuthorizationMissing } ref, err := storagespace.ParseReference(path) if err != nil { @@ -66,18 +66,18 @@ func (s CS3) Get(ctx context.Context, path string) (io.ReadCloser, error) { return nil, err } - if rsp.Status.Code != rpc.Code_CODE_OK { - return nil, fmt.Errorf("could not load image: %s", rsp.Status.Message) + if rsp.GetStatus().GetCode() != rpc.Code_CODE_OK { + return nil, fmt.Errorf("could not load image: %s", rsp.GetStatus().GetMessage()) } var ep, tk string - for _, p := range rsp.Protocols { - if p.Protocol == "spaces" { - ep, tk = p.DownloadEndpoint, p.Token + for _, p := range rsp.GetProtocols() { + if p.GetProtocol() == "spaces" { + ep, tk = p.GetDownloadEndpoint(), p.GetToken() break } } - if (ep == "" || tk == "") && len(rsp.Protocols) > 0 { - ep, tk = rsp.Protocols[0].DownloadEndpoint, rsp.Protocols[0].Token + if (ep == "" || tk == "") && len(rsp.GetProtocols()) > 0 { + ep, tk = rsp.GetProtocols()[0].GetDownloadEndpoint(), rsp.GetProtocols()[0].GetToken() } httpReq, err := rhttp.NewRequest(ctx, "GET", ep, nil) @@ -93,7 +93,7 @@ func (s CS3) Get(ctx context.Context, path string) (io.ReadCloser, error) { } client := &http.Client{} - resp, err := client.Do(httpReq) // nolint:bodyclose + resp, err := client.Do(httpReq) if err != nil { return nil, err } diff --git a/services/thumbnails/pkg/thumbnail/imgsource/webdav.go b/services/thumbnails/pkg/thumbnail/imgsource/webdav.go index 6e02bc0465..9dda9dd8a1 100644 --- a/services/thumbnails/pkg/thumbnail/imgsource/webdav.go +++ b/services/thumbnails/pkg/thumbnail/imgsource/webdav.go @@ -44,7 +44,7 @@ func (s WebDav) Get(ctx context.Context, url string) (io.ReadCloser, error) { } client := &http.Client{} - resp, err := client.Do(req) // nolint:bodyclose + resp, err := client.Do(req) if err != nil { return nil, errors.Wrapf(err, `could not get the image "%s"`, url) } diff --git a/services/thumbnails/pkg/thumbnail/storage/filesystem.go b/services/thumbnails/pkg/thumbnail/storage/filesystem.go index dd18e88f57..0188fc8b43 100644 --- a/services/thumbnails/pkg/thumbnail/storage/filesystem.go +++ b/services/thumbnails/pkg/thumbnail/storage/filesystem.go @@ -31,6 +31,7 @@ type FileSystem struct { logger log.Logger } +// Stat returns if a file for the given key exists on the filesystem func (s FileSystem) Stat(key string) bool { img := filepath.Join(s.root, filesDir, key) if _, err := os.Stat(img); err != nil { @@ -39,6 +40,7 @@ func (s FileSystem) Stat(key string) bool { return true } +// Get returns the file content for the given key func (s FileSystem) Get(key string) ([]byte, error) { img := filepath.Join(s.root, filesDir, key) content, err := os.ReadFile(img) @@ -51,6 +53,7 @@ func (s FileSystem) Get(key string) ([]byte, error) { return content, nil } +// Put stores image data in the file system for the given key func (s FileSystem) Put(key string, img []byte) error { imgPath := filepath.Join(s.root, filesDir, key) dir := filepath.Dir(imgPath) diff --git a/services/thumbnails/pkg/thumbnail/storage/inmemory.go b/services/thumbnails/pkg/thumbnail/storage/inmemory.go deleted file mode 100644 index 1768318518..0000000000 --- a/services/thumbnails/pkg/thumbnail/storage/inmemory.go +++ /dev/null @@ -1,50 +0,0 @@ -package storage - -import ( - "strings" -) - -// NewInMemoryStorage creates a new InMemory instance. -func NewInMemoryStorage() InMemory { - return InMemory{ - store: make(map[string][]byte), - } -} - -// InMemory represents an in memory storage for thumbnails -// Can be used during development -type InMemory struct { - store map[string][]byte -} - -func (s InMemory) Stat(key string) bool { - _, exists := s.store[key] - return exists -} - -// Get loads the thumbnail from memory. -func (s InMemory) Get(key string) ([]byte, error) { - return s.store[key], nil -} - -// Set stores the thumbnail in memory. -func (s InMemory) Put(key string, thumbnail []byte) error { - s.store[key] = thumbnail - return nil -} - -// BuildKey generates a unique key to store and retrieve the thumbnail. -func (s InMemory) BuildKey(r Request) string { - parts := []string{ - r.Checksum, - r.Resolution.String(), - } - - if r.Characteristic != "" { - parts = append(parts, r.Characteristic) - } - - parts = append(parts, strings.Join(r.Types, ",")) - - return strings.Join(parts, "+") -} diff --git a/services/thumbnails/pkg/thumbnail/storage/inmemory_test.go b/services/thumbnails/pkg/thumbnail/storage/inmemory_test.go deleted file mode 100644 index 9f33a6ae49..0000000000 --- a/services/thumbnails/pkg/thumbnail/storage/inmemory_test.go +++ /dev/null @@ -1,65 +0,0 @@ -package storage_test - -import ( - "image" - "testing" - - tAssert "github.com/stretchr/testify/assert" - - "github.com/owncloud/ocis/v2/services/thumbnails/pkg/thumbnail/storage" -) - -func TestInMemory_BuildKey(t *testing.T) { - tests := []struct { - r storage.Request - want string - }{ - { - r: storage.Request{ - Checksum: "cs", - Types: []string{"png", "jpg"}, - Resolution: image.Rectangle{ - Min: image.Point{ - X: 1, - Y: 2, - }, - Max: image.Point{ - X: 3, - Y: 4, - }, - }, - Characteristic: "", - }, - want: "cs+(1,2)-(3,4)+png,jpg", - }, - { - r: storage.Request{ - Checksum: "cs", - Types: []string{"png", "jpg"}, - Resolution: image.Rectangle{ - Min: image.Point{ - X: 1, - Y: 2, - }, - Max: image.Point{ - X: 3, - Y: 4, - }, - }, - Characteristic: "fill", - }, - want: "cs+(1,2)-(3,4)+fill+png,jpg", - }, - } - - s := storage.InMemory{} - assert := tAssert.New(t) - - for _, tt := range tests { - tt := tt - t.Run("", func(t *testing.T) { - assert.Equal(s.BuildKey(tt.r), tt.want) - }) - } - -} diff --git a/services/thumbnails/pkg/thumbnail/storage/storage.go b/services/thumbnails/pkg/thumbnail/storage/storage.go index 591b05bdc1..143ab69bc4 100644 --- a/services/thumbnails/pkg/thumbnail/storage/storage.go +++ b/services/thumbnails/pkg/thumbnail/storage/storage.go @@ -16,7 +16,7 @@ type Request struct { // The resolution of the thumbnail Resolution image.Rectangle // Characteristic defines the different image characteristics, - // for example, if its scaled up to fit in the bounding box or not, + // for example, if it's scaled up to fit in the bounding box or not, // is it a chroma version of the image, and so on... // the main propose for this is to be able to differentiate between images which have // the same resolution but different characteristics. @@ -25,8 +25,8 @@ type Request struct { // Storage defines the interface for a thumbnail store. type Storage interface { - Stat(string) bool - Get(string) ([]byte, error) - Put(string, []byte) error - BuildKey(Request) string + Stat(key string) bool + Get(key string) ([]byte, error) + Put(key string, img []byte) error + BuildKey(r Request) string } diff --git a/services/thumbnails/pkg/thumbnail/thumbnail.go b/services/thumbnails/pkg/thumbnail/thumbnail.go index 2871f1b8fc..48ffab36bd 100644 --- a/services/thumbnails/pkg/thumbnail/thumbnail.go +++ b/services/thumbnails/pkg/thumbnail/thumbnail.go @@ -7,11 +7,12 @@ import ( "mime" "github.com/owncloud/ocis/v2/ocis-pkg/log" + "github.com/owncloud/ocis/v2/services/thumbnails/pkg/errors" "github.com/owncloud/ocis/v2/services/thumbnails/pkg/thumbnail/storage" ) var ( - // SupportedMimeTypes contains a all mimetypes which are supported by the thumbnailer. + // SupportedMimeTypes contains an all mimetypes which are supported by the thumbnailer. SupportedMimeTypes = map[string]struct{}{ "image/png": {}, "image/jpg": {}, @@ -28,7 +29,7 @@ var ( } ) -// Request bundles information needed to generate a thumbnail for afile +// Request bundles information needed to generate a thumbnail for a file type Request struct { Resolution image.Rectangle Encoder Encoder @@ -41,37 +42,48 @@ type Request struct { type Manager interface { // Generate creates a thumbnail and stores it. // The function returns a key with which the actual file can be retrieved. - Generate(Request, interface{}) (string, error) + Generate(r Request, img interface{}) (string, error) // CheckThumbnail checks if a thumbnail with the requested attributes exists. // The function will return a status if the file exists and the key to the file. - CheckThumbnail(Request) (string, bool) + CheckThumbnail(r Request) (string, bool) // GetThumbnail will load the thumbnail from the storage and return its content. GetThumbnail(key string) ([]byte, error) } // NewSimpleManager creates a new instance of SimpleManager -func NewSimpleManager(resolutions Resolutions, storage storage.Storage, logger log.Logger) SimpleManager { +func NewSimpleManager(resolutions Resolutions, storage storage.Storage, logger log.Logger, maxInputWidth, maxInputHeight int) SimpleManager { return SimpleManager{ - storage: storage, - logger: logger, - resolutions: resolutions, + storage: storage, + logger: logger, + resolutions: resolutions, + maxDimension: image.Point{X: maxInputWidth, Y: maxInputHeight}, } } // SimpleManager is a simple implementation of Manager type SimpleManager struct { - storage storage.Storage - logger log.Logger - resolutions Resolutions + storage storage.Storage + logger log.Logger + resolutions Resolutions + maxDimension image.Point } +// Generate creates a thumbnail and stores it func (s SimpleManager) Generate(r Request, img interface{}) (string, error) { var match image.Rectangle + var inputDimensions image.Rectangle switch m := img.(type) { case *gif.GIF: match = s.resolutions.ClosestMatch(r.Resolution, m.Image[0].Bounds()) + inputDimensions = m.Image[0].Bounds() case image.Image: match = s.resolutions.ClosestMatch(r.Resolution, m.Bounds()) + inputDimensions = m.Bounds() + } + + // validate max input image dimensions - 6016x4000 + if inputDimensions.Size().X > s.maxDimension.X || inputDimensions.Size().Y > s.maxDimension.Y { + return "", errors.ErrImageTooLarge } thumbnail, err := r.Generator.Generate(match, img, r.Processor) @@ -92,11 +104,13 @@ func (s SimpleManager) Generate(r Request, img interface{}) (string, error) { return k, nil } +// CheckThumbnail checks if a thumbnail with the requested attributes exists. func (s SimpleManager) CheckThumbnail(r Request) (string, bool) { k := s.storage.BuildKey(mapToStorageRequest(r)) return k, s.storage.Stat(k) } +// GetThumbnail will load the thumbnail from the storage and return its content. func (s SimpleManager) GetThumbnail(key string) ([]byte, error) { return s.storage.Get(key) } diff --git a/services/thumbnails/pkg/thumbnail/thumbnail_test.go b/services/thumbnails/pkg/thumbnail/thumbnail_test.go index fc4c979642..2a046e15f5 100644 --- a/services/thumbnails/pkg/thumbnail/thumbnail_test.go +++ b/services/thumbnails/pkg/thumbnail/thumbnail_test.go @@ -1,8 +1,12 @@ package thumbnail import ( + "github.com/owncloud/ocis/v2/services/thumbnails/pkg/errors" + "github.com/owncloud/ocis/v2/services/thumbnails/pkg/preprocessor" + "github.com/stretchr/testify/assert" "image" "os" + "path" "path/filepath" "testing" @@ -17,11 +21,11 @@ type NoOpManager struct { storage.Storage } -func (m NoOpManager) BuildKey(r storage.Request) string { +func (m NoOpManager) BuildKey(_ storage.Request) string { return "" } -func (m NoOpManager) Set(username, key string, thumbnail []byte) error { +func (m NoOpManager) Set(_, _ string, _ []byte) error { return nil } @@ -31,6 +35,8 @@ func BenchmarkGet(b *testing.B) { Resolutions{}, NoOpManager{}, log.NewLogger(), + 6016, + 4000, ) res, _ := ParseResolution("32x32") @@ -126,10 +132,59 @@ func TestPrepareRequest(t *testing.T) { return } - // func's are not reflactable, ignore + // funcs are not reflactable, ignore if diff := cmp.Diff(tt.want, got, cmpopts.IgnoreFields(Request{}, "Processor")); diff != "" { t.Errorf("PrepareRequest(): %v", diff) } }) } } + +func TestPreviewGenerationTooBigImage(t *testing.T) { + tests := []struct { + name string + fileName string + mimeType string + }{ + {name: "png", mimeType: "image/png", fileName: "../../testdata/oc.png"}, + {name: "gif", mimeType: "image/gif", fileName: "../../testdata/oc.gif"}, + {name: "ggs", mimeType: "application/vnd.geogebra.slides", fileName: "../../testdata/test.ggs"}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + sut := NewSimpleManager( + Resolutions{}, + NoOpManager{}, + log.NewLogger(), + 1024, + 768, + ) + + res, _ := ParseResolution("32x32") + req := Request{ + Resolution: res, + Checksum: "1872ade88f3013edeb33decd74a4f947", + } + cwd, _ := os.Getwd() + p := filepath.Join(cwd, tt.fileName) + f, _ := os.Open(p) + defer f.Close() + + preproc := preprocessor.ForType(tt.mimeType, nil) + convert, err := preproc.Convert(f) + if err != nil { + return + } + + ext := path.Ext(tt.fileName) + req.Encoder, _ = EncoderForType(ext) + generate, err := sut.Generate(req, convert) + if err != nil { + return + } + assert.ErrorIs(t, err, errors.ErrImageTooLarge) + assert.Equal(t, "", generate) + }) + } +} diff --git a/services/thumbnails/testdata/oc.gif b/services/thumbnails/testdata/oc.gif new file mode 100644 index 0000000000000000000000000000000000000000..e4f4e9e28b22012879360d1c6ac749823849ee23 GIT binary patch literal 25602 zcmeEt`8!naANM(DR)aCtM#KmiYa>~sM%mIfN=Vd{ELkTB6@AXwm1VRbDMPkwO|qwq zvL$M=B}+|Z4~->jZ8K+tfI`vrCIqLcEPjU;)=qu>L*WY zN}s=YR`sg9rlGu=TUpmw`>Oc`xAoQQx360|n%=%|Y3H@S?|%QW??ZP#ulrM1@4%;l zFMXefH~D$+YyaTa&qMryVgBHkZ^K`IY{oC&M!tR@-HiP2qu+jveIFU$j6X&vM#d&a z$0kR|H#s#jHaR}|b8O=0#N_nE)b!*gf6h$(oc%dH`*Y^k^vtiRpEEPFf=$i}=6(t0 zHz}Cg~U){>A+3-gXOtz?~qRvxx6jUdzVIWYtKCRJiqgS(dDV3x6cbc zWKkn=+iWWeKR&j(qv6_K@ua(e_Sp8JZDmn!vD0(!;r7bnPfzJ@Zf-kNRr2|{S9g)? zyQn%SYd( z>-l`?sC_>EfqmeX!jZa)$?n1v#b-L}Dt~_BxV?LH}6jnT1 zUo-ck?T+Tz_w}_43xzE@4FJCPG;=j4Ip?K_7{p!L*ck$T|uU@V% z3x2%II@a*&@A}HZ�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≦I^qY}NTzd^8 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 *QryK5LoRG>r{7YAJ9G;bx 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 literal 0 HcmV?d00001