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 - name: start cluster
run: | run: |
export POMERIUM_TAG=dev export POMERIUM_TAG=dev
cd ./integration/clusters/single cd ./integration/clusters/single-stateful
docker-compose up -d docker-compose up -d
- name: integration tests - name: integration tests

View file

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

View file

@ -830,6 +830,15 @@ func (o *Options) GetInternalAuthenticateURL() (*url.URL, error) {
// UseStatelessAuthenticateFlow returns true if the stateless authentication // UseStatelessAuthenticateFlow returns true if the stateless authentication
// flow should be used (i.e. for hosted authenticate). // flow should be used (i.e. for hosted authenticate).
func (o *Options) UseStatelessAuthenticateFlow() bool { 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() u, err := o.GetInternalAuthenticateURL()
if err != nil { if err != nil {
return false return false

View file

@ -869,6 +869,16 @@ func TestOptions_UseStatelessAuthenticateFlow(t *testing.T) {
options := &Options{AuthenticateURLString: "https://authenticate.example.com"} options := &Options{AuthenticateURLString: "https://authenticate.example.com"}
assert.False(t, options.UseStatelessAuthenticateFlow()) 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) { 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" "golang.org/x/net/publicsuffix"
) )
var IDP, ClusterType string var IDP, ClusterType, AuthenticateFlow string
func TestMain(m *testing.M) { func TestMain(m *testing.M) {
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr}) log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})
@ -44,7 +44,7 @@ func TestMain(m *testing.M) {
return return
} }
setIDPAndClusterType(ctx) setClusterInfo(ctx)
status := m.Run() status := m.Run()
os.Exit(status) 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" IDP = "oidc"
ClusterType = "single" ClusterType = "single"
AuthenticateFlow = "stateful"
cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation()) cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
if err != nil { if err != nil {
@ -185,14 +186,19 @@ func setIDPAndClusterType(ctx context.Context) {
} }
for _, container := range containers { for _, container := range containers {
for _, name := range container.Names { for _, name := range container.Names {
parts := regexp.MustCompile(`^/(\w+?)[-_]pomerium.*$`).FindStringSubmatch(name) parts := regexp.MustCompile(`^/(\w+?)-(\w+?)[-_]pomerium.*$`).FindStringSubmatch(name)
if len(parts) == 2 { if len(parts) == 3 {
ClusterType = parts[1] 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 { 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', AUTHENTICATE_SERVICE_URL: 'https://authenticate.localhost.pomerium.io',
CERTIFICATE: std.base64(importstr '../files/trusted.pem'), CERTIFICATE: std.base64(importstr '../files/trusted.pem'),
@ -104,6 +104,8 @@ local Environment(mode, idp, dns_suffix) =
DATABROKER_SERVICE_URL: 'https://pomerium-databroker:5443', DATABROKER_SERVICE_URL: 'https://pomerium-databroker:5443',
GRPC_ADDRESS: ':5443', GRPC_ADDRESS: ':5443',
GRPC_INSECURE: 'false', GRPC_INSECURE: 'false',
} else {} + if authentication_flow == 'stateless' then {
DEBUG_FORCE_AUTHENTICATE_FLOW: 'stateless',
} else {}; } else {};
local ComposeService(name, definition, additionalAliases=[]) = local ComposeService(name, definition, additionalAliases=[]) =
@ -128,10 +130,10 @@ local ComposeService(name, definition, additionalAliases=[]) =
}, },
}, additionalAliases); }, additionalAliases);
function(mode, idp, dns_suffix='') { function(mode, idp, authentication_flow, dns_suffix='') {
local name = 'pomerium', local name = 'pomerium',
local image = 'pomerium/pomerium:${POMERIUM_TAG:-main}', local image = 'pomerium/pomerium:${POMERIUM_TAG:-main}',
local environment = Environment(mode, idp, dns_suffix), local environment = Environment(mode, idp, authentication_flow, dns_suffix),
compose: { compose: {
services: if mode == 'multi' then 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/fortio.libsonnet')().kubernetes +
(import '../backends/httpdetails.libsonnet')().kubernetes + (import '../backends/httpdetails.libsonnet')().kubernetes +
(import '../backends/mock-idp.libsonnet')(idp).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/postgres.libsonnet')().kubernetes +
(import '../backends/verify.libsonnet')('single').kubernetes + (import '../backends/verify.libsonnet')('single').kubernetes +
(import '../backends/websocket-echo.libsonnet')().kubernetes (import '../backends/websocket-echo.libsonnet')().kubernetes

View file

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

View file

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