Skip to content

Commit

Permalink
Fix loop bug causing bad user cache load
Browse files Browse the repository at this point in the history
* Added additional logging
* Added additional user to e2e tests
* Set non-zero sync period in e2e tests to confirm cache behavior
* Updated e2e keycloak script to overwrite, not just append, roles
        * This fixes tests failing when re-run from an existing state
  • Loading branch information
meln5674 committed Jul 16, 2024
1 parent 89d1162 commit 9848a94
Show file tree
Hide file tree
Showing 5 changed files with 214 additions and 85 deletions.
23 changes: 11 additions & 12 deletions e2e_suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ func TestNexusOidcProxy(t *testing.T) {
RunSpecs(t, "NexusOidcProxy Suite")
}

var b *biloba.Biloba
// var b *biloba.Biloba
var gk8s gingk8s.Gingk8s

var _ = BeforeSuite(func(ctx context.Context) {
Expand Down Expand Up @@ -226,24 +226,23 @@ var _ = BeforeSuite(func(ctx context.Context) {
}
}

bopts := []chromedp.ExecAllocatorOption{
chromedp.ProxyServer("http://localhost:8080"),
chromedp.Flag("ignore-certificate-errors", "1"),
}

if os.Getenv("IT_IN_CONTAINER") != "" {
bopts = append(bopts, chromedp.NoSandbox)
GinkgoWriter.Printf("!!! WARNING: Sandbox disabled due to containerized environment detected from IT_IN_CONTAINER. This is insecure if this not actually a container!\n")
}

biloba.SpinUpChrome(GinkgoT(), bopts...)
b = biloba.ConnectToChrome(GinkgoT())
b := biloba.ConnectToChrome(GinkgoT())

keycloakLogin(true)
keycloakLogin(b, true, "user-1", "user-1-password")
})

var (
devMode = false // TODO get this from a env var
bopts = []chromedp.ExecAllocatorOption{
chromedp.ProxyServer("http://localhost:8080"),
chromedp.Flag("ignore-certificate-errors", "1"),
}
)

var (
Expand Down Expand Up @@ -491,7 +490,7 @@ var (
"NEXUS_CLIENT_ID=nexus",
"NEXUS_CALLBACK_URL=https://nexus.nexus-oidc-proxy-it.cluster/oauth2/callback",
"CREATE_ROLES='nx-role1 nx-role2'",
"CREATE_USERS='user-1 user-1-password nx-role1'",
"CREATE_USERS='user-1 user-1-password default-roles-integration-test nx-role1;user-2 user-2-password default-roles-integration-test'",
))

oauth2ProxyConfig = gingk8s.KubernetesManifests{
Expand Down Expand Up @@ -590,7 +589,7 @@ func execKeycloakSetup(pod string, extraEnv ...string) func(g gingk8s.Gingk8s, c
}
}

func keycloakLogin(needCredentials bool) {
func keycloakLogin(b *biloba.Biloba, needCredentials bool, username, password string) {
GinkgoHelper()
By("Navigating to the oauth proxy sign-in")
nexusURL := fmt.Sprintf("https://%s/oauth2/sign_in", oauth2Proxy.Set["ingress.hostname"])
Expand All @@ -602,8 +601,8 @@ func keycloakLogin(needCredentials bool) {
b.Click(loginButton)
if needCredentials {
Eventually(b.Location, "5s").Should(HavePrefix(fmt.Sprintf("https://%s/", keycloak.Set["ingress.hostname"])))
b.SetValue("#username", "user-1")
b.SetValue("#password", "user-1-password")
b.SetValue("#username", username)
b.SetValue("#password", password)
b.Click("#kc-login")
}

Expand Down
179 changes: 141 additions & 38 deletions e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,49 @@ package main_test

import (
"context"
"net/http"
"strings"
"time"

"github.com/meln5674/gingk8s"

"github.com/onsi/biloba"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)

const (
accountButton = `a[data-name='user'] > span > span > span.x-btn-inner`
)

var _ = Describe("The Nexus OIDC Proxy", Ordered, func() {
It("Should start", func() {
b := biloba.ConnectToChrome(GinkgoT())
defer b.Close()
keycloakLogin(b, true, "user-1", "user-1-password")

b.Navigate("https://nexus.nexus-oidc-proxy-it.cluster")

welcomeImg := `img.nxrm-welcome__logo`
Eventually(welcomeImg, "30s").Should(b.Exist())
})

It("Should show the user as logged in", func() {
b.Navigate("https://nexus.nexus-oidc-proxy-it.cluster")
b := biloba.ConnectToChrome(GinkgoT())
defer b.Close()
keycloakLogin(b, true, "user-1", "user-1-password")

accountButton := `a[data-name='user'] > span > span > span.x-btn-inner`
b.Navigate("https://nexus.nexus-oidc-proxy-it.cluster")

Eventually(accountButton, "15s").Should(b.Exist())
Expect(accountButton).To(b.HaveInnerText("user-1"))
})

It("Should allow generating, and logging into the API with, a token", func() {
// This is allowed to flake because nexus's change password API sometimes decides
// you don't appreciate it enough
It("Should allow generating, and logging into the API with, a token", FlakeAttempts(10), func() {
b := biloba.ConnectToChrome(GinkgoT())
defer b.Close()
keycloakLogin(b, true, "user-1", "user-1-password")

b.Navigate("https://nexus.nexus-oidc-proxy-it.cluster/token")

generateButton := `input`
Expand All @@ -35,12 +53,12 @@ var _ = Describe("The Nexus OIDC Proxy", Ordered, func() {
b.Click(generateButton)

Eventually(`body`, "5s").Should(b.HaveInnerText(ContainSubstring("Your new token is: ")))
Expect(`body code`).To(b.Exist())

bodyLines := strings.Split(b.InnerText(`body`), "\n")
Expect(bodyLines).To(HaveLen(4))
tokenLine := bodyLines[2]
token := b.InnerText(`body > code`)

token := strings.TrimPrefix(tokenLine, "Your new token is: ")
GinkgoLogr.Info("Got token", "token", token)
time.Sleep(5 * time.Second)

b.Navigate("https://nexus-api.nexus-oidc-proxy-it.cluster")
welcomeImg := `img.nxrm-welcome__logo`
Expand All @@ -66,42 +84,127 @@ var _ = Describe("The Nexus OIDC Proxy", Ordered, func() {
b.Click(submitButtonXPath)
Eventually(submitButtonXPath).ShouldNot(b.Exist())

accountButton := `a[data-name='user'] > span > span > span.x-btn-inner`

Eventually(accountButton, "15s").Should(b.Exist())
Eventually(accountButton, "15s").Should(b.HaveInnerText("user-1"))
Expect(accountButton).To(b.HaveInnerText("user-1"))
})

browseButton := `tr[data-qtip='Browse assets and components']`
testRepo1XPath := `//div[text() = 'test-repo-1']`
testRepo2XPath := `//div[text() = 'test-repo-2']`
It("should show repos granted to the user by their roles", func(ctx context.Context) {
b.Navigate("https://nexus.nexus-oidc-proxy-it.cluster")

Eventually(browseButton, "15s").Should(b.Exist())
b.Click(browseButton)

Eventually(testRepo1XPath, "5s").Should(b.Exist())
Expect(testRepo2XPath).ToNot(b.Exist())
execKeycloakSetup(
"keycloak-0",
"KEYCLOAK_URL=https://keycloak.default.svc.cluster.local",
"KEYCLOAK_ADMIN_PASSWORD=adminPassword",
"NEXUS_REALM=integration-test",
"NEXUS_CLIENT_ID=nexus",
"NEXUS_CALLBACK_URL=https://nexus.nexus-oidc-proxy-it.cluster/oauth2/callback",
"CREATE_ROLES='nx-role1 nx-role2'",
"CREATE_USERS='user-1 user-1-password nx-role1 nx-role2'",
)(gk8s, ctx, &cluster)

b.NavigateWithStatus("https://nexus.nexus-oidc-proxy-it.cluster/oauth2/sign_out", http.StatusForbidden)

keycloakLogin(false)
func() {
b := biloba.ConnectToChrome(GinkgoT())
defer b.Close()

keycloakLogin(b, true, "user-1", "user-1-password")
b.Navigate("https://nexus.nexus-oidc-proxy-it.cluster")

Eventually(browseButton, "15s").Should(b.Exist())
b.Click(browseButton)

Eventually(testRepo1XPath, "5s").Should(b.Exist())
Expect(testRepo2XPath).ToNot(b.Exist())
execKeycloakSetup(
"keycloak-0",
"KEYCLOAK_URL=https://keycloak.default.svc.cluster.local",
"KEYCLOAK_ADMIN_PASSWORD=adminPassword",
"NEXUS_REALM=integration-test",
"NEXUS_CLIENT_ID=nexus",
"NEXUS_CALLBACK_URL=https://nexus.nexus-oidc-proxy-it.cluster/oauth2/callback",
"CREATE_ROLES='nx-role1 nx-role2'",
"CREATE_USERS='user-1 user-1-password default-roles-integration-test nx-role1 nx-role2'",
)(gk8s, ctx, &cluster)
}()

func() {
b := biloba.ConnectToChrome(GinkgoT())
defer b.Close()

keycloakLogin(b, true, "user-1", "user-1-password")

b.Navigate("https://nexus.nexus-oidc-proxy-it.cluster")
Eventually(accountButton, "15s").Should(b.Exist())
Expect(accountButton).To(b.HaveInnerText("user-1"))
Expect(browseButton).To(b.Exist())
// syncInterval is 5s, so after 6s, it should have changed
time.Sleep(6 * time.Second)
b.Navigate("https://nexus.nexus-oidc-proxy-it.cluster")
Eventually(browseButton, "15s").Should(b.Exist())
b.Click(browseButton)
Eventually(testRepo1XPath, "5s").Should(b.Exist())
Expect(testRepo2XPath).To(b.Exist())
}()

func() {
b := biloba.ConnectToChrome(GinkgoT())
defer b.Close()
keycloakLogin(b, true, "user-2", "user-2-password")

b.Navigate("https://nexus.nexus-oidc-proxy-it.cluster")
Eventually(accountButton, "15s").Should(b.Exist())
Expect(accountButton).To(b.HaveInnerText("user-2"))
Expect(browseButton).ToNot(b.Exist())
}()
})
It("should show repos granted to the user by their roles after a restart", func(ctx context.Context) {
Expect(gk8s.KubectlRollout(ctx, &cluster, gingk8s.ResourceReference{Kind: "deployment", Name: "nexus-oidc-proxy"}).Run()).To(Succeed())
time.Sleep(5 * time.Second)

func() {
b := biloba.ConnectToChrome(GinkgoT())
defer b.Close()

keycloakLogin(b, true, "user-1", "user-1-password")

b.Navigate("https://nexus.nexus-oidc-proxy-it.cluster")

Eventually(browseButton, "15s").Should(b.Exist())
b.Click(browseButton)

Eventually(testRepo1XPath, "5s").Should(b.Exist())
Expect(testRepo2XPath).To(b.Exist())
execKeycloakSetup(
"keycloak-0",
"KEYCLOAK_URL=https://keycloak.default.svc.cluster.local",
"KEYCLOAK_ADMIN_PASSWORD=adminPassword",
"NEXUS_REALM=integration-test",
"NEXUS_CLIENT_ID=nexus",
"NEXUS_CALLBACK_URL=https://nexus.nexus-oidc-proxy-it.cluster/oauth2/callback",
"CREATE_ROLES='nx-role1 nx-role2'",
"CREATE_USERS='user-1 user-1-password default-roles-integration-test nx-role1'",
)(gk8s, ctx, &cluster)
}()

func() {
b := biloba.ConnectToChrome(GinkgoT())
defer b.Close()
keycloakLogin(b, true, "user-1", "user-1-password")

b.Navigate("https://nexus.nexus-oidc-proxy-it.cluster")
Eventually(accountButton, "15s").Should(b.Exist())
Expect(accountButton).To(b.HaveInnerText("user-1"))
Expect(browseButton).To(b.Exist())
// syncInterval is 5s, so after 6s, it should have changed
time.Sleep(6 * time.Second)
b.Navigate("https://nexus.nexus-oidc-proxy-it.cluster")
Eventually(browseButton, "15s").Should(b.Exist())
b.Click(browseButton)
Eventually(testRepo1XPath, "5s").Should(b.Exist())
Expect(testRepo2XPath).ToNot(b.Exist())
}()

func() {
b := biloba.ConnectToChrome(GinkgoT())
defer b.Close()

keycloakLogin(b, true, "user-2", "user-2-password")

b.Navigate("https://nexus.nexus-oidc-proxy-it.cluster")
Eventually(accountButton, "15s").Should(b.Exist())
Expect(accountButton).To(b.HaveInnerText("user-2"))
Expect(browseButton).ToNot(b.Exist())
}()

b.Navigate("https://nexus.nexus-oidc-proxy-it.cluster")
Eventually(browseButton, "15s").Should(b.Exist())
b.Click(browseButton)
Eventually(testRepo1XPath, "5s").Should(b.Exist())
Expect(testRepo2XPath).To(b.Exist())
})

})
19 changes: 17 additions & 2 deletions integration-test/configure-keycloak.sh
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,21 @@ function ensure-user {
)
fi

kcadm.sh get-roles \
-r "${realm}" \
--uusername "${username}" \
-F name \
| tee /dev/stderr \
| grep '"name"' \
| sed -E 's/.*"name" : "([^"]+)".*/\1/' \
| tee /dev/stderr \
| while read -r role ; do
kcadm.sh remove-roles \
-r "${realm}" \
--rolename "${role}" \
--uusername "${username}"
done

for role in "$@"; do
kcadm.sh add-roles \
-r "${realm}" \
Expand Down Expand Up @@ -161,6 +176,6 @@ for role in ${CREATE_ROLES:-}; do
ensure-role "${NEXUS_REALM}" "${role}"
done

while read -r username password roles ; do
tr ';' '\n' <<< "${CREATE_USERS:-}" | while read -r username password roles ; do
ensure-user "${NEXUS_REALM}" "${username}" "${password}" ${roles}
done <<< "${CREATE_USERS:-}"
done
4 changes: 2 additions & 2 deletions integration-test/nexus-oidc-proxy.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ nexus:
oidc:
wellKnownURL: https://keycloak.nexus-oidc-proxy-it/realms/integration-test/.well-known/openid-configuration
accessTokenHeader: X-Forwarded-Access-Token
syncInterval: 0s
syncInterval: 5s
userTemplate: |-
userId: '{{ .Token.Claims.preferred_username }}'
firstName: '{{ .Token.Claims.given_name }}'
Expand All @@ -29,6 +29,6 @@ oidc:
{{- end }}
{{- end }}
{{- end }}
{{- $roles | default (list "nx-anonymous") | toJson }}
{{- $roles | default (list "nx-empty") | toJson }}
defaultRoles:
- nx-empty
Loading

0 comments on commit 9848a94

Please sign in to comment.