integration: test with both authentication flows (#4817)

Add an environment variable to allow forcing either the stateful or the
stateless authenticate flow.

Split the existing integration test clusters "single" and "multi" into
four new clusters: "single-stateful", "single-stateless",
"multi-stateful", and "multi-stateless", so that the integration tests
will run for both the stateful and the stateless authenticate flows.

(The "kubernetes" cluster is not currently being run, so I've left it
alone for now.)
This commit is contained in:
Kenneth Jenkins 2023-12-07 16:06:41 -08:00 committed by GitHub
parent a0d5f49c17
commit 08c186a72e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 1819 additions and 19 deletions

View file

@ -43,7 +43,7 @@ jobs:
- name: start cluster
run: |
export POMERIUM_TAG=dev
cd ./integration/clusters/single
cd ./integration/clusters/single-stateful
docker-compose up -d
- name: integration tests

View file

@ -16,6 +16,7 @@ jobs:
node-version: [16.x]
platform: [ubuntu-latest]
deployment: [multi, single]
authenticate-flow: [stateful, stateless]
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
@ -41,12 +42,12 @@ jobs:
- name: start cluster
run: |
export POMERIUM_TAG=dev
cd ./integration/clusters/${{matrix.deployment}}
cd ./integration/clusters/${{matrix.deployment}}-${{matrix.authenticate-flow}}
docker-compose up -d
- name: integration tests
run: |
(cd ./integration/clusters/${{matrix.deployment}} && docker-compose logs -f &)
(cd ./integration/clusters/${{matrix.deployment}}-${{matrix.authenticate-flow}} && docker-compose logs -f &)
go test -v ./integration/...
build:

View file

@ -830,6 +830,15 @@ func (o *Options) GetInternalAuthenticateURL() (*url.URL, error) {
// UseStatelessAuthenticateFlow returns true if the stateless authentication
// flow should be used (i.e. for hosted authenticate).
func (o *Options) UseStatelessAuthenticateFlow() bool {
if flow := os.Getenv("DEBUG_FORCE_AUTHENTICATE_FLOW"); flow != "" {
if flow == "stateless" {
return true
} else if flow == "stateful" {
return false
}
log.Warn(context.Background()).
Msgf("ignoring unknown DEBUG_FORCE_AUTHENTICATE_FLOW setting %q", flow)
}
u, err := o.GetInternalAuthenticateURL()
if err != nil {
return false

View file

@ -869,6 +869,16 @@ func TestOptions_UseStatelessAuthenticateFlow(t *testing.T) {
options := &Options{AuthenticateURLString: "https://authenticate.example.com"}
assert.False(t, options.UseStatelessAuthenticateFlow())
})
t.Run("force enabled", func(t *testing.T) {
options := &Options{AuthenticateURLString: "https://authenticate.example.com"}
t.Setenv("DEBUG_FORCE_AUTHENTICATE_FLOW", "stateless")
assert.True(t, options.UseStatelessAuthenticateFlow())
})
t.Run("force disabled", func(t *testing.T) {
options := &Options{}
t.Setenv("DEBUG_FORCE_AUTHENTICATE_FLOW", "stateful")
assert.False(t, options.UseStatelessAuthenticateFlow())
})
}
func TestOptions_GetOauthOptions(t *testing.T) {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -23,7 +23,7 @@ import (
"golang.org/x/net/publicsuffix"
)
var IDP, ClusterType string
var IDP, ClusterType, AuthenticateFlow string
func TestMain(m *testing.M) {
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})
@ -44,7 +44,7 @@ func TestMain(m *testing.M) {
return
}
setIDPAndClusterType(ctx)
setClusterInfo(ctx)
status := m.Run()
os.Exit(status)
@ -169,9 +169,10 @@ func waitForHealthy(ctx context.Context) error {
}
}
func setIDPAndClusterType(ctx context.Context) {
func setClusterInfo(ctx context.Context) {
IDP = "oidc"
ClusterType = "single"
AuthenticateFlow = "stateful"
cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
if err != nil {
@ -185,14 +186,19 @@ func setIDPAndClusterType(ctx context.Context) {
}
for _, container := range containers {
for _, name := range container.Names {
parts := regexp.MustCompile(`^/(\w+?)[-_]pomerium.*$`).FindStringSubmatch(name)
if len(parts) == 2 {
parts := regexp.MustCompile(`^/(\w+?)-(\w+?)[-_]pomerium.*$`).FindStringSubmatch(name)
if len(parts) == 3 {
ClusterType = parts[1]
AuthenticateFlow = parts[2]
}
}
}
log.Info().Str("idp", IDP).Str("cluster-type", ClusterType).Send()
log.Info().
Str("idp", IDP).
Str("cluster-type", ClusterType).
Str("authenticate-flow", AuthenticateFlow).
Send()
}
func mustParseURL(str string) *url.URL {

View file

@ -74,7 +74,7 @@ local KubernetesService(name) =
};
local Environment(mode, idp, dns_suffix) =
local Environment(mode, idp, authentication_flow, dns_suffix) =
{
AUTHENTICATE_SERVICE_URL: 'https://authenticate.localhost.pomerium.io',
CERTIFICATE: std.base64(importstr '../files/trusted.pem'),
@ -104,6 +104,8 @@ local Environment(mode, idp, dns_suffix) =
DATABROKER_SERVICE_URL: 'https://pomerium-databroker:5443',
GRPC_ADDRESS: ':5443',
GRPC_INSECURE: 'false',
} else {} + if authentication_flow == 'stateless' then {
DEBUG_FORCE_AUTHENTICATE_FLOW: 'stateless',
} else {};
local ComposeService(name, definition, additionalAliases=[]) =
@ -128,10 +130,10 @@ local ComposeService(name, definition, additionalAliases=[]) =
},
}, additionalAliases);
function(mode, idp, dns_suffix='') {
function(mode, idp, authentication_flow, dns_suffix='') {
local name = 'pomerium',
local image = 'pomerium/pomerium:${POMERIUM_TAG:-main}',
local environment = Environment(mode, idp, dns_suffix),
local environment = Environment(mode, idp, authentication_flow, dns_suffix),
compose: {
services: if mode == 'multi' then

View file

@ -0,0 +1 @@
(import '../../deployments/multi.libsonnet')('oidc', 'stateful')

View file

@ -0,0 +1 @@
(import '../../deployments/multi.libsonnet')('oidc', 'stateless')

View file

@ -1 +0,0 @@
(import '../../deployments/multi.libsonnet')('oidc')

View file

@ -0,0 +1 @@
(import '../../deployments/single.libsonnet')('oidc', 'stateful')

View file

@ -0,0 +1 @@
(import '../../deployments/single.libsonnet')('oidc', 'stateless')

View file

@ -1 +0,0 @@
(import '../../deployments/single.libsonnet')('oidc')

View file

@ -6,7 +6,7 @@ function(idp) utils.Merge([
(import '../backends/fortio.libsonnet')().kubernetes +
(import '../backends/httpdetails.libsonnet')().kubernetes +
(import '../backends/mock-idp.libsonnet')(idp).kubernetes +
(import '../backends/pomerium.libsonnet')('single', idp, '.default.svc.cluster.local').kubernetes +
(import '../backends/pomerium.libsonnet')('single', idp, 'stateful', '.default.svc.cluster.local').kubernetes +
(import '../backends/postgres.libsonnet')().kubernetes +
(import '../backends/verify.libsonnet')('single').kubernetes +
(import '../backends/websocket-echo.libsonnet')().kubernetes

View file

@ -1,10 +1,10 @@
local utils = import '../utils.libsonnet';
function(idp) utils.Merge([
function(idp, authentication_flow) utils.Merge([
(import '../backends/fortio.libsonnet')().compose,
(import '../backends/httpdetails.libsonnet')().compose,
(import '../backends/mock-idp.libsonnet')(idp).compose,
(import '../backends/pomerium.libsonnet')('multi', idp).compose,
(import '../backends/pomerium.libsonnet')('multi', idp, authentication_flow).compose,
(import '../backends/postgres.libsonnet')().compose,
(import '../backends/verify.libsonnet')('multi').compose,
(import '../backends/websocket-echo.libsonnet')().compose,

View file

@ -1,10 +1,10 @@
local utils = import '../utils.libsonnet';
function(idp) utils.Merge([
function(idp, authentication_flow) utils.Merge([
(import '../backends/fortio.libsonnet')().compose,
(import '../backends/httpdetails.libsonnet')().compose,
(import '../backends/mock-idp.libsonnet')(idp).compose,
(import '../backends/pomerium.libsonnet')('single', idp).compose,
(import '../backends/pomerium.libsonnet')('single', idp, authentication_flow).compose,
(import '../backends/postgres.libsonnet')().compose,
(import '../backends/verify.libsonnet')('single').compose,
(import '../backends/websocket-echo.libsonnet')().compose,