-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fixes #362 In a follow-up, I will also start logging these incoming requests. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit ## Summary by CodeRabbit ## Release Notes - **New Features** - Added version tracking capabilities to authentication and token generation processes. - Introduced version compatibility validation for JWT claims. - **Improvements** - Enhanced token factory and server initialization to support version information. - Updated the build process to utilize version identifiers instead of commit hashes. - Improved GitHub Actions workflow to provide descriptive commit references during Docker image builds. - **Dependencies** - Added `github.com/Masterminds/semver/v3` package for version management. The changes primarily focus on improving version handling and compatibility across the system's authentication and server components. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
- Loading branch information
Showing
24 changed files
with
319 additions
and
51 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
package authn | ||
|
||
import ( | ||
"fmt" | ||
"github.com/Masterminds/semver/v3" | ||
"github.com/golang-jwt/jwt/v5" | ||
) | ||
|
||
const ( | ||
// XMTPD_COMPATIBLE_VERSION_CONSTRAINT major or minor serverVersion bumps indicate backwards incompatible changes | ||
XMTPD_COMPATIBLE_VERSION_CONSTRAINT = "~ 0.1.3" | ||
) | ||
|
||
type XmtpdClaims struct { | ||
Version *semver.Version `json:"version,omitempty"` | ||
jwt.RegisteredClaims | ||
} | ||
|
||
func ValidateVersionClaimIsCompatible(claims *XmtpdClaims) error { | ||
if claims.Version == nil { | ||
return nil | ||
} | ||
|
||
c, err := semver.NewConstraint(XMTPD_COMPATIBLE_VERSION_CONSTRAINT) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
// SemVer implementations generally do not consider pre-releases to be valid next releases | ||
// we use SemVer here to allow incoming connections, for which in-development nodes are acceptable | ||
// see discussion in https://github.com/Masterminds/semver/issues/21 | ||
sanitizedVersion, err := claims.Version.SetPrerelease("") | ||
if err != nil { | ||
return err | ||
} | ||
if ok := c.Check(&sanitizedVersion); !ok { | ||
return fmt.Errorf("serverVersion %s is not compatible", *claims.Version) | ||
} | ||
|
||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
package authn | ||
|
||
import ( | ||
"bytes" | ||
"github.com/Masterminds/semver/v3" | ||
"github.com/stretchr/testify/require" | ||
"github.com/xmtp/xmtpd/pkg/registry" | ||
"github.com/xmtp/xmtpd/pkg/testutils" | ||
"os/exec" | ||
"strings" | ||
"testing" | ||
) | ||
|
||
func getLatestTag(t *testing.T) string { | ||
// Prepare the command | ||
cmd := exec.Command("git", "describe", "--tags", "--abbrev=0") | ||
|
||
// Capture the output | ||
var out bytes.Buffer | ||
cmd.Stdout = &out | ||
cmd.Stderr = &out | ||
|
||
// Run the command | ||
err := cmd.Run() | ||
require.NoError(t, err, out.String()) | ||
return strings.TrimSpace(out.String()) | ||
} | ||
|
||
func getLatestVersion(t *testing.T) semver.Version { | ||
tag := getLatestTag(t) | ||
v, err := semver.NewVersion(tag) | ||
require.NoError(t, err) | ||
|
||
return *v | ||
} | ||
|
||
func newVersionNoError(t *testing.T, version string, pre string, meta string) semver.Version { | ||
v, err := semver.NewVersion(version) | ||
require.NoError(t, err) | ||
|
||
vextras, err := v.SetPrerelease(pre) | ||
require.NoError(t, err) | ||
|
||
vmeta, err := vextras.SetMetadata(meta) | ||
require.NoError(t, err) | ||
|
||
return vmeta | ||
} | ||
|
||
func TestClaimsVerifierNoVersion(t *testing.T) { | ||
signerPrivateKey := testutils.RandomPrivateKey(t) | ||
|
||
tests := []struct { | ||
name string | ||
version *semver.Version | ||
wantErr bool | ||
}{ | ||
{"no-version", nil, false}, | ||
} | ||
|
||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
tokenFactory := NewTokenFactory(signerPrivateKey, uint32(SIGNER_NODE_ID), tt.version) | ||
|
||
verifier, nodeRegistry := buildVerifier(t, uint32(VERIFIER_NODE_ID)) | ||
nodeRegistry.EXPECT().GetNode(uint32(SIGNER_NODE_ID)).Return(®istry.Node{ | ||
SigningKey: &signerPrivateKey.PublicKey, | ||
NodeID: uint32(SIGNER_NODE_ID), | ||
}, nil) | ||
|
||
token, err := tokenFactory.CreateToken(uint32(VERIFIER_NODE_ID)) | ||
require.NoError(t, err) | ||
verificationError := verifier.Verify(token.SignedString) | ||
if tt.wantErr { | ||
require.Error(t, verificationError) | ||
} else { | ||
require.NoError(t, verificationError) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
func TestClaimsVerifier(t *testing.T) { | ||
signerPrivateKey := testutils.RandomPrivateKey(t) | ||
|
||
currentVersion := getLatestVersion(t) | ||
|
||
tests := []struct { | ||
name string | ||
version semver.Version | ||
wantErr bool | ||
}{ | ||
{"current-version", currentVersion, false}, | ||
{"next-patch-version", currentVersion.IncPatch(), false}, | ||
{"next-minor-version", currentVersion.IncMinor(), true}, | ||
{"next-major-version", currentVersion.IncMajor(), true}, | ||
{"last-supported-version", newVersionNoError(t, currentVersion.String(), "", ""), false}, | ||
{ | ||
"with-prerelease-version", | ||
newVersionNoError(t, currentVersion.String(), "17-gdeadbeef", ""), | ||
false, | ||
}, | ||
{ | ||
"with-metadata-version", | ||
newVersionNoError(t, currentVersion.String(), "", "branch-dev"), | ||
false, | ||
}, | ||
} | ||
|
||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
tokenFactory := NewTokenFactory(signerPrivateKey, uint32(SIGNER_NODE_ID), &tt.version) | ||
|
||
verifier, nodeRegistry := buildVerifier(t, uint32(VERIFIER_NODE_ID)) | ||
nodeRegistry.EXPECT().GetNode(uint32(SIGNER_NODE_ID)).Return(®istry.Node{ | ||
SigningKey: &signerPrivateKey.PublicKey, | ||
NodeID: uint32(SIGNER_NODE_ID), | ||
}, nil) | ||
|
||
token, err := tokenFactory.CreateToken(uint32(VERIFIER_NODE_ID)) | ||
require.NoError(t, err) | ||
verificationError := verifier.Verify(token.SignedString) | ||
if tt.wantErr { | ||
require.Error(t, verificationError) | ||
} else { | ||
require.NoError(t, verificationError) | ||
} | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.