/ macaroons / constraints_test.go
constraints_test.go
  1  package macaroons_test
  2  
  3  import (
  4  	"fmt"
  5  	"strings"
  6  	"testing"
  7  	"time"
  8  
  9  	"github.com/lightningnetwork/lnd/macaroons"
 10  	"github.com/stretchr/testify/require"
 11  	macaroon "gopkg.in/macaroon.v2"
 12  )
 13  
 14  var (
 15  	testRootKey                 = []byte("dummyRootKey")
 16  	testID                      = []byte("dummyId")
 17  	testLocation                = "lnd"
 18  	testVersion                 = macaroon.LatestVersion
 19  	expectedTimeCaveatSubstring = fmt.Sprintf("time-before %d", time.Now().Year())
 20  )
 21  
 22  func createDummyMacaroon(t *testing.T) *macaroon.Macaroon {
 23  	dummyMacaroon, err := macaroon.New(
 24  		testRootKey, testID, testLocation, testVersion,
 25  	)
 26  	require.NoError(t, err, "Error creating initial macaroon")
 27  	return dummyMacaroon
 28  }
 29  
 30  // TestAddConstraints tests that constraints can be added to an existing
 31  // macaroon and therefore tighten its restrictions.
 32  func TestAddConstraints(t *testing.T) {
 33  	t.Parallel()
 34  
 35  	// We need a dummy macaroon to start with. Create one without
 36  	// a bakery, because we mock everything anyway.
 37  	initialMac := createDummyMacaroon(t)
 38  
 39  	// Now add a constraint and make sure we have a cloned macaroon
 40  	// with the constraint applied instead of a mutated initial one.
 41  	newMac, err := macaroons.AddConstraints(
 42  		initialMac, macaroons.TimeoutConstraint(1),
 43  	)
 44  	require.NoError(t, err, "Error adding constraint")
 45  	if &newMac == &initialMac {
 46  		t.Fatalf("Initial macaroon has been changed, something " +
 47  			"went wrong!")
 48  	}
 49  
 50  	// Finally, test that the constraint has been added.
 51  	if len(initialMac.Caveats()) == len(newMac.Caveats()) {
 52  		t.Fatalf("No caveat has been added to the macaroon when " +
 53  			"constraint was applied")
 54  	}
 55  }
 56  
 57  // TestTimeoutConstraint tests that a caveat for the lifetime of
 58  // a macaroon is created.
 59  func TestTimeoutConstraint(t *testing.T) {
 60  	t.Parallel()
 61  
 62  	// Get a configured version of the constraint function.
 63  	constraintFunc := macaroons.TimeoutConstraint(3)
 64  
 65  	// Now we need a dummy macaroon that we can apply the constraint
 66  	// function to.
 67  	testMacaroon := createDummyMacaroon(t)
 68  	err := constraintFunc(testMacaroon)
 69  	require.NoError(t, err, "Error applying timeout constraint")
 70  
 71  	// Finally, check that the created caveat has an
 72  	// acceptable value.
 73  	if !strings.HasPrefix(
 74  		string(testMacaroon.Caveats()[0].Id),
 75  		expectedTimeCaveatSubstring,
 76  	) {
 77  
 78  		t.Fatalf("Added caveat '%s' does not meet the expectations!",
 79  			testMacaroon.Caveats()[0].Id)
 80  	}
 81  }
 82  
 83  // TestTimeoutConstraint tests that a caveat for the lifetime of
 84  // a macaroon is created.
 85  func TestIpLockConstraint(t *testing.T) {
 86  	t.Parallel()
 87  
 88  	// Get a configured version of the constraint function.
 89  	constraintFunc := macaroons.IPLockConstraint("127.0.0.1")
 90  
 91  	// Now we need a dummy macaroon that we can apply the constraint
 92  	// function to.
 93  	testMacaroon := createDummyMacaroon(t)
 94  	err := constraintFunc(testMacaroon)
 95  	require.NoError(t, err, "Error applying timeout constraint")
 96  
 97  	// Finally, check that the created caveat has an
 98  	// acceptable value.
 99  	if string(testMacaroon.Caveats()[0].Id) != "ipaddr 127.0.0.1" {
100  		t.Fatalf("Added caveat '%s' does not meet the expectations!",
101  			testMacaroon.Caveats()[0].Id)
102  	}
103  }
104  
105  // TestIPLockBadIP tests that an IP constraint cannot be added if the
106  // provided string is not a valid IP address.
107  func TestIPLockBadIP(t *testing.T) {
108  	t.Parallel()
109  
110  	constraintFunc := macaroons.IPLockConstraint("127.0.0/800")
111  	testMacaroon := createDummyMacaroon(t)
112  	err := constraintFunc(testMacaroon)
113  	if err == nil {
114  		t.Fatalf("IPLockConstraint with bad IP should fail.")
115  	}
116  }
117  
118  // TestCustomConstraint tests that a custom constraint with a name and value can
119  // be added to a macaroon.
120  func TestCustomConstraint(t *testing.T) {
121  	t.Parallel()
122  
123  	// Test a custom caveat with a value first.
124  	constraintFunc := macaroons.CustomConstraint("unit-test", "test-value")
125  	testMacaroon := createDummyMacaroon(t)
126  	require.NoError(t, constraintFunc(testMacaroon))
127  
128  	require.Equal(
129  		t, []byte("lnd-custom unit-test test-value"),
130  		testMacaroon.Caveats()[0].Id,
131  	)
132  	require.True(t, macaroons.HasCustomCaveat(testMacaroon, "unit-test"))
133  	require.False(t, macaroons.HasCustomCaveat(testMacaroon, "test-value"))
134  	require.False(t, macaroons.HasCustomCaveat(testMacaroon, "something"))
135  	require.False(t, macaroons.HasCustomCaveat(nil, "foo"))
136  
137  	customCaveatCondition := macaroons.GetCustomCaveatCondition(
138  		testMacaroon, "unit-test",
139  	)
140  	require.Equal(t, customCaveatCondition, "test-value")
141  
142  	// Custom caveats don't necessarily need a value, just the name is fine
143  	// too to create a tagged macaroon.
144  	constraintFunc = macaroons.CustomConstraint("unit-test", "")
145  	testMacaroon = createDummyMacaroon(t)
146  	require.NoError(t, constraintFunc(testMacaroon))
147  
148  	require.Equal(
149  		t, []byte("lnd-custom unit-test"), testMacaroon.Caveats()[0].Id,
150  	)
151  	require.True(t, macaroons.HasCustomCaveat(testMacaroon, "unit-test"))
152  	require.False(t, macaroons.HasCustomCaveat(testMacaroon, "test-value"))
153  	require.False(t, macaroons.HasCustomCaveat(testMacaroon, "something"))
154  
155  	customCaveatCondition = macaroons.GetCustomCaveatCondition(
156  		testMacaroon, "unit-test",
157  	)
158  	require.Equal(t, customCaveatCondition, "")
159  }