iris/middleware/basicauth/error.go
2020-11-24 14:58:02 +02:00

109 lines
3.3 KiB
Go

package basicauth
import (
"fmt"
"net/http"
"time"
"github.com/kataras/iris/v12/context"
)
type (
// ErrHTTPVersion is fired when Options.HTTPSOnly was enabled
// and the current request is a plain http one.
ErrHTTPVersion struct{}
// ErrCredentialsForbidden is fired when Options.MaxTries have been consumed
// by the user and the client is forbidden to retry at least for "Age" time.
ErrCredentialsForbidden struct {
Username string
Password string
Tries int
Age time.Duration
}
// ErrCredentialsMissing is fired when the authorization header is empty or malformed.
ErrCredentialsMissing struct {
Header string
AuthenticateHeader string
AuthenticateHeaderValue string
Code int
}
// ErrCredentialsInvalid is fired when the user input does not match with an existing user.
ErrCredentialsInvalid struct {
Username string
Password string
CurrentTries int
AuthenticateHeader string
AuthenticateHeaderValue string
Code int
}
// ErrCredentialsExpired is fired when the username:password combination is valid
// but the memory stored user has been expired.
ErrCredentialsExpired struct {
Username string
Password string
AuthenticateHeader string
AuthenticateHeaderValue string
Code int
}
)
func (e ErrHTTPVersion) Error() string {
return "http version not supported"
}
func (e ErrCredentialsForbidden) Error() string {
return fmt.Sprintf("credentials: forbidden <%s:%s> for <%s> after <%d> attempts", e.Username, e.Password, e.Age, e.Tries)
}
func (e ErrCredentialsMissing) Error() string {
if e.Header != "" {
return fmt.Sprintf("credentials: malformed <%s>", e.Header)
}
return "empty credentials"
}
func (e ErrCredentialsInvalid) Error() string {
return fmt.Sprintf("credentials: invalid <%s:%s> current tries <%d>", e.Username, e.Password, e.CurrentTries)
}
func (e ErrCredentialsExpired) Error() string {
return fmt.Sprintf("credentials: expired <%s:%s>", e.Username, e.Password)
}
// DefaultErrorHandler is the default error handler for the Options.ErrorHandler field.
func DefaultErrorHandler(ctx *context.Context, err error) {
switch e := err.(type) {
case ErrHTTPVersion:
ctx.StopWithStatus(http.StatusHTTPVersionNotSupported)
case ErrCredentialsForbidden:
// If a (proxy) server receives valid credentials that are inadequate to access a given resource,
// the server should respond with the 403 Forbidden status code.
// Unlike 401 Unauthorized or 407 Proxy Authentication Required, authentication is impossible for this user.
ctx.StopWithStatus(http.StatusForbidden)
case ErrCredentialsMissing:
unauthorize(ctx, e.AuthenticateHeader, e.AuthenticateHeaderValue, e.Code)
case ErrCredentialsInvalid:
unauthorize(ctx, e.AuthenticateHeader, e.AuthenticateHeaderValue, e.Code)
case ErrCredentialsExpired:
unauthorize(ctx, e.AuthenticateHeader, e.AuthenticateHeaderValue, e.Code)
default:
// This will never happen.
ctx.StopWithText(http.StatusInternalServerError, "unknown error: %v", err)
}
}
// unauthorize sends a 401 status code (or 407 if Proxy was set to true)
// which client should catch and prompt for username:password credentials.
func unauthorize(ctx *context.Context, authHeader, authHeaderValue string, code int) {
ctx.Header(authHeader, authHeaderValue)
ctx.StopWithStatus(code)
}