Skip to content

Commit 6c6933a

Browse files
author
Agent
committed
Add OIDC authentication to integration tests
Deploys Dex (OIDC provider) and GLAuth (LDAP) alongside CTS in the EaaS pipeline and exercises the full mod_auth_openidc → load_openidc_user → get_user_info → query_ldap_groups → has_role auth stack end-to-end. Pipeline changes (.tekton/integration-test-eaas.yaml): - New deploy-glauth task (parallel with deploy-dex, after provision-environment): creates a ConfigMap with a GLAuth config defining two users (builder/readonly) and one group (cts-builders), then deploys GLAuth on port 3893. - New deploy-dex task (parallel with deploy-glauth): creates a ConfigMap with a Dex config using the password connector and a static OAuth2 client (cts-integration), then deploys Dex on port 5556. - Updated deploy-cts: runAfter now includes deploy-glauth and deploy-dex. The cts-config ConfigMap sets AUTH_BACKEND=openidc, AUTH_OPENIDC_USERINFO_URI, AUTH_LDAP_SERVER, AUTH_LDAP_GROUPS, ADMINS, and ALLOWED_BUILDERS. The httpd.conf uses AuthType openid-connect with OIDCProviderMetadataURL pointing at Dex's discovery document (which accepts http:// URLs) and OIDCSSLValidateServer Off to allow the HTTP-only Dex endpoint. OIDCOAuth* introspection directives are not used because mod_auth_openidc validates Bearer tokens via the userinfo endpoint when AuthType is openid-connect, avoiding the https-only restriction that OIDCOAuthIntrospectionEndpoint and OIDCOAuthVerifyJwksUri both enforce at parse time. The <RequireAny> block allows unauthenticated GET requests while requiring a valid Bearer token for writes. - Updated run-tests: installs requests alongside pytest and passes AUTH_BACKEND=openidc so the auth tests are not skipped. Test changes (tests/test_integration_api.py): - AuthHTTPClient: HTTPClient subclass that injects Authorization: Bearer on every request. - _get_oidc_token(): obtains a real access token from Dex via the ROPC grant. - write_http_client fixture: returns an AuthHTTPClient (builder token) under OIDC or a plain HTTPClient in noauth mode; used by the five pre-existing workflow tests so they continue to pass in both modes. - Four new test functions (skipped when AUTH_BACKEND != openidc): test_auth_unauthenticated_write_returns_401, test_auth_builder_can_post_compose, test_auth_unauthorized_user_returns_403, test_auth_get_endpoints_accessible_without_token. Generated-By: OpenCode (google-vertex-anthropic/claude-sonnet-4-6@default)
1 parent 6552ee2 commit 6c6933a

2 files changed

Lines changed: 535 additions & 32 deletions

File tree

.tekton/integration-test-eaas.yaml

Lines changed: 319 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,293 @@ spec:
7777
- name: ownerUid
7878
value: $(context.pipelineRun.uid)
7979

80+
- name: deploy-glauth
81+
runAfter:
82+
- provision-environment
83+
taskSpec:
84+
params:
85+
- name: kubeconfig-secret
86+
type: string
87+
steps:
88+
- name: create-glauth
89+
image: quay.io/konflux-ci/appstudio-utils:latest
90+
script: |
91+
#!/usr/bin/env bash
92+
set -euo pipefail
93+
94+
KUBECONFIG=/tmp/kubeconfig
95+
kubectl get secret $(params.kubeconfig-secret) -o jsonpath='{.data.kubeconfig}' | base64 -d > $KUBECONFIG
96+
export KUBECONFIG
97+
98+
echo "=========================================="
99+
echo "Deploying GLAuth LDAP Server"
100+
echo "=========================================="
101+
102+
kubectl apply -f - <<'EOFYAML'
103+
apiVersion: v1
104+
kind: ConfigMap
105+
metadata:
106+
name: glauth-config
107+
data:
108+
glauth.cfg: |
109+
[ldap]
110+
enabled = true
111+
listen = "0.0.0.0:3893"
112+
113+
[ldaps]
114+
enabled = false
115+
116+
[backend]
117+
datastore = "config"
118+
baseDN = "dc=example,dc=com"
119+
120+
[[users]]
121+
name = "builder"
122+
unixid = 5001
123+
primarygroup = 5501
124+
passsha256 = "6478579e37aff45f013e14eeb30b3cc56c72ccdc310123bcdf53e0333e3f416a"
125+
[[users.capabilities]]
126+
action = "search"
127+
object = "*"
128+
129+
[[users]]
130+
name = "readonly"
131+
unixid = 5002
132+
primarygroup = 5502
133+
passsha256 = "6478579e37aff45f013e14eeb30b3cc56c72ccdc310123bcdf53e0333e3f416a"
134+
[[users.capabilities]]
135+
action = "search"
136+
object = "*"
137+
138+
[[groups]]
139+
name = "cts-builders"
140+
unixid = 5501
141+
includegroups = []
142+
143+
[[groups]]
144+
name = "readonly-users"
145+
unixid = 5502
146+
includegroups = []
147+
EOFYAML
148+
149+
kubectl apply -f - <<'EOFYAML'
150+
apiVersion: apps/v1
151+
kind: Deployment
152+
metadata:
153+
name: glauth
154+
labels:
155+
app: glauth
156+
spec:
157+
replicas: 1
158+
selector:
159+
matchLabels:
160+
app: glauth
161+
template:
162+
metadata:
163+
labels:
164+
app: glauth
165+
spec:
166+
containers:
167+
- name: glauth
168+
image: docker.io/glauth/glauth:v2.3.2
169+
command: ["/app/glauth"]
170+
args: ["-c", "/etc/glauth/glauth.cfg"]
171+
ports:
172+
- containerPort: 3893
173+
name: ldap
174+
readinessProbe:
175+
tcpSocket:
176+
port: 3893
177+
initialDelaySeconds: 3
178+
periodSeconds: 5
179+
failureThreshold: 12
180+
resources:
181+
requests:
182+
memory: "64Mi"
183+
cpu: "50m"
184+
limits:
185+
memory: "128Mi"
186+
cpu: "200m"
187+
volumeMounts:
188+
- name: glauth-config
189+
mountPath: /etc/glauth
190+
readOnly: true
191+
volumes:
192+
- name: glauth-config
193+
configMap:
194+
name: glauth-config
195+
---
196+
apiVersion: v1
197+
kind: Service
198+
metadata:
199+
name: glauth
200+
labels:
201+
app: glauth
202+
spec:
203+
ports:
204+
- port: 3893
205+
targetPort: 3893
206+
name: ldap
207+
selector:
208+
app: glauth
209+
EOFYAML
210+
211+
echo "Waiting for GLAuth to be ready..."
212+
if ! kubectl wait --for=condition=available --timeout=120s deployment/glauth; then
213+
echo "GLAuth deployment failed! Debug info:"
214+
kubectl describe deployment glauth
215+
kubectl describe pod -l app=glauth
216+
kubectl logs -l app=glauth --tail=50 || echo "No logs available"
217+
exit 1
218+
fi
219+
echo "✓ GLAuth is ready"
220+
params:
221+
- name: kubeconfig-secret
222+
value: $(tasks.provision-environment.results.secretRef)
223+
224+
- name: deploy-dex
225+
runAfter:
226+
- provision-environment
227+
taskSpec:
228+
params:
229+
- name: kubeconfig-secret
230+
type: string
231+
steps:
232+
- name: create-dex
233+
image: quay.io/konflux-ci/appstudio-utils:latest
234+
script: |
235+
#!/usr/bin/env bash
236+
set -euo pipefail
237+
238+
KUBECONFIG=/tmp/kubeconfig
239+
kubectl get secret $(params.kubeconfig-secret) -o jsonpath='{.data.kubeconfig}' | base64 -d > $KUBECONFIG
240+
export KUBECONFIG
241+
242+
echo "=========================================="
243+
echo "Deploying Dex OIDC Provider"
244+
echo "=========================================="
245+
246+
kubectl apply -f - <<'EOFYAML'
247+
apiVersion: v1
248+
kind: ConfigMap
249+
metadata:
250+
name: dex-config
251+
data:
252+
config.yaml: |
253+
issuer: http://dex:5556
254+
255+
storage:
256+
type: memory
257+
258+
web:
259+
http: 0.0.0.0:5556
260+
261+
logger:
262+
level: debug
263+
264+
enablePasswordDB: true
265+
266+
staticPasswords:
267+
- email: builder@example.com
268+
hash: "$2y$10$6cZyh42YBb2Q5.fE9nj3mu3UHs21NUmVP2fpU63EM3/ecJuHVscEC"
269+
username: builder
270+
userID: "builder-id"
271+
- email: readonly@example.com
272+
hash: "$2y$10$6cZyh42YBb2Q5.fE9nj3mu3UHs21NUmVP2fpU63EM3/ecJuHVscEC"
273+
username: readonly
274+
userID: "readonly-id"
275+
276+
staticClients:
277+
- id: cts-integration
278+
secret: cts-integration-secret
279+
name: CTS Integration
280+
redirectURIs:
281+
- http://cts:8080/redirect_uri
282+
grantTypes:
283+
- password
284+
- authorization_code
285+
286+
oauth2:
287+
skipApprovalScreen: true
288+
passwordConnector: local
289+
EOFYAML
290+
291+
kubectl apply -f - <<'EOFYAML'
292+
apiVersion: apps/v1
293+
kind: Deployment
294+
metadata:
295+
name: dex
296+
labels:
297+
app: dex
298+
spec:
299+
replicas: 1
300+
selector:
301+
matchLabels:
302+
app: dex
303+
template:
304+
metadata:
305+
labels:
306+
app: dex
307+
spec:
308+
containers:
309+
- name: dex
310+
image: ghcr.io/dexidp/dex:v2.41.1
311+
args: ["dex", "serve", "/etc/dex/config.yaml"]
312+
ports:
313+
- containerPort: 5556
314+
name: http
315+
resources:
316+
requests:
317+
memory: "64Mi"
318+
cpu: "50m"
319+
limits:
320+
memory: "256Mi"
321+
cpu: "500m"
322+
readinessProbe:
323+
httpGet:
324+
path: /.well-known/openid-configuration
325+
port: 5556
326+
initialDelaySeconds: 5
327+
periodSeconds: 5
328+
timeoutSeconds: 5
329+
failureThreshold: 12
330+
volumeMounts:
331+
- name: dex-config
332+
mountPath: /etc/dex
333+
readOnly: true
334+
volumes:
335+
- name: dex-config
336+
configMap:
337+
name: dex-config
338+
---
339+
apiVersion: v1
340+
kind: Service
341+
metadata:
342+
name: dex
343+
labels:
344+
app: dex
345+
spec:
346+
ports:
347+
- port: 5556
348+
targetPort: 5556
349+
name: http
350+
selector:
351+
app: dex
352+
EOFYAML
353+
354+
echo "Waiting for Dex to be ready..."
355+
if ! kubectl wait --for=condition=available --timeout=180s deployment/dex; then
356+
echo "Dex deployment failed! Debug info:"
357+
kubectl describe deployment dex
358+
kubectl describe pod -l app=dex
359+
kubectl logs -l app=dex --tail=50 || echo "No logs available"
360+
exit 1
361+
fi
362+
echo "✓ Dex is ready"
363+
params:
364+
- name: kubeconfig-secret
365+
value: $(tasks.provision-environment.results.secretRef)
366+
80367
- name: deploy-database
81368
runAfter:
82369
- provision-environment
@@ -215,6 +502,8 @@ spec:
215502
- name: deploy-cts
216503
runAfter:
217504
- deploy-database
505+
- deploy-glauth
506+
- deploy-dex
218507
taskSpec:
219508
params:
220509
- name: kubeconfig-secret
@@ -241,7 +530,7 @@ spec:
241530
echo "Image: $IMAGE"
242531
243532
# Create ConfigMap with production configuration and httpd config for CTS
244-
kubectl apply -f - <<EOF
533+
kubectl apply -f - <<'EOFYAML'
245534
apiVersion: v1
246535
kind: ConfigMap
247536
metadata:
@@ -251,8 +540,16 @@ spec:
251540
from conf.config import BaseConfiguration
252541
253542
class ProdConfiguration(BaseConfiguration):
254-
AUTH_BACKEND = "noauth"
543+
AUTH_BACKEND = "openidc"
255544
SQLALCHEMY_DATABASE_URI = "postgresql://cts:cts-test@cts-db:5432/cts"
545+
AUTH_OPENIDC_USERINFO_URI = "http://dex:5556/userinfo"
546+
AUTH_OPENIDC_REQUIRED_SCOPES = ["openid"]
547+
AUTH_LDAP_SERVER = "ldap://glauth:3893"
548+
AUTH_LDAP_GROUPS = [
549+
("ou=groups,dc=example,dc=com", "(&(objectClass=posixGroup)(memberUid={0}))"),
550+
]
551+
ADMINS = {"groups": [], "users": ["builder"]}
552+
ALLOWED_BUILDERS = {"groups": [], "users": ["builder"]}
256553
httpd.conf: |
257554
ServerRoot "/etc/httpd"
258555
PidFile /tmp/httpd.pid
@@ -268,15 +565,31 @@ spec:
268565
269566
Include conf.modules.d/*.conf
270567
568+
OIDCProviderMetadataURL http://dex:5556/.well-known/openid-configuration
569+
OIDCSSLValidateServer Off
570+
OIDCClientID cts-integration
571+
OIDCClientSecret cts-integration-secret
572+
OIDCRedirectURI http://cts:8080/redirect_uri
573+
OIDCCryptoPassphrase changeme
574+
OIDCRemoteUserClaim preferred_username
575+
271576
WSGISocketPrefix /tmp/wsgi
272577
WSGIDaemonProcess cts threads=5 home=/usr/share/cts
273578
WSGIScriptAlias / /usr/share/cts/cts.wsgi
274579
275580
<Directory /usr/share/cts>
276581
WSGIProcessGroup cts
277582
WSGIApplicationGroup %{GLOBAL}
583+
AuthType openid-connect
584+
<RequireAny>
585+
<RequireAll>
586+
Require expr %{REQUEST_URI} !~ /userinfo/
587+
Require method GET
588+
</RequireAll>
589+
Require valid-user
590+
</RequireAny>
278591
</Directory>
279-
EOF
592+
EOFYAML
280593
281594
# Deploy CTS
282595
kubectl apply -f - <<EOF
@@ -478,9 +791,9 @@ spec:
478791
set -ex
479792
export HOME=/tmp
480793
481-
echo 'Installing pytest...'
794+
echo 'Installing pytest and requests...'
482795
python3 -m ensurepip
483-
python3 -m pip install --user pytest
796+
python3 -m pip install --user pytest requests
484797
485798
echo ''
486799
echo 'Cloning repository...'
@@ -492,7 +805,7 @@ spec:
492805
493806
echo ''
494807
echo 'Running pytest...'
495-
CTS_URL=http://cts:8080 python3 -m pytest tests/test_integration_api.py -v -s -o addopts=
808+
CTS_URL=http://cts:8080 AUTH_BACKEND=openidc python3 -m pytest tests/test_integration_api.py -v -s -o addopts=
496809
"
497810
TEST_RESULT=$?
498811
set -e

0 commit comments

Comments
 (0)