package integration_test import ( "context" "os" "testing" "time" "github.com/int128/kubelogin/integration_test/httpdriver" "github.com/int128/kubelogin/integration_test/keypair" "github.com/int128/kubelogin/integration_test/kubeconfig" "github.com/int128/kubelogin/integration_test/oidcserver" "github.com/int128/kubelogin/integration_test/oidcserver/testconfig" "github.com/int128/kubelogin/pkg/di" "github.com/int128/kubelogin/pkg/infrastructure/browser" "github.com/int128/kubelogin/pkg/testing/clock" "github.com/int128/kubelogin/pkg/testing/logger" ) // Run the integration tests of the Login use-case. // // 1. Start the auth server. // 2. Run the Cmd. // 3. Open a request for the local server. // 4. Verify the kubeconfig. func TestStandalone(t *testing.T) { timeout := 3 * time.Second now := time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC) for name, tc := range map[string]struct { keyPair keypair.KeyPair args []string }{ "NoTLS": {}, "TLS": { keyPair: keypair.Server, }, } { httpDriverOption := httpdriver.Config{ TLSConfig: tc.keyPair.TLSConfig, BodyContains: "Authenticated", } t.Run(name, func(t *testing.T) { t.Run("AuthCode", func(t *testing.T) { t.Parallel() ctx, cancel := context.WithTimeout(context.TODO(), timeout) defer cancel() sv := oidcserver.New(t, tc.keyPair, testconfig.Config{ Want: testconfig.Want{ Scope: "openid", RedirectURIPrefix: "http://localhost:", }, Response: testconfig.Response{ IDTokenExpiry: now.Add(time.Hour), }, }) kubeConfigFilename := kubeconfig.Create(t, &kubeconfig.Values{ Issuer: sv.IssuerURL(), IDPCertificateAuthority: tc.keyPair.CACertPath, }) runStandalone(t, ctx, standaloneConfig{ issuerURL: sv.IssuerURL(), kubeConfigFilename: kubeConfigFilename, httpDriver: httpdriver.New(ctx, t, httpDriverOption), now: now, }) kubeconfig.Verify(t, kubeConfigFilename, kubeconfig.AuthProviderConfig{ IDToken: sv.LastTokenResponse().IDToken, RefreshToken: sv.LastTokenResponse().RefreshToken, }) }) t.Run("ROPC", func(t *testing.T) { t.Parallel() ctx, cancel := context.WithTimeout(context.TODO(), timeout) defer cancel() sv := oidcserver.New(t, tc.keyPair, testconfig.Config{ Want: testconfig.Want{ Scope: "openid", RedirectURIPrefix: "http://localhost:", Username: "USER1", Password: "PASS1", }, Response: testconfig.Response{ IDTokenExpiry: now.Add(time.Hour), }, }) kubeConfigFilename := kubeconfig.Create(t, &kubeconfig.Values{ Issuer: sv.IssuerURL(), IDPCertificateAuthority: tc.keyPair.CACertPath, }) runStandalone(t, ctx, standaloneConfig{ issuerURL: sv.IssuerURL(), kubeConfigFilename: kubeConfigFilename, httpDriver: httpdriver.Zero(t), now: now, args: []string{ "--username", "USER1", "--password", "PASS1", }, }) kubeconfig.Verify(t, kubeConfigFilename, kubeconfig.AuthProviderConfig{ IDToken: sv.LastTokenResponse().IDToken, RefreshToken: sv.LastTokenResponse().RefreshToken, }) }) t.Run("TokenLifecycle", func(t *testing.T) { t.Parallel() ctx, cancel := context.WithTimeout(context.TODO(), timeout) defer cancel() sv := oidcserver.New(t, tc.keyPair, testconfig.Config{}) kubeConfigFilename := kubeconfig.Create(t, &kubeconfig.Values{ Issuer: sv.IssuerURL(), IDPCertificateAuthority: tc.keyPair.CACertPath, }) t.Run("NoToken", func(t *testing.T) { sv.SetConfig(testconfig.Config{ Want: testconfig.Want{ Scope: "openid", RedirectURIPrefix: "http://localhost:", }, Response: testconfig.Response{ IDTokenExpiry: now.Add(time.Hour), RefreshToken: "REFRESH_TOKEN_1", }, }) runStandalone(t, ctx, standaloneConfig{ issuerURL: sv.IssuerURL(), kubeConfigFilename: kubeConfigFilename, httpDriver: httpdriver.New(ctx, t, httpDriverOption), now: now, }) kubeconfig.Verify(t, kubeConfigFilename, kubeconfig.AuthProviderConfig{ IDToken: sv.LastTokenResponse().IDToken, RefreshToken: "REFRESH_TOKEN_1", }) }) t.Run("Valid", func(t *testing.T) { sv.SetConfig(testconfig.Config{}) runStandalone(t, ctx, standaloneConfig{ issuerURL: sv.IssuerURL(), kubeConfigFilename: kubeConfigFilename, httpDriver: httpdriver.Zero(t), now: now, }) kubeconfig.Verify(t, kubeConfigFilename, kubeconfig.AuthProviderConfig{ IDToken: sv.LastTokenResponse().IDToken, RefreshToken: "REFRESH_TOKEN_1", }) }) t.Run("Refresh", func(t *testing.T) { sv.SetConfig(testconfig.Config{ Want: testconfig.Want{ Scope: "openid", RedirectURIPrefix: "http://localhost:", RefreshToken: "REFRESH_TOKEN_1", }, Response: testconfig.Response{ IDTokenExpiry: now.Add(3 * time.Hour), RefreshToken: "REFRESH_TOKEN_2", }, }) runStandalone(t, ctx, standaloneConfig{ issuerURL: sv.IssuerURL(), kubeConfigFilename: kubeConfigFilename, httpDriver: httpdriver.New(ctx, t, httpDriverOption), now: now.Add(2 * time.Hour), }) kubeconfig.Verify(t, kubeConfigFilename, kubeconfig.AuthProviderConfig{ IDToken: sv.LastTokenResponse().IDToken, RefreshToken: "REFRESH_TOKEN_2", }) }) t.Run("RefreshAgain", func(t *testing.T) { sv.SetConfig(testconfig.Config{ Want: testconfig.Want{ Scope: "openid", RedirectURIPrefix: "http://localhost:", RefreshToken: "REFRESH_TOKEN_2", }, Response: testconfig.Response{ IDTokenExpiry: now.Add(5 * time.Hour), }, }) runStandalone(t, ctx, standaloneConfig{ issuerURL: sv.IssuerURL(), kubeConfigFilename: kubeConfigFilename, httpDriver: httpdriver.New(ctx, t, httpDriverOption), now: now.Add(4 * time.Hour), }) kubeconfig.Verify(t, kubeConfigFilename, kubeconfig.AuthProviderConfig{ IDToken: sv.LastTokenResponse().IDToken, RefreshToken: "REFRESH_TOKEN_2", }) }) }) }) } t.Run("TLSData", func(t *testing.T) { t.Parallel() ctx, cancel := context.WithTimeout(context.TODO(), timeout) defer cancel() sv := oidcserver.New(t, keypair.Server, testconfig.Config{ Want: testconfig.Want{ Scope: "openid", RedirectURIPrefix: "http://localhost:", }, Response: testconfig.Response{ IDTokenExpiry: now.Add(time.Hour), }, }) kubeConfigFilename := kubeconfig.Create(t, &kubeconfig.Values{ Issuer: sv.IssuerURL(), IDPCertificateAuthorityData: keypair.Server.CACertBase64, }) runStandalone(t, ctx, standaloneConfig{ issuerURL: sv.IssuerURL(), kubeConfigFilename: kubeConfigFilename, httpDriver: httpdriver.New(ctx, t, httpdriver.Config{TLSConfig: keypair.Server.TLSConfig}), now: now, }) kubeconfig.Verify(t, kubeConfigFilename, kubeconfig.AuthProviderConfig{ IDToken: sv.LastTokenResponse().IDToken, RefreshToken: sv.LastTokenResponse().RefreshToken, }) }) t.Run("env_KUBECONFIG", func(t *testing.T) { ctx, cancel := context.WithTimeout(context.TODO(), timeout) defer cancel() sv := oidcserver.New(t, keypair.None, testconfig.Config{ Want: testconfig.Want{ Scope: "openid", RedirectURIPrefix: "http://localhost:", }, Response: testconfig.Response{ IDTokenExpiry: now.Add(time.Hour), }, }) kubeConfigFilename := kubeconfig.Create(t, &kubeconfig.Values{ Issuer: sv.IssuerURL(), }) t.Setenv("KUBECONFIG", kubeConfigFilename+string(os.PathListSeparator)+"kubeconfig/testdata/dummy.yaml") runStandalone(t, ctx, standaloneConfig{ issuerURL: sv.IssuerURL(), httpDriver: httpdriver.New(ctx, t, httpdriver.Config{}), now: now, }) kubeconfig.Verify(t, kubeConfigFilename, kubeconfig.AuthProviderConfig{ IDToken: sv.LastTokenResponse().IDToken, RefreshToken: sv.LastTokenResponse().RefreshToken, }) }) t.Run("ExtraScopes", func(t *testing.T) { t.Parallel() ctx, cancel := context.WithTimeout(context.TODO(), timeout) defer cancel() sv := oidcserver.New(t, keypair.None, testconfig.Config{ Want: testconfig.Want{ Scope: "profile groups openid", RedirectURIPrefix: "http://localhost:", }, Response: testconfig.Response{ IDTokenExpiry: now.Add(time.Hour), }, }) kubeConfigFilename := kubeconfig.Create(t, &kubeconfig.Values{ Issuer: sv.IssuerURL(), ExtraScopes: "profile,groups", }) runStandalone(t, ctx, standaloneConfig{ issuerURL: sv.IssuerURL(), kubeConfigFilename: kubeConfigFilename, httpDriver: httpdriver.New(ctx, t, httpdriver.Config{}), now: now, }) kubeconfig.Verify(t, kubeConfigFilename, kubeconfig.AuthProviderConfig{ IDToken: sv.LastTokenResponse().IDToken, RefreshToken: sv.LastTokenResponse().RefreshToken, }) }) } type standaloneConfig struct { issuerURL string kubeConfigFilename string httpDriver browser.Interface now time.Time args []string } func runStandalone(t *testing.T, ctx context.Context, cfg standaloneConfig) { cmd := di.NewCmdForHeadless(clock.Fake(cfg.now), os.Stdin, os.Stdout, logger.New(t), cfg.httpDriver) exitCode := cmd.Run(ctx, append([]string{ "kubelogin", "--kubeconfig", cfg.kubeConfigFilename, "--listen-address", "127.0.0.1:0", }, cfg.args...), "HEAD") if exitCode != 0 { t.Errorf("exit status wants 0 but %d", exitCode) } }