Benjamin Grosse b1850834a3
fix: don't constrain middlewares' context-keys to strings 🐛 (#2751)
* Revert "Revert "🐛 requestid.Config.ContextKey is interface{} (#2369)" (#2742)"

This reverts commit 28be17f929cfa7d3c27dd292fc3956f2f9882e22.

* fix: request ContextKey default value condition

Should check for `nil` since it is `any`.

* fix: don't constrain middlewares' context-keys to strings

`context` recommends using "unexported type" as context keys to avoid
collisions https://pkg.go.dev/github.com/gofiber/fiber/v2#Ctx.Locals.

The official go blog also recommends this https://go.dev/blog/context.

`fiber.Ctx.Locals(key any, value any)` correctly allows consumers to
use unexported types or e.g. strings.

But some fiber middlewares constrain their context-keys to `string` in
their "default config structs", making it impossible to use unexported
types.

This PR removes the `string` _constraint_ from all middlewares, allowing
to now use unexported types as per the official guidelines. However
the default value is still a string, so it's not a breaking change, and
anyone still using strings as context keys is not affected.
2023-12-12 14:55:29 +01:00

106 lines
2.7 KiB
Go

package basicauth
import (
"crypto/subtle"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/utils"
)
// Config defines the config for middleware.
type Config struct {
// Next defines a function to skip this middleware when returned true.
//
// Optional. Default: nil
Next func(c *fiber.Ctx) bool
// Users defines the allowed credentials
//
// Required. Default: map[string]string{}
Users map[string]string
// Realm is a string to define realm attribute of BasicAuth.
// the realm identifies the system to authenticate against
// and can be used by clients to save credentials
//
// Optional. Default: "Restricted".
Realm string
// Authorizer defines a function you can pass
// to check the credentials however you want.
// It will be called with a username and password
// and is expected to return true or false to indicate
// that the credentials were approved or not.
//
// Optional. Default: nil.
Authorizer func(string, string) bool
// Unauthorized defines the response body for unauthorized responses.
// By default it will return with a 401 Unauthorized and the correct WWW-Auth header
//
// Optional. Default: nil
Unauthorized fiber.Handler
// ContextUser is the key to store the username in Locals
//
// Optional. Default: "username"
ContextUsername interface{}
// ContextPass is the key to store the password in Locals
//
// Optional. Default: "password"
ContextPassword interface{}
}
// ConfigDefault is the default config
var ConfigDefault = Config{
Next: nil,
Users: map[string]string{},
Realm: "Restricted",
Authorizer: nil,
Unauthorized: nil,
ContextUsername: "username",
ContextPassword: "password",
}
// Helper function to set default values
func configDefault(config ...Config) Config {
// Return default config if nothing provided
if len(config) < 1 {
return ConfigDefault
}
// Override default config
cfg := config[0]
// Set default values
if cfg.Next == nil {
cfg.Next = ConfigDefault.Next
}
if cfg.Users == nil {
cfg.Users = ConfigDefault.Users
}
if cfg.Realm == "" {
cfg.Realm = ConfigDefault.Realm
}
if cfg.Authorizer == nil {
cfg.Authorizer = func(user, pass string) bool {
userPwd, exist := cfg.Users[user]
return exist && subtle.ConstantTimeCompare(utils.UnsafeBytes(userPwd), utils.UnsafeBytes(pass)) == 1
}
}
if cfg.Unauthorized == nil {
cfg.Unauthorized = func(c *fiber.Ctx) error {
c.Set(fiber.HeaderWWWAuthenticate, "basic realm="+cfg.Realm)
return c.SendStatus(fiber.StatusUnauthorized)
}
}
if cfg.ContextUsername == nil {
cfg.ContextUsername = ConfigDefault.ContextUsername
}
if cfg.ContextPassword == nil {
cfg.ContextPassword = ConfigDefault.ContextPassword
}
return cfg
}