mirror of
https://github.com/lightninglabs/loop
synced 2024-11-13 13:10:30 +00:00
203 lines
4.7 KiB
Go
203 lines
4.7 KiB
Go
|
package lsat
|
||
|
|
||
|
import (
|
||
|
"errors"
|
||
|
"testing"
|
||
|
|
||
|
"gopkg.in/macaroon.v2"
|
||
|
)
|
||
|
|
||
|
var (
|
||
|
testMacaroon, _ = macaroon.New(nil, nil, "", macaroon.LatestVersion)
|
||
|
)
|
||
|
|
||
|
// TestCaveatSerialization ensures that we can properly encode/decode valid
|
||
|
// caveats and cannot do so for invalid ones.
|
||
|
func TestCaveatSerialization(t *testing.T) {
|
||
|
t.Parallel()
|
||
|
|
||
|
tests := []struct {
|
||
|
name string
|
||
|
caveatStr string
|
||
|
err error
|
||
|
}{
|
||
|
{
|
||
|
name: "valid caveat",
|
||
|
caveatStr: "expiration=1337",
|
||
|
err: nil,
|
||
|
},
|
||
|
{
|
||
|
name: "valid caveat with separator in value",
|
||
|
caveatStr: "expiration=1337=",
|
||
|
err: nil,
|
||
|
},
|
||
|
{
|
||
|
name: "invalid caveat",
|
||
|
caveatStr: "expiration:1337",
|
||
|
err: ErrInvalidCaveat,
|
||
|
},
|
||
|
}
|
||
|
|
||
|
for _, test := range tests {
|
||
|
test := test
|
||
|
success := t.Run(test.name, func(t *testing.T) {
|
||
|
caveat, err := DecodeCaveat(test.caveatStr)
|
||
|
if !errors.Is(err, test.err) {
|
||
|
t.Fatalf("expected err \"%v\", got \"%v\"",
|
||
|
test.err, err)
|
||
|
}
|
||
|
|
||
|
if test.err != nil {
|
||
|
return
|
||
|
}
|
||
|
|
||
|
caveatStr := EncodeCaveat(caveat)
|
||
|
if caveatStr != test.caveatStr {
|
||
|
t.Fatalf("expected encoded caveat \"%v\", "+
|
||
|
"got \"%v\"", test.caveatStr, caveatStr)
|
||
|
}
|
||
|
})
|
||
|
if !success {
|
||
|
return
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// TestHasCaveat ensures we can determine whether a macaroon contains a caveat
|
||
|
// with a specific condition.
|
||
|
func TestHasCaveat(t *testing.T) {
|
||
|
t.Parallel()
|
||
|
|
||
|
const (
|
||
|
cond = "cond"
|
||
|
value = "value"
|
||
|
)
|
||
|
m := testMacaroon.Clone()
|
||
|
|
||
|
// The macaroon doesn't have any caveats, so we shouldn't find any.
|
||
|
if _, ok := HasCaveat(m, cond); ok {
|
||
|
t.Fatal("found unexpected caveat with unknown condition")
|
||
|
}
|
||
|
|
||
|
// Add two caveats, one in a valid LSAT format and another invalid.
|
||
|
// We'll test that we're still able to determine the macaroon contains
|
||
|
// the valid caveat even though there is one that is invalid.
|
||
|
invalidCaveat := []byte("invalid")
|
||
|
if err := m.AddFirstPartyCaveat(invalidCaveat); err != nil {
|
||
|
t.Fatalf("unable to add macaroon caveat: %v", err)
|
||
|
}
|
||
|
validCaveat1 := Caveat{Condition: cond, Value: value}
|
||
|
if err := AddFirstPartyCaveats(m, validCaveat1); err != nil {
|
||
|
t.Fatalf("unable to add macaroon caveat: %v", err)
|
||
|
}
|
||
|
|
||
|
caveatValue, ok := HasCaveat(m, cond)
|
||
|
if !ok {
|
||
|
t.Fatal("expected macaroon to contain caveat")
|
||
|
}
|
||
|
if caveatValue != validCaveat1.Value {
|
||
|
t.Fatalf("expected caveat value \"%v\", got \"%v\"",
|
||
|
validCaveat1.Value, caveatValue)
|
||
|
}
|
||
|
|
||
|
// If we add another caveat with the same condition, the value of the
|
||
|
// most recently added caveat should be returned instead.
|
||
|
validCaveat2 := validCaveat1
|
||
|
validCaveat2.Value += value
|
||
|
if err := AddFirstPartyCaveats(m, validCaveat2); err != nil {
|
||
|
t.Fatalf("unable to add macaroon caveat: %v", err)
|
||
|
}
|
||
|
|
||
|
caveatValue, ok = HasCaveat(m, cond)
|
||
|
if !ok {
|
||
|
t.Fatal("expected macaroon to contain caveat")
|
||
|
}
|
||
|
if caveatValue != validCaveat2.Value {
|
||
|
t.Fatalf("expected caveat value \"%v\", got \"%v\"",
|
||
|
validCaveat2.Value, caveatValue)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// TestVerifyCaveats ensures caveat verification only holds true for known
|
||
|
// caveats.
|
||
|
func TestVerifyCaveats(t *testing.T) {
|
||
|
t.Parallel()
|
||
|
|
||
|
caveat1 := Caveat{Condition: "1", Value: "test"}
|
||
|
caveat2 := Caveat{Condition: "2", Value: "test"}
|
||
|
satisfier := Satisfier{
|
||
|
Condition: caveat1.Condition,
|
||
|
SatisfyPrevious: func(c Caveat, prev Caveat) error {
|
||
|
return nil
|
||
|
},
|
||
|
SatisfyFinal: func(c Caveat) error {
|
||
|
return nil
|
||
|
},
|
||
|
}
|
||
|
invalidSatisfyPrevious := func(c Caveat, prev Caveat) error {
|
||
|
return errors.New("no")
|
||
|
}
|
||
|
invalidSatisfyFinal := func(c Caveat) error {
|
||
|
return errors.New("no")
|
||
|
}
|
||
|
|
||
|
tests := []struct {
|
||
|
name string
|
||
|
caveats []Caveat
|
||
|
satisfiers []Satisfier
|
||
|
shouldFail bool
|
||
|
}{
|
||
|
{
|
||
|
name: "simple verification",
|
||
|
caveats: []Caveat{caveat1},
|
||
|
satisfiers: []Satisfier{satisfier},
|
||
|
shouldFail: false,
|
||
|
},
|
||
|
{
|
||
|
name: "unknown caveat",
|
||
|
caveats: []Caveat{caveat1, caveat2},
|
||
|
satisfiers: []Satisfier{satisfier},
|
||
|
shouldFail: false,
|
||
|
},
|
||
|
{
|
||
|
name: "one invalid",
|
||
|
caveats: []Caveat{caveat1, caveat2},
|
||
|
satisfiers: []Satisfier{
|
||
|
satisfier,
|
||
|
{
|
||
|
Condition: caveat2.Condition,
|
||
|
SatisfyFinal: invalidSatisfyFinal,
|
||
|
},
|
||
|
},
|
||
|
shouldFail: true,
|
||
|
},
|
||
|
{
|
||
|
name: "prev invalid",
|
||
|
caveats: []Caveat{caveat1, caveat1},
|
||
|
satisfiers: []Satisfier{
|
||
|
{
|
||
|
Condition: caveat1.Condition,
|
||
|
SatisfyPrevious: invalidSatisfyPrevious,
|
||
|
},
|
||
|
},
|
||
|
shouldFail: true,
|
||
|
},
|
||
|
}
|
||
|
|
||
|
for _, test := range tests {
|
||
|
test := test
|
||
|
success := t.Run(test.name, func(t *testing.T) {
|
||
|
err := VerifyCaveats(test.caveats, test.satisfiers...)
|
||
|
if test.shouldFail && err == nil {
|
||
|
t.Fatal("expected caveat verification to fail")
|
||
|
}
|
||
|
if !test.shouldFail && err != nil {
|
||
|
t.Fatal("unexpected caveat verification failure")
|
||
|
}
|
||
|
})
|
||
|
if !success {
|
||
|
return
|
||
|
}
|
||
|
}
|
||
|
}
|