lavender/auth/auth.go

104 lines
2.6 KiB
Go
Raw Normal View History

2024-09-13 15:31:40 +01:00
package auth
2024-10-06 21:30:39 +01:00
import (
"context"
"errors"
"fmt"
"github.com/1f349/lavender/database"
2024-12-06 17:07:20 +00:00
"html/template"
2024-10-06 21:30:39 +01:00
"net/http"
)
2024-09-13 15:31:40 +01:00
2024-10-06 21:30:39 +01:00
type Factor byte
const (
2024-12-06 17:07:20 +00:00
// FactorUnauthorized defines the "unauthorized" state of a session
FactorUnauthorized Factor = iota
FactorBasic
FactorExtended
FactorSudo
2024-10-06 21:30:39 +01:00
)
type Provider interface {
// Factor defines the factors potentially supported by the provider
// Some factors might be unavailable due to user preference
Factor() Factor
// Name defines a string value for the provider, useful for template switching
Name() string
2024-12-06 17:07:20 +00:00
// RenderTemplate returns HTML to embed in the page template
RenderTemplate(ctx context.Context, req *http.Request, user *database.User) (template.HTML, error)
2024-10-06 21:30:39 +01:00
// AttemptLogin processes the login request
AttemptLogin(ctx context.Context, req *http.Request, user *database.User) error
}
var (
2024-12-06 17:07:20 +00:00
// ErrRequiresBasicFactor notifies the ServeHTTP function to ask for another factor
ErrRequiresBasicFactor = errors.New("requires basic factor")
// ErrRequiresExtendedFactor is a generic error for providers which require a previous factor
ErrRequiresExtendedFactor = errors.New("requires extended factor")
ErrRequiresSudoFactor = errors.New("requires sudo factor")
// ErrUserDoesNotSupportFactor is a generic error for providers with are unable to support the user
ErrUserDoesNotSupportFactor = errors.New("user does not support factor")
)
2024-10-06 21:30:39 +01:00
type UserSafeError struct {
Display string
Code int
Internal error
}
func (e UserSafeError) Error() string {
return fmt.Sprintf("%s [%d]: %v", e.Display, e.Code, e.Internal)
}
func (e UserSafeError) Unwrap() error {
return e.Internal
}
func BasicUserSafeError(code int, message string) UserSafeError {
return UserSafeError{
Code: code,
Display: message,
Internal: errors.New(message),
}
}
func AdminSafeError(inner error) UserSafeError {
return UserSafeError{
Code: http.StatusInternalServerError,
Display: "Internal server error",
Internal: inner,
}
}
type RedirectError struct {
Target string
Code int
}
func (e RedirectError) TargetUrl() string { return e.Target }
func (e RedirectError) Error() string {
return fmt.Sprintf("redirect to '%s'", e.Target)
}
2024-10-06 21:30:39 +01:00
type lookupUserDB interface {
GetUser(ctx context.Context, subject string) (database.User, error)
2024-09-13 15:31:40 +01:00
}
2024-10-06 21:30:39 +01:00
func lookupUser(ctx context.Context, db lookupUserDB, subject string, resolvesTwoFactor bool, user *database.User) error {
getUser, err := db.GetUser(ctx, subject)
if err != nil {
return err
}
*user = getUser
if user.NeedFactor && !resolvesTwoFactor {
return ErrRequiresSecondFactor
}
return nil
2024-09-13 15:31:40 +01:00
}