diff --git a/acp/identity/identity.go b/acp/identity/identity.go index 5367720690..34755efbd6 100644 --- a/acp/identity/identity.go +++ b/acp/identity/identity.go @@ -20,6 +20,7 @@ import ( "github.com/lestrrat-go/jwx/v2/jwa" "github.com/lestrrat-go/jwx/v2/jwt" "github.com/sourcenetwork/immutable" + acptypes "github.com/sourcenetwork/sourcehub/x/acp/bearer_token" ) // didProducer generates a did:key from a public key @@ -35,7 +36,7 @@ type Identity struct { // PublicKey is the actor's public key. PublicKey *secp256k1.PublicKey // PrivateKey is the actor's private key. - PrivateKey *secp256k1.PrivateKey // todo - remove, it is only used in test + PrivateKey *secp256k1.PrivateKey // DID is the actor's unique identifier. // // The address is derived from the actor's public key, @@ -46,7 +47,14 @@ type Identity struct { } // FromPrivateKey returns a new identity using the given private key. -func FromPrivateKey( // todo - doc params +// +// - duration: The [time.Duration] that this identity is valid for. +// - audience: The audience that this identity is valid for. This is required +// by the Defra http client. For example `github.com/sourcenetwork/defradb` +// - authorizedAccount: An account that this identity is authorizing to make +// SourceHub calls on behalf of this actor. This is currently required when +// using the SourceHub ACP module. +func FromPrivateKey( privateKey *secp256k1.PrivateKey, duration time.Duration, audience immutable.Option[string], @@ -59,7 +67,7 @@ func FromPrivateKey( // todo - doc params } subject := hex.EncodeToString(publicKey.SerializeCompressed()) - now := time.Now() // todo - probably best to parameterise this + now := time.Now() jwtBuilder := jwt.NewBuilder() jwtBuilder = jwtBuilder.Subject(subject) @@ -78,7 +86,7 @@ func FromPrivateKey( // todo - doc params } if authorizedAccount.HasValue() { - err = token.Set("authorized_account", authorizedAccount.Value()) // todo - const? + err = token.Set(acptypes.AuthorizedAccountClaim, authorizedAccount.Value()) if err != nil { return Identity{}, err } diff --git a/http/auth_test.go b/http/auth_test.go index b3dda84d46..7597578f8a 100644 --- a/http/auth_test.go +++ b/http/auth_test.go @@ -10,141 +10,67 @@ package http -/* import ( - "encoding/hex" "testing" "time" - "github.com/lestrrat-go/jwx/v2/jwt" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" acpIdentity "github.com/sourcenetwork/defradb/acp/identity" "github.com/sourcenetwork/defradb/crypto" + "github.com/sourcenetwork/immutable" ) -func TestBuildAuthToken(t *testing.T) { - privKey, err := crypto.GenerateSecp256k1() - require.NoError(t, err) - - identity, err := acpIdentity.FromPrivateKey(privKey) - require.NoError(t, err) - token, err := buildAuthToken(identity.Value(), "abc123") - require.NoError(t, err) - - subject := hex.EncodeToString(privKey.PubKey().SerializeCompressed()) - assert.Equal(t, subject, token.Subject()) - - assert.True(t, token.NotBefore().Before(time.Now())) - assert.True(t, token.Expiration().After(time.Now())) - assert.Equal(t, []string{"abc123"}, token.Audience()) -} - -func TestSignAuthTokenErrorsWithPublicIdentity(t *testing.T) { - privKey, err := crypto.GenerateSecp256k1() - require.NoError(t, err) - - identity, err := acpIdentity.FromPublicKey(privKey.PubKey()) - require.NoError(t, err) - token, err := buildAuthToken(identity.Value(), "abc123") - require.NoError(t, err) - - _, err = signAuthToken(identity.Value(), token) - assert.ErrorIs(t, err, ErrMissingIdentityPrivateKey) -} - func TestVerifyAuthToken(t *testing.T) { - privKey, err := crypto.GenerateSecp256k1() - require.NoError(t, err) + audience := "abc123" - identity, err := acpIdentity.FromPrivateKey(privKey) - require.NoError(t, err) - token, err := buildAndSignAuthToken(identity.Value(), "abc123") + privKey, err := crypto.GenerateSecp256k1() require.NoError(t, err) - actual, err := verifyAuthToken(token, "abc123") + identity, err := acpIdentity.FromPrivateKey( + privKey, + time.Hour, + immutable.Some(audience), + immutable.None[string](), + ) require.NoError(t, err) - expected, err := acpIdentity.FromPublicKey(privKey.PubKey()) + err = verifyAuthToken(identity, audience) require.NoError(t, err) - assert.Equal(t, expected.Value().DID, actual.Value().DID) } func TestVerifyAuthTokenErrorsWithNonMatchingAudience(t *testing.T) { privKey, err := crypto.GenerateSecp256k1() require.NoError(t, err) - identity, err := acpIdentity.FromPrivateKey(privKey) - require.NoError(t, err) - token, err := buildAndSignAuthToken(identity.Value(), "valid") - require.NoError(t, err) - - _, err = verifyAuthToken(token, "invalid") - assert.Error(t, err) -} - -func TestVerifyAuthTokenErrorsWithWrongPublicKey(t *testing.T) { - privKey, err := crypto.GenerateSecp256k1() - require.NoError(t, err) - - otherKey, err := crypto.GenerateSecp256k1() - require.NoError(t, err) - - identity, err := acpIdentity.FromPrivateKey(privKey) - require.NoError(t, err) - token, err := buildAuthToken(identity.Value(), "123abc") - require.NoError(t, err) - - // override subject - subject := hex.EncodeToString(otherKey.PubKey().SerializeCompressed()) - err = token.Set(jwt.SubjectKey, subject) - require.NoError(t, err) - - data, err := signAuthToken(identity.Value(), token) + identity, err := acpIdentity.FromPrivateKey( + privKey, + time.Hour, + immutable.Some("valid"), + immutable.None[string](), + ) require.NoError(t, err) - _, err = verifyAuthToken(data, "123abc") + err = verifyAuthToken(identity, "invalid") assert.Error(t, err) } func TestVerifyAuthTokenErrorsWithExpired(t *testing.T) { - privKey, err := crypto.GenerateSecp256k1() - require.NoError(t, err) + audience := "abc123" - identity, err := acpIdentity.FromPrivateKey(privKey) - require.NoError(t, err) - token, err := buildAuthToken(identity.Value(), "123abc") - require.NoError(t, err) - - // override expiration - err = token.Set(jwt.ExpirationKey, time.Now().Add(-15*time.Minute)) - require.NoError(t, err) - - data, err := signAuthToken(identity.Value(), token) - require.NoError(t, err) - - _, err = verifyAuthToken(data, "123abc") - assert.Error(t, err) -} - -func TestVerifyAuthTokenErrorsWithNotBefore(t *testing.T) { privKey, err := crypto.GenerateSecp256k1() require.NoError(t, err) - identity, err := acpIdentity.FromPrivateKey(privKey) - require.NoError(t, err) - token, err := buildAuthToken(identity.Value(), "123abc") - require.NoError(t, err) - - // override not before - err = token.Set(jwt.NotBeforeKey, time.Now().Add(15*time.Minute)) - require.NoError(t, err) - - data, err := signAuthToken(identity.Value(), token) + identity, err := acpIdentity.FromPrivateKey( + privKey, + // negative expiration + -time.Hour, + immutable.Some(audience), + immutable.None[string](), + ) require.NoError(t, err) - _, err = verifyAuthToken(data, "123abc") + err = verifyAuthToken(identity, "123abc") assert.Error(t, err) } -*/ diff --git a/internal/db/p2p_schema_root_test.go b/internal/db/p2p_schema_root_test.go index e2e5792a1c..8ae02cb65b 100644 --- a/internal/db/p2p_schema_root_test.go +++ b/internal/db/p2p_schema_root_test.go @@ -12,11 +12,19 @@ package db import ( "context" + "encoding/hex" + "fmt" "testing" + "time" + "github.com/decred/dcrd/dcrec/secp256k1/v4" + "github.com/sourcenetwork/immutable" "github.com/stretchr/testify/require" + "github.com/sourcenetwork/defradb/acp" + acpIdentity "github.com/sourcenetwork/defradb/acp/identity" "github.com/sourcenetwork/defradb/client" + "github.com/sourcenetwork/defradb/datastore/memory" "github.com/sourcenetwork/defradb/event" ) @@ -241,7 +249,6 @@ func TestGetAllP2PCollections_WithMultipleValidCollections_ShouldSucceed(t *test require.Equal(t, []string{schema2.Root, schema1.Root}, cols) } -/* // This test documents that we don't allow adding p2p collections that have a policy // until the following is implemented: // TODO-ACP: ACP <> P2P https://github.com/sourcenetwork/defradb/issues/2366 @@ -272,10 +279,10 @@ func TestAddP2PCollectionsWithPermissionedCollection_Error(t *testing.T) { privKeyBytes, err := hex.DecodeString("028d53f37a19afb9a0dbc5b4be30c65731479ee8cfa0c9bc8f8bf198cc3c075f") require.NoError(t, err) privKey := secp256k1.PrivKeyFromBytes(privKeyBytes) - identity, err := acpIdentity.FromPrivateKey(privKey) + identity, err := acpIdentity.FromPrivateKey(privKey, time.Hour, immutable.None[string](), immutable.None[string]()) require.NoError(t, err) - ctx = SetContextIdentity(ctx, identity) + ctx = SetContextIdentity(ctx, immutable.Some(identity)) policyResult, err := db.AddPolicy(ctx, policy) policyID := policyResult.PolicyID require.NoError(t, err) @@ -298,4 +305,3 @@ func TestAddP2PCollectionsWithPermissionedCollection_Error(t *testing.T) { require.Error(t, err) require.ErrorIs(t, err, ErrP2PColHasPolicy) } -*/ diff --git a/tests/integration/acp.go b/tests/integration/acp.go index 1ba8d23131..b57d1b4c3b 100644 --- a/tests/integration/acp.go +++ b/tests/integration/acp.go @@ -47,6 +47,11 @@ const ( LocalACPType ACPType = "local" ) +const ( + // authTokenExpiration is the expiration time for auth tokens. + authTokenExpiration = time.Minute * 1 +) + var ( acpType ACPType ) @@ -408,7 +413,7 @@ func getIdentity(s *state, nodeIndex int, index immutable.Option[int]) immutable identity, err := acpIdentity.FromPrivateKey( privateKey, - time.Minute, + authTokenExpiration, audience, immutable.Some(s.sourcehubAddress), )