mirror of
https://github.com/openziti/ziti.git
synced 2026-09-10 00:35:41 +00:00
948735d86c
* fixes openziti/ziti#4094 accept first-party certs issued by a separate edge signing CA - adds FirstPartyX509CertValidation and ThirdPartyX509CertValidation usages and an intermediates field to the router data model public keys, deprecating ClientX509CertValidation - publishes config CA bundle roots as first-party anchors with their intermediates and Ca store entries as third-party anchors; controller certs carry JWT validation only, since a controller identity is never a CA and anchors no client cert chains - builds router first-party and client cert trust pools from the published usages, falling back to the deprecated usage against older controllers - propagates the full signing cert chain between controllers via a new mesh SigningCertChainHeader and persists whole chains in controller records - removes the orphaned InstantStrategy.AddPublicKey, dead since public key sync moved to controller list data - gives each command dispatcher its own decoder registry so multiple in-process controllers no longer decode into the last-started controller's managers - adds a three-controller in-process HA test harness with a split signing PKI, cluster formation and first-party cert integration tests - trusts the edge signing CA when verifying router control channel certs - adds a variadic additionalRoots parameter to VerifyLeafCertChain, applied to a clone of the caller's pool so an identity's live tls.Configs are unaffected - passes the edge enrollment signing CA bundle as additional roots when admitting a router control channel connection, so a deployment whose signing CA sits outside the controller's own trust bundle no longer has every router refused - leaves the fingerprint check bound to the verified leaf, so the wider anchor set changes which chains verify, not which routers are admitted - covers the split-root case and the caller-pool guarantee in common/cert tests
206 lines
6.5 KiB
Go
206 lines
6.5 KiB
Go
package tests
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/golang-jwt/jwt/v5"
|
|
edge_apis "github.com/openziti/sdk-golang/v2/edge-apis"
|
|
"github.com/openziti/ziti/v2/common"
|
|
)
|
|
|
|
func Test_API_Session_TOTP_Tokens(t *testing.T) {
|
|
ctx := NewTestContext(t)
|
|
defer ctx.Teardown()
|
|
ctx.StartServer()
|
|
|
|
managementApi := ctx.NewEdgeManagementApi(nil)
|
|
adminCreds := ctx.NewAdminCredentials()
|
|
|
|
apiSession, err := managementApi.Authenticate(adminCreds, nil)
|
|
ctx.Req.NoError(err)
|
|
ctx.Req.NotNil(apiSession)
|
|
|
|
t.Run("non admins in the client api", func(t *testing.T) {
|
|
|
|
ctx.testContextChanged(t)
|
|
|
|
nonAdminIdentity, nonAdminCreds, err := managementApi.CreateAndEnrollOttIdentity(false)
|
|
ctx.Req.NoError(err)
|
|
ctx.Req.NotNil(nonAdminIdentity)
|
|
ctx.Req.NotNil(nonAdminCreds)
|
|
|
|
nonAdminClientClient := ctx.NewEdgeClientApi(nil)
|
|
|
|
nonAdminApiSession, err := nonAdminClientClient.Authenticate(nonAdminCreds, nil)
|
|
ctx.Req.NoError(err)
|
|
ctx.Req.NotNil(nonAdminApiSession)
|
|
|
|
nonAdminTotpProvider, nonAdminTotpDetail, err := nonAdminClientClient.EnrollTotpMfa()
|
|
ctx.Req.NoError(err)
|
|
ctx.Req.NotNil(nonAdminTotpDetail)
|
|
ctx.Req.NotNil(nonAdminTotpProvider)
|
|
|
|
t.Run("cannot create totp tokens using legacy authentication", func(t *testing.T) {
|
|
ctx.testContextChanged(t)
|
|
|
|
nonAdminClientClient = ctx.NewEdgeClientApi(nonAdminTotpProvider.FuncProvider())
|
|
nonAdminClientClient.SetUseOidc(false)
|
|
nonAdminClientClient.SetAllowOidcDynamicallyEnabled(false)
|
|
|
|
nonAdminApiSession, err = nonAdminClientClient.Authenticate(nonAdminCreds, nil)
|
|
ctx.Req.NoError(err)
|
|
ctx.Req.NotNil(nonAdminApiSession)
|
|
ctx.Req.Equal(edge_apis.ApiSessionTypeLegacy, nonAdminApiSession.GetType())
|
|
|
|
code := nonAdminTotpProvider.Code()
|
|
|
|
token, err := nonAdminClientClient.GetTotpToken(code)
|
|
ctx.Req.Error(err)
|
|
ctx.Req.Empty(token)
|
|
})
|
|
|
|
t.Run("can create totp tokens using oidc authentication", func(t *testing.T) {
|
|
ctx.testContextChanged(t)
|
|
|
|
nonAdminClientClient = ctx.NewEdgeClientApi(nonAdminTotpProvider.FuncProvider())
|
|
nonAdminClientClient.SetUseOidc(true)
|
|
nonAdminClientClient.SetAllowOidcDynamicallyEnabled(true)
|
|
|
|
nonAdminApiSession, err = nonAdminClientClient.Authenticate(nonAdminCreds, nil)
|
|
ctx.Req.NoError(err)
|
|
ctx.Req.NotNil(nonAdminApiSession)
|
|
ctx.Req.Equal(edge_apis.ApiSessionTypeOidc, nonAdminApiSession.GetType())
|
|
|
|
code := nonAdminTotpProvider.Code()
|
|
|
|
totpToken, err := nonAdminClientClient.GetTotpToken(code)
|
|
ctx.Req.NoError(err)
|
|
ctx.Req.NotNil(totpToken)
|
|
ctx.Req.NotNil(totpToken.Token)
|
|
|
|
t.Run("the token is a JWT and has the correct values", func(t *testing.T) {
|
|
ctx.testContextChanged(t)
|
|
|
|
totpClaims := &common.TotpClaims{}
|
|
totpJwtToken, err := jwt.ParseWithClaims(*totpToken.Token, totpClaims, ctx.EdgeController.AppEnv.JwtSignerKeyFunc)
|
|
ctx.NoError(err)
|
|
ctx.NotNil(totpJwtToken)
|
|
|
|
accessClaims := &common.AccessClaims{}
|
|
apiSessionJwtToken, err := jwt.ParseWithClaims(string(nonAdminApiSession.GetToken()), accessClaims, ctx.EdgeController.AppEnv.JwtSignerKeyFunc)
|
|
ctx.NoError(err)
|
|
ctx.NotNil(apiSessionJwtToken)
|
|
|
|
ctx.Req.NoError(err)
|
|
ctx.NotNil(totpJwtToken)
|
|
|
|
ctx.Equal(common.TokenTypeTotp, totpClaims.Type)
|
|
|
|
expTime, err := totpJwtToken.Claims.GetExpirationTime()
|
|
ctx.NoError(err)
|
|
ctx.Nil(expTime)
|
|
|
|
ctx.Equal(accessClaims.ApiSessionId, totpClaims.ApiSessionId)
|
|
|
|
})
|
|
})
|
|
})
|
|
|
|
t.Run("admins in the management api", func(t *testing.T) {
|
|
|
|
ctx.testContextChanged(t)
|
|
|
|
adminIdentity, adminCreds, err := managementApi.CreateAndEnrollOttIdentity(true)
|
|
ctx.Req.NoError(err)
|
|
ctx.Req.NotNil(adminIdentity)
|
|
ctx.Req.NotNil(adminCreds)
|
|
|
|
adminManagementClient := ctx.NewEdgeManagementApi(nil)
|
|
|
|
adminApiSession, err := adminManagementClient.Authenticate(adminCreds, nil)
|
|
ctx.Req.NoError(err)
|
|
ctx.Req.NotNil(adminApiSession)
|
|
|
|
adminTotpProvider, adminTotpDetail, err := adminManagementClient.EnrollTotpMfa()
|
|
ctx.Req.NoError(err)
|
|
ctx.Req.NotNil(adminTotpDetail)
|
|
ctx.Req.NotNil(adminTotpProvider)
|
|
|
|
t.Run("cannot create totp tokens using legacy authentication", func(t *testing.T) {
|
|
ctx.testContextChanged(t)
|
|
|
|
adminManagementClient = ctx.NewEdgeManagementApi(adminTotpProvider.FuncProvider())
|
|
adminManagementClient.SetUseOidc(false)
|
|
adminManagementClient.SetAllowOidcDynamicallyEnabled(false)
|
|
|
|
adminApiSession, err = adminManagementClient.Authenticate(adminCreds, nil)
|
|
ctx.Req.NoError(err)
|
|
ctx.Req.NotNil(adminApiSession)
|
|
ctx.Req.Equal(edge_apis.ApiSessionTypeLegacy, adminApiSession.GetType())
|
|
|
|
code := adminTotpProvider.Code()
|
|
|
|
token, err := adminManagementClient.GetTotpToken(code)
|
|
ctx.Req.Error(err)
|
|
ctx.Req.Empty(token)
|
|
})
|
|
|
|
t.Run("can create totp tokens using oidc authentication", func(t *testing.T) {
|
|
ctx.testContextChanged(t)
|
|
|
|
adminManagementClient = ctx.NewEdgeManagementApi(adminTotpProvider.FuncProvider())
|
|
adminManagementClient.SetUseOidc(true)
|
|
adminManagementClient.SetAllowOidcDynamicallyEnabled(true)
|
|
|
|
adminApiSession, err = adminManagementClient.Authenticate(adminCreds, nil)
|
|
ctx.Req.NoError(err)
|
|
ctx.Req.NotNil(adminApiSession)
|
|
ctx.Req.Equal(edge_apis.ApiSessionTypeOidc, adminApiSession.GetType())
|
|
|
|
code := adminTotpProvider.Code()
|
|
|
|
beforeTotpCreate := time.Now()
|
|
totpToken, err := adminManagementClient.GetTotpToken(code)
|
|
afterTotpCreate := time.Now()
|
|
ctx.Req.NoError(err)
|
|
ctx.Req.NotNil(totpToken)
|
|
ctx.Req.NotNil(totpToken.Token)
|
|
|
|
t.Run("the token is a JWT and has the correct values", func(t *testing.T) {
|
|
ctx.testContextChanged(t)
|
|
|
|
totpClaims := &common.TotpClaims{}
|
|
totpJwtToken, err := jwt.ParseWithClaims(*totpToken.Token, totpClaims, ctx.EdgeController.AppEnv.JwtSignerKeyFunc)
|
|
ctx.NoError(err)
|
|
ctx.NotNil(totpJwtToken)
|
|
|
|
accessClaims := &common.AccessClaims{}
|
|
apiSessionJwtToken, err := jwt.ParseWithClaims(string(adminApiSession.GetToken()), accessClaims, ctx.EdgeController.AppEnv.JwtSignerKeyFunc)
|
|
ctx.NoError(err)
|
|
ctx.NotNil(apiSessionJwtToken)
|
|
|
|
ctx.Req.NoError(err)
|
|
ctx.NotNil(totpJwtToken)
|
|
|
|
ctx.Equal(common.TokenTypeTotp, totpClaims.Type)
|
|
|
|
expTime, err := totpJwtToken.Claims.GetExpirationTime()
|
|
ctx.NoError(err)
|
|
ctx.Nil(expTime)
|
|
|
|
issuedAt, err := totpJwtToken.Claims.GetIssuedAt()
|
|
ctx.NoError(err)
|
|
ctx.NotNil(issuedAt)
|
|
|
|
// The serialized iat truncates to whole seconds, so the lower bound must too.
|
|
ctx.Req.WithinRange(issuedAt.Time, beforeTotpCreate.Truncate(time.Second), afterTotpCreate)
|
|
|
|
ctx.Equal(accessClaims.ApiSessionId, totpClaims.ApiSessionId)
|
|
|
|
})
|
|
})
|
|
})
|
|
|
|
}
|