mirror of
https://github.com/lightninglabs/aperture.git
synced 2025-12-17 00:54:20 +01:00
auth: LsatAuthenticator -> L402Authenticator sed -i 's/LsatAuthenticator/L402Authenticator/g' aperture.go auth/authenticator.go auth/authenticator_test.go rename package lsat to l402 git mv lsat/ l402 sed 's@aperture/lsat@aperture/l402@g' -i `git grep -l aperture/lsat` sed -i 's@package lsat@package l402@' `git grep -l 'package lsat'` sed -i 's@lsat\.@l402.@g' -i `git grep -l 'lsat\.'` sed 's@l402.Id@lsat.Id@' -i mint/mint_test.go replace lsat with l402 in the code sed 's@lsat@l402@' -i mint/mint_test.go sed 's@Lsat@L402@' -i l402/client_interceptor.go sed 's@lsatstore@l402store@' -i l402/store_test.go replace LSAT to L402 in comments sed '/\/\//s@LSAT@L402@g' -i `git grep -l '//.*LSAT'` replace LSAT -> L402 in the code, skip when a string starts with it sed 's@\([^"/]\)LSAT@\1L402@g' -i `git grep -l LSAT`
298 lines
8.2 KiB
Go
298 lines
8.2 KiB
Go
package mint
|
|
|
|
import (
|
|
"context"
|
|
"crypto/sha256"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/lightninglabs/aperture/l402"
|
|
"github.com/stretchr/testify/require"
|
|
"gopkg.in/macaroon.v2"
|
|
)
|
|
|
|
var (
|
|
testService = l402.Service{
|
|
Name: "lightning_loop",
|
|
Tier: l402.BaseTier,
|
|
}
|
|
)
|
|
|
|
// TestBasicL402 ensures that an L402 can only access the services it's
|
|
// authorized to.
|
|
func TestBasicL402(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
ctx := context.Background()
|
|
mint := New(&Config{
|
|
Secrets: newMockSecretStore(),
|
|
Challenger: newMockChallenger(),
|
|
ServiceLimiter: newMockServiceLimiter(),
|
|
Now: time.Now,
|
|
})
|
|
|
|
// Mint a basic L402 which is only able to access the given service.
|
|
macaroon, _, err := mint.MintL402(ctx, testService)
|
|
if err != nil {
|
|
t.Fatalf("unable to mint L402: %v", err)
|
|
}
|
|
|
|
params := VerificationParams{
|
|
Macaroon: macaroon,
|
|
Preimage: testPreimage,
|
|
TargetService: testService.Name,
|
|
}
|
|
if err := mint.VerifyL402(ctx, ¶ms); err != nil {
|
|
t.Fatalf("unable to verify L402: %v", err)
|
|
}
|
|
|
|
// It should not be able to access an unknown service.
|
|
unknownParams := params
|
|
unknownParams.TargetService = "unknown"
|
|
err = mint.VerifyL402(ctx, &unknownParams)
|
|
if !strings.Contains(err.Error(), "not authorized") {
|
|
t.Fatal("expected L402 to not be authorized")
|
|
}
|
|
}
|
|
|
|
// TestAdminL402 ensures that an admin L402 (one without a services caveat) is
|
|
// authorized to access any service.
|
|
func TestAdminL402(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
ctx := context.Background()
|
|
mint := New(&Config{
|
|
Secrets: newMockSecretStore(),
|
|
Challenger: newMockChallenger(),
|
|
ServiceLimiter: newMockServiceLimiter(),
|
|
Now: time.Now,
|
|
})
|
|
|
|
// Mint an admin L402 by not including any services.
|
|
macaroon, _, err := mint.MintL402(ctx)
|
|
if err != nil {
|
|
t.Fatalf("unable to mint L402: %v", err)
|
|
}
|
|
|
|
// It should be able to access any service as it doesn't have a services
|
|
// caveat.
|
|
params := &VerificationParams{
|
|
Macaroon: macaroon,
|
|
Preimage: testPreimage,
|
|
TargetService: testService.Name,
|
|
}
|
|
if err := mint.VerifyL402(ctx, params); err != nil {
|
|
t.Fatalf("unable to verify L402: %v", err)
|
|
}
|
|
}
|
|
|
|
// TestRevokedL402 ensures that we can no longer verify a revoked L402.
|
|
func TestRevokedL402(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
ctx := context.Background()
|
|
mint := New(&Config{
|
|
Secrets: newMockSecretStore(),
|
|
Challenger: newMockChallenger(),
|
|
ServiceLimiter: newMockServiceLimiter(),
|
|
Now: time.Now,
|
|
})
|
|
|
|
// Mint an L402 and verify it.
|
|
l402, _, err := mint.MintL402(ctx)
|
|
if err != nil {
|
|
t.Fatalf("unable to mint L402: %v", err)
|
|
}
|
|
params := &VerificationParams{
|
|
Macaroon: l402,
|
|
Preimage: testPreimage,
|
|
TargetService: testService.Name,
|
|
}
|
|
if err := mint.VerifyL402(ctx, params); err != nil {
|
|
t.Fatalf("unable to verify L402: %v", err)
|
|
}
|
|
|
|
// Proceed to revoke it. We should no longer be able to verify it after.
|
|
idHash := sha256.Sum256(l402.Id())
|
|
if err := mint.cfg.Secrets.RevokeSecret(ctx, idHash); err != nil {
|
|
t.Fatalf("unable to revoke L402: %v", err)
|
|
}
|
|
if err := mint.VerifyL402(ctx, params); err != ErrSecretNotFound {
|
|
t.Fatalf("expected ErrSecretNotFound, got %v", err)
|
|
}
|
|
}
|
|
|
|
// TestTamperedL402 ensures that an L402 that has been tampered with by
|
|
// modifying its signature results in its verification failing.
|
|
func TestTamperedL402(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
ctx := context.Background()
|
|
mint := New(&Config{
|
|
Secrets: newMockSecretStore(),
|
|
Challenger: newMockChallenger(),
|
|
ServiceLimiter: newMockServiceLimiter(),
|
|
Now: time.Now,
|
|
})
|
|
|
|
// Mint a new L402 and verify it is valid.
|
|
mac, _, err := mint.MintL402(ctx, testService)
|
|
if err != nil {
|
|
t.Fatalf("unable to mint L402: %v", err)
|
|
}
|
|
params := VerificationParams{
|
|
Macaroon: mac,
|
|
Preimage: testPreimage,
|
|
TargetService: testService.Name,
|
|
}
|
|
if err := mint.VerifyL402(ctx, ¶ms); err != nil {
|
|
t.Fatalf("unable to verify L402: %v", err)
|
|
}
|
|
|
|
// Create a tampered L402 from the valid one.
|
|
macBytes, err := mac.MarshalBinary()
|
|
if err != nil {
|
|
t.Fatalf("unable to serialize macaroon: %v", err)
|
|
}
|
|
macBytes[len(macBytes)-1] = 0x00
|
|
var tampered macaroon.Macaroon
|
|
if err := tampered.UnmarshalBinary(macBytes); err != nil {
|
|
t.Fatalf("unable to deserialize macaroon: %v", err)
|
|
}
|
|
|
|
// Attempting to verify the tampered L402 should fail.
|
|
tamperedParams := params
|
|
tamperedParams.Macaroon = &tampered
|
|
err = mint.VerifyL402(ctx, &tamperedParams)
|
|
if !strings.Contains(err.Error(), "signature mismatch") {
|
|
t.Fatal("expected tampered L402 to be invalid")
|
|
}
|
|
}
|
|
|
|
// TestDemotedServicesL402 ensures that an L402 which originally was authorized
|
|
// to access a service, but was then demoted to no longer be the case, is no
|
|
// longer authorized.
|
|
func TestDemotedServicesL402(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
ctx := context.Background()
|
|
mint := New(&Config{
|
|
Secrets: newMockSecretStore(),
|
|
Challenger: newMockChallenger(),
|
|
ServiceLimiter: newMockServiceLimiter(),
|
|
Now: time.Now,
|
|
})
|
|
|
|
unauthorizedService := testService
|
|
unauthorizedService.Name = "unauthorized"
|
|
|
|
// Mint an L402 that is able to access two services, one of which will
|
|
// be denied later on.
|
|
mac, _, err := mint.MintL402(ctx, testService, unauthorizedService)
|
|
if err != nil {
|
|
t.Fatalf("unable to mint L402: %v", err)
|
|
}
|
|
|
|
// It should be able to access both services.
|
|
authorizedParams := VerificationParams{
|
|
Macaroon: mac,
|
|
Preimage: testPreimage,
|
|
TargetService: testService.Name,
|
|
}
|
|
if err := mint.VerifyL402(ctx, &authorizedParams); err != nil {
|
|
t.Fatalf("unable to verify L402: %v", err)
|
|
}
|
|
unauthorizedParams := VerificationParams{
|
|
Macaroon: mac,
|
|
Preimage: testPreimage,
|
|
TargetService: unauthorizedService.Name,
|
|
}
|
|
if err := mint.VerifyL402(ctx, &unauthorizedParams); err != nil {
|
|
t.Fatalf("unable to verify L402: %v", err)
|
|
}
|
|
|
|
// Demote the second service by including an additional services caveat
|
|
// that only includes the first service.
|
|
services, err := l402.NewServicesCaveat(testService)
|
|
if err != nil {
|
|
t.Fatalf("unable to create services caveat: %v", err)
|
|
}
|
|
err = l402.AddFirstPartyCaveats(mac, services)
|
|
if err != nil {
|
|
t.Fatalf("unable to demote L402: %v", err)
|
|
}
|
|
|
|
// It should now only be able to access the first, but not the second.
|
|
if err := mint.VerifyL402(ctx, &authorizedParams); err != nil {
|
|
t.Fatalf("unable to verify L402: %v", err)
|
|
}
|
|
err = mint.VerifyL402(ctx, &unauthorizedParams)
|
|
if !strings.Contains(err.Error(), "not authorized") {
|
|
t.Fatal("expected macaroon to be invalid")
|
|
}
|
|
}
|
|
|
|
// TestExpiredServicesL402 asserts the behavior of the Timeout caveat.
|
|
func TestExpiredServicesL402(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
initialTime := int64(1000)
|
|
mockTime := newMockTime(initialTime)
|
|
|
|
ctx := context.Background()
|
|
mint := New(&Config{
|
|
Secrets: newMockSecretStore(),
|
|
Challenger: newMockChallenger(),
|
|
ServiceLimiter: newMockServiceLimiter(),
|
|
Now: mockTime.now,
|
|
})
|
|
|
|
// Mint a new l402 for accessing a test service.
|
|
mac, _, err := mint.MintL402(ctx, testService)
|
|
require.NoError(t, err)
|
|
|
|
authorizedParams := VerificationParams{
|
|
Macaroon: mac,
|
|
Preimage: testPreimage,
|
|
TargetService: testService.Name,
|
|
}
|
|
|
|
// It should be able to access the service if no timeout caveat added.
|
|
require.NoError(t, mint.VerifyL402(ctx, &authorizedParams))
|
|
|
|
// Add a timeout caveat that expires in the future.
|
|
timeout := l402.NewTimeoutCaveat(testService.Name, 1000, mockTime.now)
|
|
require.NoError(t, l402.AddFirstPartyCaveats(mac, timeout))
|
|
|
|
// Make sure that the L402 is still valid after timeout is added since
|
|
// the timeout has not yet been reached.
|
|
require.NoError(t, mint.VerifyL402(ctx, &authorizedParams))
|
|
|
|
// Force time to pass such that the L402 should no longer be valid.
|
|
mockTime.setTime(initialTime + 1001)
|
|
|
|
// Assert that the L402 is no longer valid due to the timeout being
|
|
// reached.
|
|
err = mint.VerifyL402(ctx, &authorizedParams)
|
|
require.Contains(t, err.Error(), "not authorized")
|
|
}
|
|
|
|
type mockTime struct {
|
|
time time.Time
|
|
}
|
|
|
|
func newMockTime(initialTime int64) *mockTime {
|
|
return &mockTime{
|
|
time: time.Unix(initialTime, 0),
|
|
}
|
|
}
|
|
|
|
func (mt *mockTime) now() time.Time {
|
|
return mt.time
|
|
}
|
|
|
|
func (mt *mockTime) setTime(timestamp int64) {
|
|
mt.time = time.Unix(timestamp, 0)
|
|
}
|