1
0
mirror of https://github.com/labstack/echo.git synced 2025-01-24 03:16:14 +02:00
echo/middleware/basic_auth.go
toimtoimtoim 6ef5f77bf2 WIP: logger examples
WIP: make default logger implemented custom writer for jsonlike logs
WIP: improve examples
WIP: defaultErrorHandler use errors.As to unwrap errors. Update readme
WIP: default logger logs json, restore e.Start method
WIP: clean router.Match a bit
WIP: func types/fields have echo.Context has first element
WIP: remove yaml tags as functions etc can not be serialized anyway
WIP: change BindPathParams,BindQueryParams,BindHeaders from methods to functions and reverse arguments to be like DefaultBinder.Bind is
WIP: improved comments, logger now extracts status from error
WIP: go mod tidy
WIP: rebase with 4.5.0
WIP:
* removed todos.
* removed StartAutoTLS and StartH2CServer methods from `StartConfig`
* KeyAuth middleware errorhandler can swallow the error and resume next middleware
WIP: add RouterConfig.UseEscapedPathForMatching to use escaped path for matching request against routes
WIP: FIXMEs
WIP: upgrade golang-jwt/jwt to `v4`
WIP: refactor http methods to return RouteInfo
WIP: refactor static not creating multiple routes
WIP: refactor route and middleware adding functions not to return error directly
WIP: Use 401 for problematic/missing headers for key auth and JWT middleware (#1552, #1402).
> In summary, a 401 Unauthorized response should be used for missing or bad authentication
WIP: replace `HTTPError.SetInternal` with `HTTPError.WithInternal` so we could not mutate global error variables
WIP: add RouteInfo and RouteMatchType into Context what we could know from in middleware what route was matched and/or type of that match (200/404/405)
WIP: make notFoundHandler and methodNotAllowedHandler private. encourage that all errors be handled in Echo.HTTPErrorHandler
WIP: server cleanup ideas
WIP: routable.ForGroup
WIP: note about logger middleware
WIP: bind should not default values on second try. use crypto rand for better randomness
WIP: router add route as interface and returns info as interface
WIP: improve flaky test (remains still flaky)
WIP: add notes about bind default values
WIP: every route can have their own path params names
WIP: routerCreator and different tests
WIP: different things
WIP: remove route implementation
WIP: support custom method types
WIP: extractor tests
WIP: v5.0.x proposal
over v4.4.0
2021-10-02 18:36:42 +03:00

106 lines
2.9 KiB
Go

package middleware
import (
"bytes"
"encoding/base64"
"errors"
"fmt"
"strconv"
"strings"
"github.com/labstack/echo/v4"
)
// BasicAuthConfig defines the config for BasicAuthWithConfig middleware.
type BasicAuthConfig struct {
// Skipper defines a function to skip middleware.
Skipper Skipper
// Validator is a function to validate BasicAuthWithConfig credentials. Note: if request contains multiple basic auth headers
// this function would be called once for each header until first valid result is returned
// Required.
Validator BasicAuthValidator
// Realm is a string to define realm attribute of BasicAuthWithConfig.
// Default value "Restricted".
Realm string
}
// BasicAuthValidator defines a function to validate BasicAuthWithConfig credentials.
type BasicAuthValidator func(c echo.Context, user string, password string) (bool, error)
const (
basic = "basic"
defaultRealm = "Restricted"
)
// BasicAuth returns an BasicAuth middleware.
//
// For valid credentials it calls the next handler.
// For missing or invalid credentials, it sends "401 - Unauthorized" response.
func BasicAuth(fn BasicAuthValidator) echo.MiddlewareFunc {
return BasicAuthWithConfig(BasicAuthConfig{Validator: fn})
}
// BasicAuthWithConfig returns an BasicAuthWithConfig middleware with config.
func BasicAuthWithConfig(config BasicAuthConfig) echo.MiddlewareFunc {
return toMiddlewareOrPanic(config)
}
// ToMiddleware converts BasicAuthConfig to middleware or returns an error for invalid configuration
func (config BasicAuthConfig) ToMiddleware() (echo.MiddlewareFunc, error) {
if config.Validator == nil {
return nil, errors.New("echo basic-auth middleware requires a validator function")
}
if config.Skipper == nil {
config.Skipper = DefaultSkipper
}
if config.Realm == "" {
config.Realm = defaultRealm
}
return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
if config.Skipper(c) {
return next(c)
}
var lastError error
l := len(basic)
for _, auth := range c.Request().Header[echo.HeaderAuthorization] {
if !(len(auth) > l+1 && strings.EqualFold(auth[:l], basic)) {
continue
}
b, errDecode := base64.StdEncoding.DecodeString(auth[l+1:])
if errDecode != nil {
lastError = fmt.Errorf("invalid basic auth value: %w", errDecode)
continue
}
idx := bytes.IndexByte(b, ':')
if idx >= 0 {
valid, errValidate := config.Validator(c, string(b[:idx]), string(b[idx+1:]))
if errValidate != nil {
lastError = errValidate
} else if valid {
return next(c)
}
}
}
if lastError != nil {
return lastError
}
realm := defaultRealm
if config.Realm != defaultRealm {
realm = strconv.Quote(config.Realm)
}
// Need to return `401` for browsers to pop-up login box.
c.Response().Header().Set(echo.HeaderWWWAuthenticate, basic+" realm="+realm)
return echo.ErrUnauthorized
}
}, nil
}