feat: publish v1.0.0-rc.36-custom.1
Publish Docker image (Multi-arch) / Build & push (amd64) (push) Failing after 9m20s
Release (Linux, macOS, Windows) / Linux Release (push) Failing after 55m7s
Publish Docker image (Multi-arch) / Build & push (arm64) (push) Canceled after 0s
Release (Linux, macOS, Windows) / macOS Release (push) Canceled after 0s
Release (Linux, macOS, Windows) / Windows Release (push) Canceled after 0s
Publish Docker image (Multi-arch) / Create multi-arch manifests (push) Canceled after 0s
Publish Docker image (Multi-arch) / Build & push (amd64) (push) Failing after 9m20s
Release (Linux, macOS, Windows) / Linux Release (push) Failing after 55m7s
Publish Docker image (Multi-arch) / Build & push (arm64) (push) Canceled after 0s
Release (Linux, macOS, Windows) / macOS Release (push) Canceled after 0s
Release (Linux, macOS, Windows) / Windows Release (push) Canceled after 0s
Publish Docker image (Multi-arch) / Create multi-arch manifests (push) Canceled after 0s
This commit is contained in:
commit
bb7968b612
2381 files changed
+551494
No files matched your search
@@ -0,0 +1,162 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/QuantumNous/new-api/common"
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func useTestSessionSecret(t *testing.T) {
|
||||
t.Helper()
|
||||
previous := common.SessionSecret
|
||||
common.SessionSecret = "test-session-secret-with-sufficient-entropy"
|
||||
t.Cleanup(func() { common.SessionSecret = previous })
|
||||
}
|
||||
|
||||
func TestAccessTokenRoundTripAndPurposeIsolation(t *testing.T) {
|
||||
setupAuthSessionTestDB(t)
|
||||
useTestSessionSecret(t)
|
||||
identity := AuthIdentity{UserID: 42, SessionID: "session-1", UserAuthVersion: 3, SessionVersion: 2}
|
||||
|
||||
token, expiresAt, err := IssueAccessToken(identity)
|
||||
require.NoError(t, err)
|
||||
assert.Positive(t, expiresAt)
|
||||
|
||||
parsed, err := ParseAccessToken(token)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, identity, parsed)
|
||||
|
||||
binding, err := BindVerificationOperation(VerificationOperation{Scope: "channel.key.read", Context: []byte(`{"channel_id":123}`)})
|
||||
require.NoError(t, err)
|
||||
proof, _, err := IssueSecurityProof(identity, "2fa", binding)
|
||||
require.NoError(t, err)
|
||||
_, err = ParseAccessToken(proof)
|
||||
assert.ErrorIs(t, err, ErrAuthTokenInvalid)
|
||||
}
|
||||
|
||||
func TestAccessTokenRejectsTampering(t *testing.T) {
|
||||
useTestSessionSecret(t)
|
||||
identity := AuthIdentity{UserID: 42, SessionID: "session-1", UserAuthVersion: 1, SessionVersion: 1}
|
||||
token, _, err := IssueAccessToken(identity)
|
||||
require.NoError(t, err)
|
||||
|
||||
tamperAt := len(token) - 2
|
||||
replacement := "x"
|
||||
if token[tamperAt] == 'x' {
|
||||
replacement = "y"
|
||||
}
|
||||
tampered := token[:tamperAt] + replacement + token[tamperAt+1:]
|
||||
_, err = ParseAccessToken(tampered)
|
||||
assert.ErrorIs(t, err, ErrAuthTokenInvalid)
|
||||
|
||||
_, internal, err := ParseDashboardAccessToken(tampered)
|
||||
assert.True(t, internal)
|
||||
assert.ErrorIs(t, err, ErrAuthTokenInvalid)
|
||||
}
|
||||
|
||||
func TestDashboardAccessTokenClassification(t *testing.T) {
|
||||
setupAuthSessionTestDB(t)
|
||||
useTestSessionSecret(t)
|
||||
|
||||
identity, internal, err := ParseDashboardAccessToken("opaque.key.with-dots")
|
||||
require.NoError(t, err)
|
||||
assert.False(t, internal)
|
||||
assert.Empty(t, identity)
|
||||
|
||||
external := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
|
||||
"iss": "external-issuer",
|
||||
"aud": authTokenAudience,
|
||||
"exp": time.Now().Add(time.Minute).Unix(),
|
||||
})
|
||||
externalRaw, err := external.SignedString([]byte("external-secret"))
|
||||
require.NoError(t, err)
|
||||
_, internal, err = ParseDashboardAccessToken(externalRaw)
|
||||
require.NoError(t, err)
|
||||
assert.False(t, internal)
|
||||
|
||||
unknownUse := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
|
||||
"iss": authTokenIssuer,
|
||||
"aud": authTokenAudience,
|
||||
"token_use": "third_party",
|
||||
"exp": time.Now().Add(time.Minute).Unix(),
|
||||
})
|
||||
unknownUseRaw, err := unknownUse.SignedString([]byte("external-secret"))
|
||||
require.NoError(t, err)
|
||||
_, internal, err = ParseDashboardAccessToken(unknownUseRaw)
|
||||
require.NoError(t, err)
|
||||
assert.False(t, internal)
|
||||
|
||||
binding, err := BindVerificationOperation(VerificationOperation{Scope: "channel.key.read", Context: []byte(`{"channel_id":123}`)})
|
||||
require.NoError(t, err)
|
||||
proof, _, err := IssueSecurityProof(AuthIdentity{
|
||||
UserID: 42, SessionID: "session-1", UserAuthVersion: 1, SessionVersion: 1,
|
||||
}, "2fa", binding)
|
||||
require.NoError(t, err)
|
||||
_, internal, err = ParseDashboardAccessToken(proof)
|
||||
assert.True(t, internal)
|
||||
assert.ErrorIs(t, err, ErrAuthTokenInvalid)
|
||||
|
||||
expiredClaims := authClaims{
|
||||
TokenUse: accessTokenUse,
|
||||
SessionID: "expired-session",
|
||||
UserAuthVersion: 1,
|
||||
SessionVersion: 1,
|
||||
RegisteredClaims: jwt.RegisteredClaims{
|
||||
Issuer: authTokenIssuer,
|
||||
Subject: "42",
|
||||
Audience: jwt.ClaimStrings{authTokenAudience},
|
||||
ExpiresAt: jwt.NewNumericDate(time.Now().Add(-time.Minute)),
|
||||
NotBefore: jwt.NewNumericDate(time.Now().Add(-2 * time.Minute)),
|
||||
IssuedAt: jwt.NewNumericDate(time.Now().Add(-2 * time.Minute)),
|
||||
ID: "expired-token",
|
||||
},
|
||||
}
|
||||
expired, err := jwt.NewWithClaims(jwt.SigningMethodHS256, expiredClaims).SignedString(authSigningKey(accessTokenUse))
|
||||
require.NoError(t, err)
|
||||
_, internal, err = ParseDashboardAccessToken(expired)
|
||||
assert.True(t, internal)
|
||||
assert.ErrorIs(t, err, ErrAuthTokenExpired)
|
||||
}
|
||||
|
||||
func TestSecurityProofBindsIdentityAndOperation(t *testing.T) {
|
||||
setupAuthSessionTestDB(t)
|
||||
useTestSessionSecret(t)
|
||||
identity := AuthIdentity{UserID: 42, SessionID: "session-1", UserAuthVersion: 3, SessionVersion: 2}
|
||||
operation := VerificationOperation{Scope: "channel.key.read", Context: []byte(`{"channel_id":123}`)}
|
||||
binding, err := BindVerificationOperation(operation)
|
||||
require.NoError(t, err)
|
||||
proof, _, err := IssueSecurityProof(identity, "2fa", binding)
|
||||
require.NoError(t, err)
|
||||
|
||||
claims, err := verifySecurityProof(proof, identity, binding)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "2fa", claims.Method)
|
||||
|
||||
wrongScope, err := BindVerificationOperation(VerificationOperation{Scope: "passkey.delete"})
|
||||
require.NoError(t, err)
|
||||
_, err = verifySecurityProof(proof, identity, wrongScope)
|
||||
assert.ErrorIs(t, err, ErrProofScope)
|
||||
|
||||
otherChannel, err := BindVerificationOperation(VerificationOperation{Scope: "channel.key.read", Context: []byte(`{"channel_id":456}`)})
|
||||
require.NoError(t, err)
|
||||
_, err = verifySecurityProof(proof, identity, otherChannel)
|
||||
assert.ErrorIs(t, err, ErrProofContext)
|
||||
|
||||
otherSession := identity
|
||||
otherSession.SessionID = "session-2"
|
||||
_, err = verifySecurityProof(proof, otherSession, binding)
|
||||
assert.True(t, errors.Is(err, ErrAuthTokenInvalid))
|
||||
|
||||
claims, err = parseAuthClaims(proof, securityProofTokenUse, authSigningKey(securityProofTokenUse))
|
||||
require.NoError(t, err)
|
||||
claims.ExpiresAt = jwt.NewNumericDate(time.Now().Add(-time.Minute))
|
||||
expired, err := jwt.NewWithClaims(jwt.SigningMethodHS256, claims).SignedString(authSigningKey(securityProofTokenUse))
|
||||
require.NoError(t, err)
|
||||
_, err = ConsumeOperationProof(expired, identity, operation)
|
||||
assert.ErrorIs(t, err, ErrAuthTokenExpired)
|
||||
}
|
||||
Reference in new issue
Block a user