-
Notifications
You must be signed in to change notification settings - Fork 26
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: use Client Credentials for managing Keycloak Clients #1341
base: main
Are you sure you want to change the base?
Changes from all commits
845bd3a
cf5273f
5040780
41039a5
08730d3
0386929
42af49b
84152df
1129b25
50d9105
42160cd
31fcc7e
9a89c49
640bdb6
cddcac2
c86344c
b517c27
4c8fc23
b8fef4f
e591f3a
ecabe19
dc6e28e
e89c5cf
f70e5ce
d2aa553
e71f53a
6737743
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
# Copyright 2024 Defense Unicorns | ||
# SPDX-License-Identifier: AGPL-3.0-or-later OR LicenseRef-Defense-Unicorns-Commercial | ||
|
||
apiVersion: v1 | ||
kind: Secret | ||
metadata: | ||
name: {{ include "keycloak.fullname" . }}-client-secrets | ||
namespace: {{ .Release.Namespace }} | ||
labels: | ||
{{- include "keycloak.labels" . | nindent 4 }} | ||
type: Opaque | ||
data: | ||
ignored-value: {{ "ignored-value" | b64enc }} | ||
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -97,9 +97,9 @@ spec: | |
- name: redirect-welcome | ||
uri: | ||
exact: / | ||
- name: redirect-admin | ||
uri: | ||
prefix: /admin | ||
{{/* - name: redirect-admin*/}} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can I revert this piece? |
||
{{/* uri:*/}} | ||
{{/* prefix: /admin*/}} | ||
- name: redirect-master-realm | ||
uri: | ||
prefix: /realms/master | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -278,4 +278,6 @@ autoscaling: | |
value: 1 | ||
periodSeconds: 300 | ||
|
||
env: [] | ||
env: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Revert this |
||
- name: JAVA_OPTS_KC_HEAP | ||
value: "-XX:MaxRAMPercentage=70 -XX:MinRAMPercentage=70 -XX:InitialRAMPercentage=50 -XX:MaxRAM=1G" |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
/** | ||
* Copyright 2025 Defense Unicorns | ||
* SPDX-License-Identifier: AGPL-3.0-or-later OR LicenseRef-Defense-Unicorns-Commercial | ||
*/ | ||
|
||
import { K8s, kind } from "pepr"; | ||
import { v4 as uuidv4 } from "uuid"; | ||
import { Component, setupLogger } from "../../../logger"; | ||
|
||
const KEYCLOAK_CLIENT_SECRET_KEY = "uds-operator"; | ||
|
||
const log = setupLogger(Component.OPERATOR_CONFIG); | ||
|
||
export async function updateKeycloakClientsSecret(config: kind.Secret) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add docs |
||
config.data = config.data || {}; | ||
|
||
// This might be a bug but it seems Zarf adds managedFields, which is prohibited in Secrets. | ||
delete config.metadata?.managedFields; | ||
|
||
if (!config.data[KEYCLOAK_CLIENT_SECRET_KEY]) { | ||
log.info("Generating new Keycloak client secret"); | ||
config.data[KEYCLOAK_CLIENT_SECRET_KEY] = Buffer.from(uuidv4()).toString("base64"); | ||
await K8s(kind.Secret).Apply(config); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
/** | ||
* Copyright 2025 Defense Unicorns | ||
* SPDX-License-Identifier: AGPL-3.0-or-later OR LicenseRef-Defense-Unicorns-Commercial | ||
*/ | ||
|
||
import { | ||
ClientCredentialsKeycloakClient, | ||
DynamicClientRegistrationClient, | ||
DynamicKeycloakClient, | ||
} from "./keycloak-client"; | ||
|
||
describe("pickImplementation method", () => { | ||
it("should return DynamicClientRegistrationClient when ENV_KEYCLOAK_CLIENT_IMPLEMENTATION is dynamic_client_registration", async () => { | ||
process.env.PEPR_KEYCLOAK_CLIENT_STRATEGY = | ||
DynamicKeycloakClient.ENV_KEYCLOAK_CLIENT_IMPLEMENTATION_DYNAMIC; | ||
const client = new DynamicKeycloakClient("http://localhost"); | ||
const implementation = await client.pickImplementation(); | ||
expect(implementation).toBeInstanceOf(DynamicClientRegistrationClient); | ||
}); | ||
|
||
it("should return ClientCredentialsKeycloakClient when ENV_KEYCLOAK_CLIENT_IMPLEMENTATION is client_credentials", async () => { | ||
process.env.PEPR_KEYCLOAK_CLIENT_STRATEGY = | ||
DynamicKeycloakClient.ENV_KEYCLOAK_CLIENT_IMPLEMENTATION_CLIENT_CREDENTIALS; | ||
const client = new DynamicKeycloakClient("http://localhost"); | ||
const implementation = await client.pickImplementation(); | ||
expect(implementation).toBeInstanceOf(ClientCredentialsKeycloakClient); | ||
}); | ||
|
||
it("should fallback to DynamicClientRegistrationClient if ClientCredentialsKeycloakClient is not properly configured", async () => { | ||
delete process.env.PEPR_KEYCLOAK_CLIENT_STRATEGY; | ||
const client = new DynamicKeycloakClient("http://localhost"); | ||
jest | ||
.spyOn(client["clientCredentialsKeycloakClient"], "getAccessToken") | ||
.mockImplementation(() => { | ||
throw new Error("Client Credentials Keycloak Client is not properly configured"); | ||
}); | ||
const implementation = await client.pickImplementation(); | ||
expect(implementation).toBeInstanceOf(DynamicClientRegistrationClient); | ||
}); | ||
|
||
it("should fallback use ClientCredentialsKeycloakClient if it is properly configured", async () => { | ||
delete process.env.PEPR_KEYCLOAK_CLIENT_STRATEGY; | ||
const client = new DynamicKeycloakClient("http://localhost"); | ||
jest | ||
.spyOn(client["clientCredentialsKeycloakClient"], "getAccessToken") | ||
.mockImplementation(async () => { | ||
return Promise.resolve("mockAccessToken"); | ||
}); | ||
const implementation = await client.pickImplementation(); | ||
expect(implementation).toBeInstanceOf(ClientCredentialsKeycloakClient); | ||
}); | ||
|
||
it("should throw an error for invalid Keycloak Client implementation", async () => { | ||
process.env.PEPR_KEYCLOAK_CLIENT_STRATEGY = "invalid_strategy"; | ||
const client = new DynamicKeycloakClient("http://localhost"); | ||
await expect(client.pickImplementation()).rejects.toThrow( | ||
"Invalid Keycloak Client implementation: invalid_strategy. Supported values: dynamic_client_registration, client_credentials", | ||
); | ||
}); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove this line.