mirror of
https://github.com/hanzozt/zt.git
synced 2026-10-30 15:04:55 +00:00
688 lines
25 KiB
Go
688 lines
25 KiB
Go
//go:build apitests
|
|
// +build apitests
|
|
|
|
/*
|
|
Copyright NetFoundry Inc.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
https://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
package tests
|
|
|
|
import (
|
|
"github.com/golang-jwt/jwt/v5"
|
|
"github.com/google/uuid"
|
|
"github.com/openziti/edge-api/rest_model"
|
|
nfpem "github.com/openziti/foundation/v2/pem"
|
|
"net/http"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func Test_Authenticate_External_Jwt(t *testing.T) {
|
|
ctx := NewTestContext(t)
|
|
defer ctx.Teardown()
|
|
ctx.StartServer()
|
|
ctx.RequireAdminManagementApiLogin()
|
|
|
|
var signerIds []string
|
|
|
|
// create a bunch of signers to use
|
|
|
|
//valid signer with issuer and audience
|
|
validJwtSignerCert, validJwtSignerPrivateKey := newSelfSignedCert("valid signer")
|
|
validJwtSignerCertPem := nfpem.EncodeToString(validJwtSignerCert)
|
|
|
|
validJwtSigner := &rest_model.ExternalJWTSignerCreate{
|
|
CertPem: &validJwtSignerCertPem,
|
|
Enabled: B(true),
|
|
Name: S("Test JWT Signer - Enabled"),
|
|
Kid: S(uuid.NewString()),
|
|
Issuer: S("the-very-best-iss"),
|
|
Audience: S("the-very-best-aud"),
|
|
}
|
|
|
|
createResponseEnv := &rest_model.CreateEnvelope{}
|
|
|
|
resp, err := ctx.AdminManagementSession.newAuthenticatedRequest().SetBody(validJwtSigner).SetResult(createResponseEnv).Post("/external-jwt-signers")
|
|
ctx.Req.NoError(err)
|
|
ctx.Req.Equal(http.StatusCreated, resp.StatusCode())
|
|
|
|
signerIds = append(signerIds, createResponseEnv.Data.ID)
|
|
|
|
//valid signer w/ external id
|
|
validExtIdJwtSignerCert, validExtIdJwtSignerPrivateKey := newSelfSignedCert("valid signer")
|
|
validExtIdJwtSignerCertPem := nfpem.EncodeToString(validExtIdJwtSignerCert)
|
|
|
|
validExtIdJwtSigner := &rest_model.ExternalJWTSignerCreate{
|
|
CertPem: &validExtIdJwtSignerCertPem,
|
|
Enabled: B(true),
|
|
Name: S("Test JWT Signer - Enabled - ExternalId"),
|
|
Kid: S(uuid.NewString()),
|
|
Issuer: S("the-very-best-iss-ext"),
|
|
Audience: S("the-very-best-aud-ext"),
|
|
UseExternalID: B(true),
|
|
}
|
|
|
|
createResponseEnv = &rest_model.CreateEnvelope{}
|
|
|
|
resp, err = ctx.AdminManagementSession.newAuthenticatedRequest().SetBody(validExtIdJwtSigner).SetResult(createResponseEnv).Post("/external-jwt-signers")
|
|
ctx.Req.NoError(err)
|
|
ctx.Req.Equal(http.StatusCreated, resp.StatusCode())
|
|
|
|
validExtIdJwtSignerId := createResponseEnv.Data.ID
|
|
|
|
//valid signer w/ external id in "alt" field
|
|
validAltExtIdJwtSignerCert, validAltExtIdJwtSignerPrivateKey := newSelfSignedCert("valid signer")
|
|
validAltExtIdJwtSignerCertPem := nfpem.EncodeToString(validAltExtIdJwtSignerCert)
|
|
|
|
validAltExtIdJwtSigner := &rest_model.ExternalJWTSignerCreate{
|
|
CertPem: &validAltExtIdJwtSignerCertPem,
|
|
Enabled: B(true),
|
|
Name: S("Test JWT Signer - Enabled - ExternalId - Alt"),
|
|
Kid: S(uuid.NewString()),
|
|
Issuer: S("the-very-best-iss-ext-alt"),
|
|
Audience: S("the-very-best-aud-ext-alt"),
|
|
UseExternalID: B(true),
|
|
ClaimsProperty: S("alt"),
|
|
}
|
|
|
|
createResponseEnv = &rest_model.CreateEnvelope{}
|
|
|
|
resp, err = ctx.AdminManagementSession.newAuthenticatedRequest().SetBody(validAltExtIdJwtSigner).SetResult(createResponseEnv).Post("/external-jwt-signers")
|
|
ctx.Req.NoError(err)
|
|
ctx.Req.Equal(http.StatusCreated, resp.StatusCode())
|
|
|
|
validAltExtIdJwtSignerId := createResponseEnv.Data.ID
|
|
|
|
//not enabled signer
|
|
createResponseEnv = &rest_model.CreateEnvelope{}
|
|
|
|
notEnabledJwtSignerCert, notEnabledJwtSignerPrivateKey := newSelfSignedCert("not enabled signer")
|
|
notEnabledJwtSignerCertPem := nfpem.EncodeToString(notEnabledJwtSignerCert)
|
|
|
|
notEnabledJwtSigner := &rest_model.ExternalJWTSignerCreate{
|
|
CertPem: ¬EnabledJwtSignerCertPem,
|
|
Enabled: B(false),
|
|
Name: S("Test JWT Signer - Not Enabled"),
|
|
Kid: S(uuid.NewString()),
|
|
Issuer: S("test-issuer"),
|
|
Audience: S("test-audience"),
|
|
}
|
|
|
|
resp, err = ctx.AdminManagementSession.newAuthenticatedRequest().SetBody(notEnabledJwtSigner).SetResult(createResponseEnv).Post("/external-jwt-signers")
|
|
ctx.Req.NoError(err)
|
|
ctx.Req.Equal(http.StatusCreated, resp.StatusCode(), string(resp.Body()))
|
|
signerIds = append(signerIds, createResponseEnv.Data.ID)
|
|
|
|
invalidJwtSignerCommonName := "invalid signer"
|
|
invalidJwtSignerCert, invalidJwtSignerPrivateKey := newSelfSignedCert(invalidJwtSignerCommonName)
|
|
invalidJwtSignerCertFingerprint := nfpem.FingerprintFromCertificate(invalidJwtSignerCert)
|
|
|
|
authPolicyPatch := &rest_model.AuthPolicyPatch{
|
|
Primary: &rest_model.AuthPolicyPrimaryPatch{
|
|
ExtJWT: &rest_model.AuthPolicyPrimaryExtJWTPatch{
|
|
AllowedSigners: signerIds,
|
|
},
|
|
},
|
|
}
|
|
resp, err = ctx.AdminManagementSession.newAuthenticatedRequest().SetBody(authPolicyPatch).Patch("/auth-policies/default")
|
|
ctx.NoError(err)
|
|
ctx.Equal(http.StatusOK, resp.StatusCode())
|
|
|
|
t.Run("authenticating with a valid jwt succeeds", func(t *testing.T) {
|
|
ctx.testContextChanged(t)
|
|
|
|
jwtToken := jwt.New(jwt.SigningMethodES256)
|
|
jwtToken.Claims = jwt.RegisteredClaims{
|
|
Audience: []string{*validJwtSigner.Audience},
|
|
ExpiresAt: &jwt.NumericDate{Time: time.Now().Add(2 * time.Hour)},
|
|
ID: time.Now().String(),
|
|
IssuedAt: &jwt.NumericDate{Time: time.Now()},
|
|
Issuer: *validJwtSigner.Issuer,
|
|
NotBefore: &jwt.NumericDate{Time: time.Now()},
|
|
Subject: *ctx.AdminManagementSession.AuthResponse.IdentityID,
|
|
}
|
|
|
|
jwtToken.Header["kid"] = *validJwtSigner.Kid
|
|
|
|
jwtStrSigned, err := jwtToken.SignedString(validJwtSignerPrivateKey)
|
|
ctx.Req.NoError(err)
|
|
ctx.Req.NotEmpty(jwtStrSigned)
|
|
|
|
result := &rest_model.CurrentAPISessionDetailEnvelope{}
|
|
|
|
resp, err := ctx.newAnonymousClientApiRequest().SetResult(result).SetHeader("Authorization", "Bearer "+jwtStrSigned).Post("/authenticate?method=ext-jwt")
|
|
ctx.Req.NoError(err)
|
|
ctx.Req.Equal(http.StatusOK, resp.StatusCode(), string(resp.Body()))
|
|
signerIds = append(signerIds, createResponseEnv.Data.ID)
|
|
|
|
ctx.Req.NotNil(result)
|
|
ctx.Req.NotNil(result.Data)
|
|
ctx.Req.NotNil(result.Data.Token)
|
|
})
|
|
|
|
t.Run("authenticating with a valid jwt but disabled signer fails", func(t *testing.T) {
|
|
ctx.testContextChanged(t)
|
|
|
|
jwtToken := jwt.New(jwt.SigningMethodES256)
|
|
jwtToken.Claims = jwt.RegisteredClaims{
|
|
Audience: []string{"ziti.controller"},
|
|
ExpiresAt: &jwt.NumericDate{Time: time.Now().Add(2 * time.Hour)},
|
|
ID: time.Now().String(),
|
|
IssuedAt: &jwt.NumericDate{Time: time.Now()},
|
|
Issuer: "fake.issuer",
|
|
NotBefore: &jwt.NumericDate{Time: time.Now()},
|
|
Subject: *ctx.AdminManagementSession.AuthResponse.IdentityID,
|
|
}
|
|
|
|
jwtToken.Header["kid"] = *notEnabledJwtSigner.Kid
|
|
|
|
jwtStrSigned, err := jwtToken.SignedString(notEnabledJwtSignerPrivateKey)
|
|
ctx.Req.NoError(err)
|
|
ctx.Req.NotEmpty(jwtStrSigned)
|
|
|
|
result := &rest_model.CurrentAPISessionDetailEnvelope{}
|
|
|
|
resp, err := ctx.newAnonymousClientApiRequest().SetResult(result).SetHeader("Authorization", "Bearer "+jwtStrSigned).Post("/authenticate?method=ext-jwt")
|
|
ctx.Req.NoError(err)
|
|
ctx.Req.Equal(http.StatusUnauthorized, resp.StatusCode())
|
|
})
|
|
|
|
t.Run("authenticating with an invalid issuer jwt fails", func(t *testing.T) {
|
|
ctx.testContextChanged(t)
|
|
|
|
jwtToken := jwt.New(jwt.SigningMethodES256)
|
|
jwtToken.Claims = jwt.RegisteredClaims{
|
|
Audience: []string{*validJwtSigner.Audience},
|
|
ExpiresAt: &jwt.NumericDate{Time: time.Now().Add(2 * time.Hour)},
|
|
ID: time.Now().String(),
|
|
IssuedAt: &jwt.NumericDate{Time: time.Now()},
|
|
Issuer: "i will cause this to fail",
|
|
NotBefore: &jwt.NumericDate{Time: time.Now()},
|
|
Subject: *ctx.AdminManagementSession.AuthResponse.IdentityID,
|
|
}
|
|
|
|
jwtToken.Header["kid"] = *validJwtSigner.Kid
|
|
|
|
jwtStrSigned, err := jwtToken.SignedString(validJwtSignerPrivateKey)
|
|
ctx.Req.NoError(err)
|
|
ctx.Req.NotEmpty(jwtStrSigned)
|
|
|
|
result := &rest_model.CurrentAPISessionDetailEnvelope{}
|
|
|
|
resp, err := ctx.newAnonymousClientApiRequest().SetResult(result).SetHeader("Authorization", "Bearer "+jwtStrSigned).Post("/authenticate?method=ext-jwt")
|
|
ctx.Req.NoError(err)
|
|
ctx.Req.Equal(http.StatusUnauthorized, resp.StatusCode())
|
|
})
|
|
|
|
t.Run("authenticating with an invalid audience jwt fails", func(t *testing.T) {
|
|
ctx.testContextChanged(t)
|
|
|
|
jwtToken := jwt.New(jwt.SigningMethodES256)
|
|
jwtToken.Claims = jwt.RegisteredClaims{
|
|
Audience: []string{"this test shall not succeed"},
|
|
ExpiresAt: &jwt.NumericDate{Time: time.Now().Add(2 * time.Hour)},
|
|
ID: time.Now().String(),
|
|
IssuedAt: &jwt.NumericDate{Time: time.Now()},
|
|
Issuer: *validJwtSigner.Issuer,
|
|
NotBefore: &jwt.NumericDate{Time: time.Now()},
|
|
Subject: *ctx.AdminManagementSession.AuthResponse.IdentityID,
|
|
}
|
|
|
|
jwtToken.Header["kid"] = *validJwtSigner.Kid
|
|
|
|
jwtStrSigned, err := jwtToken.SignedString(validJwtSignerPrivateKey)
|
|
ctx.Req.NoError(err)
|
|
ctx.Req.NotEmpty(jwtStrSigned)
|
|
|
|
result := &rest_model.CurrentAPISessionDetailEnvelope{}
|
|
|
|
resp, err := ctx.newAnonymousClientApiRequest().SetResult(result).SetHeader("Authorization", "Bearer "+jwtStrSigned).Post("/authenticate?method=ext-jwt")
|
|
ctx.Req.NoError(err)
|
|
ctx.Req.Equal(http.StatusUnauthorized, resp.StatusCode())
|
|
})
|
|
|
|
t.Run("authenticating with a valid jwt but no kid fails", func(t *testing.T) {
|
|
ctx.testContextChanged(t)
|
|
|
|
jwtToken := jwt.New(jwt.SigningMethodES256)
|
|
jwtToken.Claims = jwt.RegisteredClaims{
|
|
Audience: []string{"ziti.controller"},
|
|
ExpiresAt: &jwt.NumericDate{Time: time.Now().Add(2 * time.Hour)},
|
|
ID: time.Now().String(),
|
|
IssuedAt: &jwt.NumericDate{Time: time.Now()},
|
|
Issuer: "fake.issuer",
|
|
NotBefore: &jwt.NumericDate{Time: time.Now()},
|
|
Subject: *ctx.AdminManagementSession.AuthResponse.IdentityID,
|
|
}
|
|
|
|
jwtStrSigned, err := jwtToken.SignedString(validJwtSignerPrivateKey)
|
|
ctx.Req.NoError(err)
|
|
ctx.Req.NotEmpty(jwtStrSigned)
|
|
|
|
result := &rest_model.CurrentAPISessionDetailEnvelope{}
|
|
|
|
resp, err := ctx.newAnonymousClientApiRequest().SetResult(result).SetHeader("Authorization", "Bearer "+jwtStrSigned).Post("/authenticate?method=ext-jwt")
|
|
ctx.Req.NoError(err)
|
|
ctx.Req.Equal(http.StatusUnauthorized, resp.StatusCode())
|
|
})
|
|
|
|
t.Run("authenticating with an jwt with unknown signer valid kid fails", func(t *testing.T) {
|
|
ctx.testContextChanged(t)
|
|
|
|
jwtToken := jwt.New(jwt.SigningMethodES256)
|
|
jwtToken.Claims = jwt.RegisteredClaims{
|
|
Audience: []string{"ziti.controller"},
|
|
ExpiresAt: &jwt.NumericDate{Time: time.Now().Add(2 * time.Hour)},
|
|
ID: time.Now().String(),
|
|
IssuedAt: &jwt.NumericDate{Time: time.Now()},
|
|
Issuer: "fake.issuer",
|
|
NotBefore: &jwt.NumericDate{Time: time.Now()},
|
|
Subject: *ctx.AdminManagementSession.AuthResponse.IdentityID,
|
|
}
|
|
|
|
jwtToken.Header["kid"] = *validJwtSigner.Kid
|
|
|
|
jwtStrSigned, err := jwtToken.SignedString(invalidJwtSignerPrivateKey)
|
|
ctx.Req.NoError(err)
|
|
ctx.Req.NotEmpty(jwtStrSigned)
|
|
|
|
result := &rest_model.CurrentAPISessionDetailEnvelope{}
|
|
|
|
resp, err := ctx.newAnonymousClientApiRequest().SetResult(result).SetHeader("Authorization", "Bearer "+jwtStrSigned).Post("/authenticate?method=ext-jwt")
|
|
ctx.Req.NoError(err)
|
|
ctx.Req.Equal(http.StatusUnauthorized, resp.StatusCode())
|
|
})
|
|
|
|
t.Run("authenticating with an jwt with unknown signer invalid kid fails", func(t *testing.T) {
|
|
ctx.testContextChanged(t)
|
|
|
|
jwtToken := jwt.New(jwt.SigningMethodES256)
|
|
jwtToken.Claims = jwt.RegisteredClaims{
|
|
Audience: []string{"ziti.controller"},
|
|
ExpiresAt: &jwt.NumericDate{Time: time.Now().Add(2 * time.Hour)},
|
|
ID: time.Now().String(),
|
|
IssuedAt: &jwt.NumericDate{Time: time.Now()},
|
|
Issuer: "fake.issuer",
|
|
NotBefore: &jwt.NumericDate{Time: time.Now()},
|
|
Subject: *ctx.AdminManagementSession.AuthResponse.IdentityID,
|
|
}
|
|
|
|
jwtToken.Header["kid"] = invalidJwtSignerCertFingerprint
|
|
|
|
jwtStrSigned, err := jwtToken.SignedString(invalidJwtSignerPrivateKey)
|
|
ctx.Req.NoError(err)
|
|
ctx.Req.NotEmpty(jwtStrSigned)
|
|
|
|
result := &rest_model.CurrentAPISessionDetailEnvelope{}
|
|
|
|
resp, err := ctx.newAnonymousClientApiRequest().SetResult(result).SetHeader("Authorization", "Bearer "+jwtStrSigned).Post("/authenticate?method=ext-jwt")
|
|
ctx.Req.NoError(err)
|
|
ctx.Req.Equal(http.StatusUnauthorized, resp.StatusCode())
|
|
})
|
|
|
|
t.Run("authenticating with a malformed jwt fails", func(t *testing.T) {
|
|
ctx.testContextChanged(t)
|
|
result := &rest_model.CurrentAPISessionDetailEnvelope{}
|
|
|
|
resp, err := ctx.newAnonymousClientApiRequest().SetResult(result).SetHeader("Authorization", "Bearer 03qwjg03rjg90qr3hngq4390rng93q0rnghq430rng34r90ng4309vn439043").Post("/authenticate?method=ext-jwt")
|
|
ctx.Req.NoError(err)
|
|
ctx.Req.Equal(http.StatusUnauthorized, resp.StatusCode())
|
|
})
|
|
|
|
t.Run("can authenticate with an external id", func(t *testing.T) {
|
|
ctx.testContextChanged(t)
|
|
|
|
externalId := uuid.NewString()
|
|
|
|
authPolicyCreate := &rest_model.AuthPolicyCreate{
|
|
Name: S(uuid.NewString()),
|
|
Primary: &rest_model.AuthPolicyPrimary{
|
|
Cert: &rest_model.AuthPolicyPrimaryCert{
|
|
Allowed: B(false),
|
|
AllowExpiredCerts: B(false),
|
|
},
|
|
ExtJWT: &rest_model.AuthPolicyPrimaryExtJWT{
|
|
Allowed: B(true),
|
|
AllowedSigners: []string{validExtIdJwtSignerId},
|
|
},
|
|
Updb: &rest_model.AuthPolicyPrimaryUpdb{
|
|
Allowed: B(false),
|
|
LockoutDurationMinutes: I(5),
|
|
MaxAttempts: I(3),
|
|
MinPasswordLength: I(5),
|
|
RequireMixedCase: B(true),
|
|
RequireNumberChar: B(true),
|
|
RequireSpecialChar: B(true),
|
|
},
|
|
},
|
|
Secondary: &rest_model.AuthPolicySecondary{
|
|
RequireExtJWTSigner: nil,
|
|
RequireTotp: B(false),
|
|
},
|
|
}
|
|
authPolicyCreateResult := &rest_model.CreateEnvelope{}
|
|
|
|
resp, err = ctx.AdminManagementSession.newAuthenticatedRequest().SetBody(authPolicyCreate).SetResult(authPolicyCreateResult).Post("/auth-policies")
|
|
ctx.NoError(err)
|
|
ctx.Equal(http.StatusCreated, resp.StatusCode(), string(resp.Body()))
|
|
ctx.NotNil(authPolicyCreateResult)
|
|
ctx.NotNil(authPolicyCreateResult.Data)
|
|
ctx.NotEmpty(authPolicyCreateResult.Data.ID)
|
|
|
|
identityType := rest_model.IdentityTypeUser
|
|
|
|
identityCreate := rest_model.IdentityCreate{
|
|
AuthPolicyID: S(authPolicyCreateResult.Data.ID),
|
|
ExternalID: S(externalId),
|
|
IsAdmin: B(false),
|
|
Name: S(uuid.NewString()),
|
|
Type: &identityType,
|
|
}
|
|
|
|
identityCreateResult := &rest_model.CreateEnvelope{}
|
|
|
|
resp, err = ctx.AdminManagementSession.newAuthenticatedRequest().SetBody(identityCreate).SetResult(identityCreateResult).Post("/identities")
|
|
ctx.NoError(err)
|
|
ctx.Equal(http.StatusCreated, resp.StatusCode())
|
|
ctx.NotNil(identityCreateResult)
|
|
ctx.NotNil(identityCreateResult.Data)
|
|
ctx.NotEmpty(identityCreateResult.Data.ID)
|
|
|
|
jwtToken := jwt.New(jwt.SigningMethodES256)
|
|
jwtToken.Claims = jwt.RegisteredClaims{
|
|
Audience: []string{*validExtIdJwtSigner.Audience},
|
|
ExpiresAt: &jwt.NumericDate{Time: time.Now().Add(2 * time.Hour)},
|
|
ID: time.Now().String(),
|
|
IssuedAt: &jwt.NumericDate{Time: time.Now()},
|
|
Issuer: *validExtIdJwtSigner.Issuer,
|
|
NotBefore: &jwt.NumericDate{Time: time.Now()},
|
|
Subject: externalId,
|
|
}
|
|
|
|
jwtToken.Header["kid"] = *validExtIdJwtSigner.Kid
|
|
|
|
jwtStrSigned, err := jwtToken.SignedString(validExtIdJwtSignerPrivateKey)
|
|
ctx.Req.NoError(err)
|
|
ctx.Req.NotEmpty(jwtStrSigned)
|
|
|
|
result := &rest_model.CurrentAPISessionDetailEnvelope{}
|
|
|
|
resp, err := ctx.newAnonymousClientApiRequest().SetResult(result).SetHeader("Authorization", "Bearer "+jwtStrSigned).Post("/authenticate?method=ext-jwt")
|
|
ctx.Req.NoError(err)
|
|
ctx.Req.Equal(http.StatusOK, resp.StatusCode(), string(resp.Body()))
|
|
|
|
ctx.Req.NotNil(result)
|
|
ctx.Req.NotNil(result.Data)
|
|
ctx.Req.NotNil(result.Data.Token)
|
|
})
|
|
|
|
t.Run("can not authenticate with an invalid external id", func(t *testing.T) {
|
|
ctx.testContextChanged(t)
|
|
|
|
externalId := uuid.NewString()
|
|
|
|
authPolicyCreate := &rest_model.AuthPolicyCreate{
|
|
Name: S(uuid.NewString()),
|
|
Primary: &rest_model.AuthPolicyPrimary{
|
|
Cert: &rest_model.AuthPolicyPrimaryCert{
|
|
Allowed: B(false),
|
|
AllowExpiredCerts: B(false),
|
|
},
|
|
ExtJWT: &rest_model.AuthPolicyPrimaryExtJWT{
|
|
Allowed: B(true),
|
|
AllowedSigners: []string{validExtIdJwtSignerId},
|
|
},
|
|
Updb: &rest_model.AuthPolicyPrimaryUpdb{
|
|
Allowed: B(false),
|
|
LockoutDurationMinutes: I(5),
|
|
MaxAttempts: I(3),
|
|
MinPasswordLength: I(5),
|
|
RequireMixedCase: B(true),
|
|
RequireNumberChar: B(true),
|
|
RequireSpecialChar: B(true),
|
|
},
|
|
},
|
|
Secondary: &rest_model.AuthPolicySecondary{
|
|
RequireExtJWTSigner: nil,
|
|
RequireTotp: B(false),
|
|
},
|
|
}
|
|
authPolicyCreateResult := &rest_model.CreateEnvelope{}
|
|
|
|
resp, err = ctx.AdminManagementSession.newAuthenticatedRequest().SetBody(authPolicyCreate).SetResult(authPolicyCreateResult).Post("/auth-policies")
|
|
ctx.NoError(err)
|
|
ctx.Equal(http.StatusCreated, resp.StatusCode(), string(resp.Body()))
|
|
ctx.NotNil(authPolicyCreateResult)
|
|
ctx.NotNil(authPolicyCreateResult.Data)
|
|
ctx.NotEmpty(authPolicyCreateResult.Data.ID)
|
|
|
|
identityType := rest_model.IdentityTypeUser
|
|
|
|
identityCreate := rest_model.IdentityCreate{
|
|
AuthPolicyID: S(authPolicyCreateResult.Data.ID),
|
|
ExternalID: S(externalId),
|
|
IsAdmin: B(false),
|
|
Name: S(uuid.NewString()),
|
|
Type: &identityType,
|
|
}
|
|
|
|
identityCreateResult := &rest_model.CreateEnvelope{}
|
|
|
|
resp, err = ctx.AdminManagementSession.newAuthenticatedRequest().SetBody(identityCreate).SetResult(identityCreateResult).Post("/identities")
|
|
ctx.NoError(err)
|
|
ctx.Equal(http.StatusCreated, resp.StatusCode())
|
|
ctx.NotNil(identityCreateResult)
|
|
ctx.NotNil(identityCreateResult.Data)
|
|
ctx.NotEmpty(identityCreateResult.Data.ID)
|
|
|
|
jwtToken := jwt.New(jwt.SigningMethodES256)
|
|
jwtToken.Claims = jwt.RegisteredClaims{
|
|
Audience: []string{*validExtIdJwtSigner.Audience},
|
|
ExpiresAt: &jwt.NumericDate{Time: time.Now().Add(2 * time.Hour)},
|
|
ID: time.Now().String(),
|
|
IssuedAt: &jwt.NumericDate{Time: time.Now()},
|
|
Issuer: *validExtIdJwtSigner.Issuer,
|
|
NotBefore: &jwt.NumericDate{Time: time.Now()},
|
|
Subject: uuid.NewString(),
|
|
}
|
|
|
|
jwtToken.Header["kid"] = *validExtIdJwtSigner.Kid
|
|
|
|
jwtStrSigned, err := jwtToken.SignedString(validExtIdJwtSignerPrivateKey)
|
|
ctx.Req.NoError(err)
|
|
ctx.Req.NotEmpty(jwtStrSigned)
|
|
|
|
resp, err := ctx.newAnonymousClientApiRequest().SetHeader("Authorization", "Bearer "+jwtStrSigned).Post("/authenticate?method=ext-jwt")
|
|
ctx.Req.NoError(err)
|
|
ctx.Req.Equal(http.StatusUnauthorized, resp.StatusCode(), string(resp.Body()))
|
|
})
|
|
|
|
t.Run("can not authenticate with an external id signed by the wrong signer", func(t *testing.T) {
|
|
ctx.testContextChanged(t)
|
|
|
|
externalId := uuid.NewString()
|
|
|
|
authPolicyCreate := &rest_model.AuthPolicyCreate{
|
|
Name: S(uuid.NewString()),
|
|
Primary: &rest_model.AuthPolicyPrimary{
|
|
Cert: &rest_model.AuthPolicyPrimaryCert{
|
|
Allowed: B(false),
|
|
AllowExpiredCerts: B(false),
|
|
},
|
|
ExtJWT: &rest_model.AuthPolicyPrimaryExtJWT{
|
|
Allowed: B(true),
|
|
AllowedSigners: []string{validExtIdJwtSignerId},
|
|
},
|
|
Updb: &rest_model.AuthPolicyPrimaryUpdb{
|
|
Allowed: B(false),
|
|
LockoutDurationMinutes: I(5),
|
|
MaxAttempts: I(3),
|
|
MinPasswordLength: I(5),
|
|
RequireMixedCase: B(true),
|
|
RequireNumberChar: B(true),
|
|
RequireSpecialChar: B(true),
|
|
},
|
|
},
|
|
Secondary: &rest_model.AuthPolicySecondary{
|
|
RequireExtJWTSigner: nil,
|
|
RequireTotp: B(false),
|
|
},
|
|
}
|
|
authPolicyCreateResult := &rest_model.CreateEnvelope{}
|
|
|
|
resp, err = ctx.AdminManagementSession.newAuthenticatedRequest().SetBody(authPolicyCreate).SetResult(authPolicyCreateResult).Post("/auth-policies")
|
|
ctx.NoError(err)
|
|
ctx.Equal(http.StatusCreated, resp.StatusCode(), string(resp.Body()))
|
|
ctx.NotNil(authPolicyCreateResult)
|
|
ctx.NotNil(authPolicyCreateResult.Data)
|
|
ctx.NotEmpty(authPolicyCreateResult.Data.ID)
|
|
|
|
identityType := rest_model.IdentityTypeUser
|
|
|
|
identityCreate := rest_model.IdentityCreate{
|
|
AuthPolicyID: S(authPolicyCreateResult.Data.ID),
|
|
ExternalID: S(externalId),
|
|
IsAdmin: B(false),
|
|
Name: S(uuid.NewString()),
|
|
Type: &identityType,
|
|
}
|
|
|
|
identityCreateResult := &rest_model.CreateEnvelope{}
|
|
|
|
resp, err = ctx.AdminManagementSession.newAuthenticatedRequest().SetBody(identityCreate).SetResult(identityCreateResult).Post("/identities")
|
|
ctx.NoError(err)
|
|
ctx.Equal(http.StatusCreated, resp.StatusCode())
|
|
ctx.NotNil(identityCreateResult)
|
|
ctx.NotNil(identityCreateResult.Data)
|
|
ctx.NotEmpty(identityCreateResult.Data.ID)
|
|
|
|
jwtToken := jwt.New(jwt.SigningMethodES256)
|
|
jwtToken.Claims = jwt.RegisteredClaims{
|
|
Audience: []string{*validExtIdJwtSigner.Audience},
|
|
ExpiresAt: &jwt.NumericDate{Time: time.Now().Add(2 * time.Hour)},
|
|
ID: time.Now().String(),
|
|
IssuedAt: &jwt.NumericDate{Time: time.Now()},
|
|
Issuer: *validExtIdJwtSigner.Issuer,
|
|
NotBefore: &jwt.NumericDate{Time: time.Now()},
|
|
Subject: externalId,
|
|
}
|
|
|
|
jwtToken.Header["kid"] = *validExtIdJwtSigner.Kid
|
|
|
|
jwtStrSigned, err := jwtToken.SignedString(notEnabledJwtSignerPrivateKey)
|
|
ctx.Req.NoError(err)
|
|
ctx.Req.NotEmpty(jwtStrSigned)
|
|
|
|
resp, err := ctx.newAnonymousClientApiRequest().SetHeader("Authorization", "Bearer "+jwtStrSigned).Post("/authenticate?method=ext-jwt")
|
|
ctx.Req.NoError(err)
|
|
ctx.Req.Equal(http.StatusUnauthorized, resp.StatusCode(), string(resp.Body()))
|
|
})
|
|
|
|
t.Run("can authenticate with an external id in a non-sub (alt) field", func(t *testing.T) {
|
|
ctx.testContextChanged(t)
|
|
|
|
externalId := uuid.NewString()
|
|
|
|
authPolicyCreate := &rest_model.AuthPolicyCreate{
|
|
Name: S(uuid.NewString()),
|
|
Primary: &rest_model.AuthPolicyPrimary{
|
|
Cert: &rest_model.AuthPolicyPrimaryCert{
|
|
Allowed: B(false),
|
|
AllowExpiredCerts: B(false),
|
|
},
|
|
ExtJWT: &rest_model.AuthPolicyPrimaryExtJWT{
|
|
Allowed: B(true),
|
|
AllowedSigners: []string{validAltExtIdJwtSignerId},
|
|
},
|
|
Updb: &rest_model.AuthPolicyPrimaryUpdb{
|
|
Allowed: B(false),
|
|
LockoutDurationMinutes: I(5),
|
|
MaxAttempts: I(3),
|
|
MinPasswordLength: I(5),
|
|
RequireMixedCase: B(true),
|
|
RequireNumberChar: B(true),
|
|
RequireSpecialChar: B(true),
|
|
},
|
|
},
|
|
Secondary: &rest_model.AuthPolicySecondary{
|
|
RequireExtJWTSigner: nil,
|
|
RequireTotp: B(false),
|
|
},
|
|
}
|
|
authPolicyCreateResult := &rest_model.CreateEnvelope{}
|
|
|
|
resp, err = ctx.AdminManagementSession.newAuthenticatedRequest().SetBody(authPolicyCreate).SetResult(authPolicyCreateResult).Post("/auth-policies")
|
|
ctx.NoError(err)
|
|
ctx.Equal(http.StatusCreated, resp.StatusCode(), string(resp.Body()))
|
|
ctx.NotNil(authPolicyCreateResult)
|
|
ctx.NotNil(authPolicyCreateResult.Data)
|
|
ctx.NotEmpty(authPolicyCreateResult.Data.ID)
|
|
|
|
identityType := rest_model.IdentityTypeUser
|
|
|
|
identityCreate := rest_model.IdentityCreate{
|
|
AuthPolicyID: S(authPolicyCreateResult.Data.ID),
|
|
ExternalID: S(externalId),
|
|
IsAdmin: B(false),
|
|
Name: S(uuid.NewString()),
|
|
Type: &identityType,
|
|
}
|
|
|
|
identityCreateResult := &rest_model.CreateEnvelope{}
|
|
|
|
resp, err = ctx.AdminManagementSession.newAuthenticatedRequest().SetBody(identityCreate).SetResult(identityCreateResult).Post("/identities")
|
|
ctx.NoError(err)
|
|
ctx.Equal(http.StatusCreated, resp.StatusCode())
|
|
ctx.NotNil(identityCreateResult)
|
|
ctx.NotNil(identityCreateResult.Data)
|
|
ctx.NotEmpty(identityCreateResult.Data.ID)
|
|
|
|
type altClaims struct {
|
|
jwt.RegisteredClaims
|
|
Alt string `json:"alt,omitempty"`
|
|
}
|
|
|
|
jwtToken := jwt.New(jwt.SigningMethodES256)
|
|
jwtToken.Claims = altClaims{
|
|
RegisteredClaims: jwt.RegisteredClaims{
|
|
Audience: []string{*validAltExtIdJwtSigner.Audience},
|
|
ExpiresAt: &jwt.NumericDate{Time: time.Now().Add(2 * time.Hour)},
|
|
ID: time.Now().String(),
|
|
IssuedAt: &jwt.NumericDate{Time: time.Now()},
|
|
Issuer: *validAltExtIdJwtSigner.Issuer,
|
|
NotBefore: &jwt.NumericDate{Time: time.Now()},
|
|
Subject: uuid.NewString(),
|
|
},
|
|
Alt: externalId,
|
|
}
|
|
|
|
jwtToken.Header["kid"] = *validAltExtIdJwtSigner.Kid
|
|
|
|
jwtStrSigned, err := jwtToken.SignedString(validAltExtIdJwtSignerPrivateKey)
|
|
ctx.Req.NoError(err)
|
|
ctx.Req.NotEmpty(jwtStrSigned)
|
|
|
|
result := &rest_model.CurrentAPISessionDetailEnvelope{}
|
|
|
|
resp, err := ctx.newAnonymousClientApiRequest().SetResult(result).SetHeader("Authorization", "Bearer "+jwtStrSigned).Post("/authenticate?method=ext-jwt")
|
|
ctx.Req.NoError(err)
|
|
ctx.Req.Equal(http.StatusOK, resp.StatusCode(), string(resp.Body()))
|
|
|
|
ctx.Req.NotNil(result)
|
|
ctx.Req.NotNil(result.Data)
|
|
ctx.Req.NotNil(result.Data.Token)
|
|
})
|
|
}
|