Skip to content

Commit a5231b0

Browse files
committed
refactor: update parameter order in image and video context processing functions
1 parent 32d3825 commit a5231b0

4 files changed

Lines changed: 265 additions & 227 deletions

File tree

routes/image.go

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ func handleImageRequest(logger *zap.Logger, cache *ristretto.Cache[string, Cache
3838
pathParams := c.Params("*")
3939
logger.Info("image request received", zap.String("pathParams", pathParams))
4040

41-
ok, status, err, params := validation.ProcessImageContextFromPath(logger, pathParams, config)
41+
ok, status, params, err := validation.ProcessImageContextFromPath(logger, pathParams, config)
4242
if !ok {
4343
return c.Status(status).SendString(err.Error())
4444
}
@@ -240,6 +240,7 @@ func processImageData(c *fiber.Ctx, logger *zap.Logger, cache *ristretto.Cache[s
240240
//#region handleImageUpload
241241

242242
// handleImageUpload processes image upload requests with path parameters
243+
// Requires: token (in path parameters), optional location and signature for S3 upload
243244
func handleImageUpload(logger *zap.Logger, cache *ristretto.Cache[string, CacheValue], config *config.Config, counters *metrics.Metrics, s3cache *S3Cache) fiber.Handler {
244245
return func(c *fiber.Ctx) error {
245246
logger.Info("image upload request received")
@@ -256,11 +257,19 @@ func handleImageUpload(logger *zap.Logger, cache *ristretto.Cache[string, CacheV
256257
}
257258
defer imageFile.Close()
258259

259-
ok, status, err, params := validation.ProcessImageUploadFromPath(logger, pathParams, config)
260+
ok, status, params, err := validation.ProcessImageUploadFromPath(logger, pathParams, config)
260261
if !ok {
261262
return c.Status(status).SendString(err.Error())
262263
}
263264

265+
// Check if S3 is required and enabled (when CustomObjectKey is provided)
266+
if params.CustomObjectKey != "" {
267+
if s3cache == nil || !s3cache.Enabled || s3cache.Client == nil {
268+
logger.Error("S3 storage is not enabled or configured")
269+
return c.Status(fiber.StatusServiceUnavailable).SendString("image upload service unavailable")
270+
}
271+
}
272+
264273
contentType := body.Header.Get("Content-Type")
265274
if contentType == "" {
266275
return c.Status(fiber.StatusForbidden).SendString("no content type received")

routes/video.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ func handleVideoPreviewRequest(logger *zap.Logger, cache *ristretto.Cache[string
5151
pathParams := c.Params("*")
5252
logger.Info("video preview request received", zap.String("pathParams", pathParams))
5353

54-
ok, status, err, params := validation.ProcessImageContextFromPath(logger, pathParams, config)
54+
ok, status, params, err := validation.ProcessImageContextFromPath(logger, pathParams, config)
5555
if !ok {
5656
return c.Status(status).SendString(err.Error())
5757
}
@@ -74,7 +74,7 @@ func handleVideoProxyRequest(logger *zap.Logger, cache *ristretto.Cache[string,
7474
pathParams := c.Params("*")
7575
logger.Info("video proxy request received", zap.String("pathParams", pathParams))
7676

77-
ok, status, err, params := validation.ProcessImageContextFromPath(logger, pathParams, config)
77+
ok, status, params, err := validation.ProcessImageContextFromPath(logger, pathParams, config)
7878
if !ok {
7979
return c.Status(status).SendString(err.Error())
8080
}

validation/validation.go

Lines changed: 64 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -216,42 +216,73 @@ func sanitizeLocation(loc string) (string, error) {
216216
}
217217

218218
// ProcessImageUploadFromPath processes image upload parameters from path
219-
func ProcessImageUploadFromPath(logger *zap.Logger, pathParams string, config *config.Config) (bool, int, error, *ImageContext) {
219+
// Validation: Either validate token OR if location and signature provided, validate signature
220+
func ProcessImageUploadFromPath(logger *zap.Logger, pathParams string, config *config.Config) (bool, int, *ImageContext, error) {
220221
params, err := ParsePathParams(pathParams)
221222
if err != nil {
222-
return false, fiber.StatusBadRequest, fmt.Errorf("invalid path parameters: %w", err), nil
223+
return false, fiber.StatusBadRequest, nil, fmt.Errorf("invalid path parameters: %w", err)
223224
}
224225

225-
if params.Token != config.Token {
226-
return false, fiber.StatusForbidden, fmt.Errorf("invalid token"), nil
226+
// Handle optional S3 location with signature validation (if provided, use signature validation instead of token)
227+
var customObjectKey string
228+
if params.Location != "" && params.Signature != "" {
229+
// Signature validation mode for S3 upload
230+
if config.HmacKey == "" {
231+
return false, fiber.StatusInternalServerError, nil, fmt.Errorf("hmac key not configured")
232+
}
233+
234+
// Decode location
235+
decodedLocation, err := DecodeBase64URL(params.Location)
236+
if err != nil {
237+
return false, fiber.StatusBadRequest, nil, fmt.Errorf("invalid location encoding: %w", err)
238+
}
239+
240+
// Sanitize location
241+
sanitized, err := sanitizeLocation(decodedLocation)
242+
if err != nil {
243+
return false, fiber.StatusBadRequest, nil, fmt.Errorf("invalid location: %w", err)
244+
}
245+
246+
// Validate signature: HMAC(location)
247+
if !compareHmacForMessage(sanitized, params.Signature, config.HmacKey) {
248+
return false, fiber.StatusForbidden, nil, fmt.Errorf("invalid signature")
249+
}
250+
251+
customObjectKey = sanitized
252+
} else {
253+
// Token validation mode (default)
254+
if params.Token != config.Token {
255+
return false, fiber.StatusForbidden, nil, fmt.Errorf("invalid token")
256+
}
227257
}
228258

229259
if params.Quality < 1 || params.Quality > 100 {
230-
return false, fiber.StatusBadRequest, fmt.Errorf("quality must be between 1 and 100"), nil
260+
return false, fiber.StatusBadRequest, nil, fmt.Errorf("quality must be between 1 and 100")
231261
}
232262

233263
if params.Width > 0 && params.Height > 0 && (params.Width < 1 || params.Height < 1) {
234-
return false, fiber.StatusBadRequest, fmt.Errorf("width and height must be greater than 0"), nil
264+
return false, fiber.StatusBadRequest, nil, fmt.Errorf("width and height must be greater than 0")
235265
}
236266

237267
if params.Scale < 0 || params.Scale > 1 {
238-
return false, fiber.StatusBadRequest, fmt.Errorf("scale must be between 0 and 1"), nil
268+
return false, fiber.StatusBadRequest, nil, fmt.Errorf("scale must be between 0 and 1")
239269
}
240270

241271
// Apply default webp setting if not specified
242272
if !params.Webp && config.Webp {
243273
params.Webp = config.Webp
244274
}
245275

246-
return true, fiber.StatusOK, nil, &ImageContext{
247-
Quality: params.Quality,
248-
Width: params.Width,
249-
Height: params.Height,
250-
Scale: params.Scale,
251-
Interpolation: params.Interpolation,
252-
Webp: params.Webp,
253-
FramePosition: params.FramePosition,
254-
}
276+
return true, fiber.StatusOK, &ImageContext{
277+
Quality: params.Quality,
278+
Width: params.Width,
279+
Height: params.Height,
280+
Scale: params.Scale,
281+
Interpolation: params.Interpolation,
282+
Webp: params.Webp,
283+
FramePosition: params.FramePosition,
284+
CustomObjectKey: customObjectKey,
285+
}, nil
255286
}
256287

257288
func ProcessImageUpload(logger *zap.Logger, c *fiber.Ctx, config *config.Config) (ok bool, status int, err error, params *ImageContext) {
@@ -294,35 +325,35 @@ func ProcessImageUpload(logger *zap.Logger, c *fiber.Ctx, config *config.Config)
294325
}
295326

296327
// ProcessImageContextFromPath processes image context from path parameters
297-
func ProcessImageContextFromPath(logger *zap.Logger, pathParams string, config *config.Config) (bool, int, error, *ImageContext) {
328+
func ProcessImageContextFromPath(logger *zap.Logger, pathParams string, config *config.Config) (bool, int, *ImageContext, error) {
298329
params, err := ParsePathParams(pathParams)
299330
if err != nil {
300-
return false, fiber.StatusBadRequest, fmt.Errorf("invalid path parameters: %w", err), nil
331+
return false, fiber.StatusBadRequest, nil, fmt.Errorf("invalid path parameters: %w", err)
301332
}
302333

303334
// URL is optional if location is provided
304335
urlParam := ""
305336
if params.EncodedURL != "" {
306337
urlParam, err = DecodeURL(params.EncodedURL)
307338
if err != nil {
308-
return false, fiber.StatusBadRequest, fmt.Errorf("failed to decode URL: %w", err), nil
339+
return false, fiber.StatusBadRequest, nil, fmt.Errorf("failed to decode URL: %w", err)
309340
}
310341
}
311342

312343
// If custom location is requested, require signature and validate location
313344
customObjectKey := ""
314345
if params.Location != "" {
315346
if config.HmacKey == "" || params.Signature == "" {
316-
return false, fiber.StatusForbidden, fmt.Errorf("signature required for custom location"), nil
347+
return false, fiber.StatusForbidden, nil, fmt.Errorf("signature required for custom location")
317348
}
318349
// Expect location to be base64 URL-safe encoded
319350
decodedLocation, derr := DecodeBase64URL(params.Location)
320351
if derr != nil {
321-
return false, fiber.StatusBadRequest, derr, nil
352+
return false, fiber.StatusBadRequest, nil, derr
322353
}
323354
sanitized, serr := sanitizeLocation(decodedLocation)
324355
if serr != nil {
325-
return false, fiber.StatusBadRequest, serr, nil
356+
return false, fiber.StatusBadRequest, nil, serr
326357
}
327358

328359
// If URL is provided, sign URL|location, otherwise just sign location
@@ -334,23 +365,23 @@ func ProcessImageContextFromPath(logger *zap.Logger, pathParams string, config *
334365
}
335366

336367
if !compareHmacForMessage(signedMsg, params.Signature, config.HmacKey) {
337-
return false, fiber.StatusForbidden, fmt.Errorf("invalid signature for location"), nil
368+
return false, fiber.StatusForbidden, nil, fmt.Errorf("invalid signature for location")
338369
}
339370
customObjectKey = sanitized
340371
} else if params.Signature != "" { // normal signature over URL only
341372
if config.HmacKey == "" {
342-
return false, fiber.StatusForbidden, fmt.Errorf("hmac key is not set"), nil
373+
return false, fiber.StatusForbidden, nil, fmt.Errorf("hmac key is not set")
343374
}
344375
if urlParam == "" {
345-
return false, fiber.StatusBadRequest, fmt.Errorf("url is required when signature is provided without location"), nil
376+
return false, fiber.StatusBadRequest, nil, fmt.Errorf("url is required when signature is provided without location")
346377
}
347378
if !compareHmac(urlParam, params.Signature, config.HmacKey) {
348-
return false, fiber.StatusForbidden, fmt.Errorf("invalid signature"), nil
379+
return false, fiber.StatusForbidden, nil, fmt.Errorf("invalid signature")
349380
}
350381
} else {
351382
// Neither location nor signature provided, URL is required
352383
if urlParam == "" {
353-
return false, fiber.StatusBadRequest, fmt.Errorf("url or location is required"), nil
384+
return false, fiber.StatusBadRequest, nil, fmt.Errorf("url or location is required")
354385
}
355386
}
356387

@@ -359,29 +390,29 @@ func ProcessImageContextFromPath(logger *zap.Logger, pathParams string, config *
359390
if urlParam != "" {
360391
validOrigin, validHostname := pool.ValidateUrl(logger, urlParam, config.AllowedOrigins)
361392
if !validOrigin {
362-
return false, fiber.StatusForbidden, fmt.Errorf("url is not allowed"), nil
393+
return false, fiber.StatusForbidden, nil, fmt.Errorf("url is not allowed")
363394
}
364395
hostname = validHostname
365396
}
366397

367398
if params.Quality < 1 || params.Quality > 100 {
368-
return false, fiber.StatusBadRequest, fmt.Errorf("quality must be between 1 and 100"), nil
399+
return false, fiber.StatusBadRequest, nil, fmt.Errorf("quality must be between 1 and 100")
369400
}
370401

371402
if params.Width > 0 && params.Height > 0 && (params.Width < 1 || params.Height < 1) {
372-
return false, fiber.StatusBadRequest, fmt.Errorf("width and height must be greater than 0"), nil
403+
return false, fiber.StatusBadRequest, nil, fmt.Errorf("width and height must be greater than 0")
373404
}
374405

375406
if params.Scale < 0 || params.Scale > 1 {
376-
return false, fiber.StatusBadRequest, fmt.Errorf("scale must be between 0 and 1"), nil
407+
return false, fiber.StatusBadRequest, nil, fmt.Errorf("scale must be between 0 and 1")
377408
}
378409

379410
// Apply default webp setting if not specified
380411
if !params.Webp && config.Webp {
381412
params.Webp = config.Webp
382413
}
383414

384-
return true, fiber.StatusOK, nil, &ImageContext{
415+
return true, fiber.StatusOK, &ImageContext{
385416
Url: urlParam,
386417
Quality: params.Quality,
387418
Width: params.Width,
@@ -392,7 +423,7 @@ func ProcessImageContextFromPath(logger *zap.Logger, pathParams string, config *
392423
FramePosition: params.FramePosition,
393424
Hostname: hostname,
394425
CustomObjectKey: customObjectKey,
395-
}
426+
}, nil
396427
}
397428

398429
func ProcessImageContext(logger *zap.Logger, c *fiber.Ctx, config *config.Config) (ok bool, status int, err error, params *ImageContext) {

0 commit comments

Comments
 (0)