Adding upstream version 2.52.6.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
a960158181
commit
6d002e9543
441 changed files with 95392 additions and 0 deletions
60
middleware/basicauth/basicauth.go
Normal file
60
middleware/basicauth/basicauth.go
Normal file
|
@ -0,0 +1,60 @@
|
|||
package basicauth
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"strings"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/gofiber/fiber/v2/utils"
|
||||
)
|
||||
|
||||
// New creates a new middleware handler
|
||||
func New(config Config) fiber.Handler {
|
||||
// Set default config
|
||||
cfg := configDefault(config)
|
||||
|
||||
// Return new handler
|
||||
return func(c *fiber.Ctx) error {
|
||||
// Don't execute middleware if Next returns true
|
||||
if cfg.Next != nil && cfg.Next(c) {
|
||||
return c.Next()
|
||||
}
|
||||
|
||||
// Get authorization header
|
||||
auth := c.Get(fiber.HeaderAuthorization)
|
||||
|
||||
// Check if the header contains content besides "basic".
|
||||
if len(auth) <= 6 || !utils.EqualFold(auth[:6], "basic ") {
|
||||
return cfg.Unauthorized(c)
|
||||
}
|
||||
|
||||
// Decode the header contents
|
||||
raw, err := base64.StdEncoding.DecodeString(auth[6:])
|
||||
if err != nil {
|
||||
return cfg.Unauthorized(c)
|
||||
}
|
||||
|
||||
// Get the credentials
|
||||
creds := utils.UnsafeString(raw)
|
||||
|
||||
// Check if the credentials are in the correct form
|
||||
// which is "username:password".
|
||||
index := strings.Index(creds, ":")
|
||||
if index == -1 {
|
||||
return cfg.Unauthorized(c)
|
||||
}
|
||||
|
||||
// Get the username and password
|
||||
username := creds[:index]
|
||||
password := creds[index+1:]
|
||||
|
||||
if cfg.Authorizer(username, password) {
|
||||
c.Locals(cfg.ContextUsername, username)
|
||||
c.Locals(cfg.ContextPassword, password)
|
||||
return c.Next()
|
||||
}
|
||||
|
||||
// Authentication failed
|
||||
return cfg.Unauthorized(c)
|
||||
}
|
||||
}
|
154
middleware/basicauth/basicauth_test.go
Normal file
154
middleware/basicauth/basicauth_test.go
Normal file
|
@ -0,0 +1,154 @@
|
|||
package basicauth
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/gofiber/fiber/v2/utils"
|
||||
|
||||
"github.com/valyala/fasthttp"
|
||||
)
|
||||
|
||||
// go test -run Test_BasicAuth_Next
|
||||
func Test_BasicAuth_Next(t *testing.T) {
|
||||
t.Parallel()
|
||||
app := fiber.New()
|
||||
app.Use(New(Config{
|
||||
Next: func(_ *fiber.Ctx) bool {
|
||||
return true
|
||||
},
|
||||
}))
|
||||
|
||||
resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil))
|
||||
utils.AssertEqual(t, nil, err)
|
||||
utils.AssertEqual(t, fiber.StatusNotFound, resp.StatusCode)
|
||||
}
|
||||
|
||||
func Test_Middleware_BasicAuth(t *testing.T) {
|
||||
t.Parallel()
|
||||
app := fiber.New()
|
||||
|
||||
app.Use(New(Config{
|
||||
Users: map[string]string{
|
||||
"john": "doe",
|
||||
"admin": "123456",
|
||||
},
|
||||
}))
|
||||
|
||||
//nolint:forcetypeassert,errcheck // TODO: Do not force-type assert
|
||||
app.Get("/testauth", func(c *fiber.Ctx) error {
|
||||
username := c.Locals("username").(string)
|
||||
password := c.Locals("password").(string)
|
||||
|
||||
return c.SendString(username + password)
|
||||
})
|
||||
|
||||
tests := []struct {
|
||||
url string
|
||||
statusCode int
|
||||
username string
|
||||
password string
|
||||
}{
|
||||
{
|
||||
url: "/testauth",
|
||||
statusCode: 200,
|
||||
username: "john",
|
||||
password: "doe",
|
||||
},
|
||||
{
|
||||
url: "/testauth",
|
||||
statusCode: 200,
|
||||
username: "admin",
|
||||
password: "123456",
|
||||
},
|
||||
{
|
||||
url: "/testauth",
|
||||
statusCode: 401,
|
||||
username: "ee",
|
||||
password: "123456",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
// Base64 encode credentials for http auth header
|
||||
creds := base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", tt.username, tt.password)))
|
||||
|
||||
req := httptest.NewRequest(fiber.MethodGet, "/testauth", nil)
|
||||
req.Header.Add("Authorization", "Basic "+creds)
|
||||
resp, err := app.Test(req)
|
||||
utils.AssertEqual(t, nil, err)
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
|
||||
utils.AssertEqual(t, nil, err)
|
||||
utils.AssertEqual(t, tt.statusCode, resp.StatusCode)
|
||||
|
||||
if tt.statusCode == 200 {
|
||||
utils.AssertEqual(t, fmt.Sprintf("%s%s", tt.username, tt.password), string(body))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// go test -v -run=^$ -bench=Benchmark_Middleware_BasicAuth -benchmem -count=4
|
||||
func Benchmark_Middleware_BasicAuth(b *testing.B) {
|
||||
app := fiber.New()
|
||||
|
||||
app.Use(New(Config{
|
||||
Users: map[string]string{
|
||||
"john": "doe",
|
||||
},
|
||||
}))
|
||||
app.Get("/", func(c *fiber.Ctx) error {
|
||||
return c.SendStatus(fiber.StatusTeapot)
|
||||
})
|
||||
|
||||
h := app.Handler()
|
||||
|
||||
fctx := &fasthttp.RequestCtx{}
|
||||
fctx.Request.Header.SetMethod(fiber.MethodGet)
|
||||
fctx.Request.SetRequestURI("/")
|
||||
fctx.Request.Header.Set(fiber.HeaderAuthorization, "basic am9objpkb2U=") // john:doe
|
||||
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
|
||||
for n := 0; n < b.N; n++ {
|
||||
h(fctx)
|
||||
}
|
||||
|
||||
utils.AssertEqual(b, fiber.StatusTeapot, fctx.Response.Header.StatusCode())
|
||||
}
|
||||
|
||||
// go test -v -run=^$ -bench=Benchmark_Middleware_BasicAuth -benchmem -count=4
|
||||
func Benchmark_Middleware_BasicAuth_Upper(b *testing.B) {
|
||||
app := fiber.New()
|
||||
|
||||
app.Use(New(Config{
|
||||
Users: map[string]string{
|
||||
"john": "doe",
|
||||
},
|
||||
}))
|
||||
app.Get("/", func(c *fiber.Ctx) error {
|
||||
return c.SendStatus(fiber.StatusTeapot)
|
||||
})
|
||||
|
||||
h := app.Handler()
|
||||
|
||||
fctx := &fasthttp.RequestCtx{}
|
||||
fctx.Request.Header.SetMethod(fiber.MethodGet)
|
||||
fctx.Request.SetRequestURI("/")
|
||||
fctx.Request.Header.Set(fiber.HeaderAuthorization, "Basic am9objpkb2U=") // john:doe
|
||||
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
|
||||
for n := 0; n < b.N; n++ {
|
||||
h(fctx)
|
||||
}
|
||||
|
||||
utils.AssertEqual(b, fiber.StatusTeapot, fctx.Response.Header.StatusCode())
|
||||
}
|
105
middleware/basicauth/config.go
Normal file
105
middleware/basicauth/config.go
Normal file
|
@ -0,0 +1,105 @@
|
|||
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
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue