garm/vendor/github.com/go-macaroon-bakery/macaroon-bakery/v3/httpbakery/checkers.go
Gabriel Adrian Samfira c61b7fd268
Update go modules
Signed-off-by: Gabriel Adrian Samfira <gsamfira@cloudbasesolutions.com>
2023-03-12 16:22:37 +02:00

157 lines
4.5 KiB
Go

package httpbakery
import (
"context"
"net"
"net/http"
"gopkg.in/errgo.v1"
"github.com/go-macaroon-bakery/macaroon-bakery/v3/bakery/checkers"
)
type httpRequestKey struct{}
// ContextWithRequest returns the context with information from the
// given request attached as context. This is used by the httpbakery
// checkers (see RegisterCheckers for details).
func ContextWithRequest(ctx context.Context, req *http.Request) context.Context {
return context.WithValue(ctx, httpRequestKey{}, req)
}
func requestFromContext(ctx context.Context) *http.Request {
req, _ := ctx.Value(httpRequestKey{}).(*http.Request)
return req
}
const (
// CondClientIPAddr holds the first party caveat condition
// that checks a client's IP address.
CondClientIPAddr = "client-ip-addr"
// CondClientOrigin holds the first party caveat condition that
// checks a client's origin header.
CondClientOrigin = "origin"
)
// CheckersNamespace holds the URI of the HTTP checkers schema.
const CheckersNamespace = "http"
var allCheckers = map[string]checkers.Func{
CondClientIPAddr: ipAddrCheck,
CondClientOrigin: clientOriginCheck,
}
// RegisterCheckers registers all the HTTP checkers with the given checker.
// Current checkers include:
//
// client-ip-addr <ip-address>
//
// The client-ip-addr caveat checks that the HTTP request has
// the given remote IP address.
//
// origin <name>
//
// The origin caveat checks that the HTTP Origin header has
// the given value.
func RegisterCheckers(c *checkers.Checker) {
c.Namespace().Register(CheckersNamespace, "http")
for cond, check := range allCheckers {
c.Register(cond, CheckersNamespace, check)
}
}
// NewChecker returns a new checker with the standard
// and HTTP checkers registered in it.
func NewChecker() *checkers.Checker {
c := checkers.New(nil)
RegisterCheckers(c)
return c
}
// ipAddrCheck implements the IP client address checker
// for an HTTP request.
func ipAddrCheck(ctx context.Context, cond, args string) error {
req := requestFromContext(ctx)
if req == nil {
return errgo.Newf("no IP address found in context")
}
ip := net.ParseIP(args)
if ip == nil {
return errgo.Newf("cannot parse IP address in caveat")
}
if req.RemoteAddr == "" {
return errgo.Newf("client has no remote address")
}
reqIP, err := requestIPAddr(req)
if err != nil {
return errgo.Mask(err)
}
if !reqIP.Equal(ip) {
return errgo.Newf("client IP address mismatch, got %s", reqIP)
}
return nil
}
// clientOriginCheck implements the Origin header checker
// for an HTTP request.
func clientOriginCheck(ctx context.Context, cond, args string) error {
req := requestFromContext(ctx)
if req == nil {
return errgo.Newf("no origin found in context")
}
// Note that web browsers may not provide the origin header when it's
// not a cross-site request with a GET method. There's nothing we
// can do about that, so just allow all requests with an empty origin.
if reqOrigin := req.Header.Get("Origin"); reqOrigin != "" && reqOrigin != args {
return errgo.Newf("request has invalid Origin header; got %q", reqOrigin)
}
return nil
}
// SameClientIPAddrCaveat returns a caveat that will check that
// the remote IP address is the same as that in the given HTTP request.
func SameClientIPAddrCaveat(req *http.Request) checkers.Caveat {
if req.RemoteAddr == "" {
return checkers.ErrorCaveatf("client has no remote IP address")
}
ip, err := requestIPAddr(req)
if err != nil {
return checkers.ErrorCaveatf("%v", err)
}
return ClientIPAddrCaveat(ip)
}
// ClientIPAddrCaveat returns a caveat that will check whether the
// client's IP address is as provided.
func ClientIPAddrCaveat(addr net.IP) checkers.Caveat {
if len(addr) != net.IPv4len && len(addr) != net.IPv6len {
return checkers.ErrorCaveatf("bad IP address %d", []byte(addr))
}
return httpCaveat(CondClientIPAddr, addr.String())
}
// ClientOriginCaveat returns a caveat that will check whether the
// client's Origin header in its HTTP request is as provided.
func ClientOriginCaveat(origin string) checkers.Caveat {
return httpCaveat(CondClientOrigin, origin)
}
func httpCaveat(cond, arg string) checkers.Caveat {
return checkers.Caveat{
Condition: checkers.Condition(cond, arg),
Namespace: CheckersNamespace,
}
}
func requestIPAddr(req *http.Request) (net.IP, error) {
reqHost, _, err := net.SplitHostPort(req.RemoteAddr)
if err != nil {
return nil, errgo.Newf("cannot parse host port in remote address: %v", err)
}
ip := net.ParseIP(reqHost)
if ip == nil {
return nil, errgo.Newf("invalid IP address in remote address %q", req.RemoteAddr)
}
return ip, nil
}