mirror of
https://github.com/lightninglabs/aperture.git
synced 2025-12-28 14:34:27 +01:00
To get rid of the loop dependency, we copy the test code that we rely on and fix some imports.
143 lines
4.3 KiB
Go
143 lines
4.3 KiB
Go
package lsat
|
|
|
|
import (
|
|
"encoding/base64"
|
|
"encoding/hex"
|
|
"errors"
|
|
"fmt"
|
|
"net/http"
|
|
"regexp"
|
|
|
|
"github.com/lightningnetwork/lnd/lntypes"
|
|
"gopkg.in/macaroon.v2"
|
|
)
|
|
|
|
const (
|
|
// HeaderAuthorization is the HTTP header field name that is used to
|
|
// send the LSAT by REST clients.
|
|
HeaderAuthorization = "Authorization"
|
|
|
|
// HeaderMacaroonMD is the HTTP header field name that is used to send
|
|
// the LSAT by certain REST and gRPC clients.
|
|
HeaderMacaroonMD = "Grpc-Metadata-Macaroon"
|
|
|
|
// HeaderMacaroon is the HTTP header field name that is used to send the
|
|
// LSAT by our own gRPC clients.
|
|
HeaderMacaroon = "Macaroon"
|
|
)
|
|
|
|
var (
|
|
authRegex = regexp.MustCompile("LSAT (.*?):([a-f0-9]{64})")
|
|
authFormat = "LSAT %s:%s"
|
|
)
|
|
|
|
// FromHeader tries to extract authentication information from HTTP headers.
|
|
// There are two supported formats that can be sent in three different header
|
|
// fields:
|
|
// 1. Authorization: LSAT <macBase64>:<preimageHex>
|
|
// 2. Grpc-Metadata-Macaroon: <macHex>
|
|
// 3. Macaroon: <macHex>
|
|
// If only the macaroon is sent in header 2 or three then it is expected to have
|
|
// a caveat with the preimage attached to it.
|
|
func FromHeader(header *http.Header) (*macaroon.Macaroon, lntypes.Preimage, error) {
|
|
var authHeader string
|
|
|
|
switch {
|
|
// Header field 1 contains the macaroon and the preimage as distinct
|
|
// values separated by a colon.
|
|
case header.Get(HeaderAuthorization) != "":
|
|
// Parse the content of the header field and check that it is in
|
|
// the correct format.
|
|
authHeader = header.Get(HeaderAuthorization)
|
|
log.Debugf("Trying to authorize with header value [%s].",
|
|
authHeader)
|
|
if !authRegex.MatchString(authHeader) {
|
|
return nil, lntypes.Preimage{}, fmt.Errorf("invalid "+
|
|
"auth header format: %s", authHeader)
|
|
}
|
|
matches := authRegex.FindStringSubmatch(authHeader)
|
|
if len(matches) != 3 {
|
|
return nil, lntypes.Preimage{}, fmt.Errorf("invalid "+
|
|
"auth header format: %s", authHeader)
|
|
}
|
|
|
|
// Decode the content of the two parts of the header value.
|
|
macBase64, preimageHex := matches[1], matches[2]
|
|
macBytes, err := base64.StdEncoding.DecodeString(macBase64)
|
|
if err != nil {
|
|
return nil, lntypes.Preimage{}, fmt.Errorf("base64 "+
|
|
"decode of macaroon failed: %v", err)
|
|
}
|
|
mac := &macaroon.Macaroon{}
|
|
err = mac.UnmarshalBinary(macBytes)
|
|
if err != nil {
|
|
return nil, lntypes.Preimage{}, fmt.Errorf("unable to "+
|
|
"unmarshal macaroon: %v", err)
|
|
}
|
|
preimage, err := lntypes.MakePreimageFromStr(preimageHex)
|
|
if err != nil {
|
|
return nil, lntypes.Preimage{}, fmt.Errorf("hex "+
|
|
"decode of preimage failed: %v", err)
|
|
}
|
|
|
|
// All done, we don't need to extract anything from the
|
|
// macaroon since the preimage was presented separately.
|
|
return mac, preimage, nil
|
|
|
|
// Header field 2: Contains only the macaroon.
|
|
case header.Get(HeaderMacaroonMD) != "":
|
|
authHeader = header.Get(HeaderMacaroonMD)
|
|
|
|
// Header field 3: Contains only the macaroon.
|
|
case header.Get(HeaderMacaroon) != "":
|
|
authHeader = header.Get(HeaderMacaroon)
|
|
|
|
default:
|
|
return nil, lntypes.Preimage{}, fmt.Errorf("no auth header " +
|
|
"provided")
|
|
}
|
|
|
|
// For case 2 and 3, we need to actually unmarshal the macaroon to
|
|
// extract the preimage.
|
|
macBytes, err := hex.DecodeString(authHeader)
|
|
if err != nil {
|
|
return nil, lntypes.Preimage{}, fmt.Errorf("hex decode of "+
|
|
"macaroon failed: %v", err)
|
|
}
|
|
mac := &macaroon.Macaroon{}
|
|
err = mac.UnmarshalBinary(macBytes)
|
|
if err != nil {
|
|
return nil, lntypes.Preimage{}, fmt.Errorf("unable to "+
|
|
"unmarshal macaroon: %v", err)
|
|
}
|
|
preimageHex, ok := HasCaveat(mac, PreimageKey)
|
|
if !ok {
|
|
return nil, lntypes.Preimage{}, errors.New("preimage caveat " +
|
|
"not found")
|
|
}
|
|
preimage, err := lntypes.MakePreimageFromStr(preimageHex)
|
|
if err != nil {
|
|
return nil, lntypes.Preimage{}, fmt.Errorf("hex decode of "+
|
|
"preimage failed: %v", err)
|
|
}
|
|
|
|
return mac, preimage, nil
|
|
}
|
|
|
|
// SetHeader sets the provided authentication elements as the default/standard
|
|
// HTTP header for the LSAT protocol.
|
|
func SetHeader(header *http.Header, mac *macaroon.Macaroon,
|
|
preimage fmt.Stringer) error {
|
|
|
|
macBytes, err := mac.MarshalBinary()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
value := fmt.Sprintf(
|
|
authFormat, base64.StdEncoding.EncodeToString(macBytes),
|
|
preimage.String(),
|
|
)
|
|
header.Set(HeaderAuthorization, value)
|
|
return nil
|
|
}
|