Skip to content

Commit 3563c0d

Browse files
committed
feat!: add terraform keycloak provider
1 parent f408666 commit 3563c0d

File tree

5 files changed

+246
-68
lines changed

5 files changed

+246
-68
lines changed

cmd/main.go

Lines changed: 80 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ import (
5252
// Config for the overall controller
5353
type config struct {
5454
// Whether to use the dummy implementation of the Keycloak interface
55-
KeycloakDummy bool `env:"KEYCLOAK_DUMMY"`
55+
KeycloakProvider string `env:"KEYCLOAK_PROVIDER"`
5656

5757
// If set, will try to populate keycloakConfig using this secret
5858
GCPSecret string `env:"GCP_SECRET"`
@@ -164,50 +164,13 @@ func main() {
164164
cfg, err := env.ParseAsWithOptions[config](env.Options{Prefix: "KEYCLOAKERATOR_"})
165165
if err != nil {
166166
setupLog.Error(err, "unable to parse general config from env")
167+
os.Exit(1)
167168
}
168169

169-
var oidcService controller.OIDCService = &controller.OidcDummy{}
170-
if !cfg.KeycloakDummy {
171-
kcConfig := &keycloakConfig{}
172-
173-
if cfg.GCPSecret != "" {
174-
if err = readKeycloakConfigFromSecretManager(ctx, cfg.GCPSecret, kcConfig); err != nil {
175-
setupLog.Error(err, "unable to fetch or parse config from secret manager")
176-
}
177-
}
178-
179-
err = env.ParseWithOptions(kcConfig, env.Options{
180-
Prefix: "KEYCLOAKERATOR_",
181-
})
182-
if err != nil {
183-
fmt.Printf("error parsing environment variables: %s", err)
184-
os.Exit(1)
185-
}
186-
187-
if !kcConfig.Valid() {
188-
fmt.Print("missing one or more keycloak parameters")
189-
os.Exit(1)
190-
}
191-
192-
setupLog.Info("initializing GoCloak wrapper")
193-
194-
// The oauth2/clientcredentials package provides a TokenSource which keeps our Keycloak token
195-
// up to date automatically.
196-
authConfig := &clientcredentials.Config{
197-
ClientID: kcConfig.ClientId,
198-
ClientSecret: kcConfig.ClientSecret,
199-
TokenURL: kcConfig.KeycloakUrl.JoinPath(
200-
"realms",
201-
kcConfig.ClientRealm,
202-
"protocol/openid-connect/token",
203-
).String(),
204-
}
205-
206-
oidcService = keycloak.NewGocloakWrapper(
207-
kcConfig.KeycloakUrl.String(),
208-
kcConfig.ClientRealm,
209-
authConfig.TokenSource(ctx),
210-
)
170+
oidcService, err := initKeycloakProvider(ctx, cfg)
171+
if err != nil {
172+
setupLog.Error(err, "unable to initialize keycloak provider")
173+
os.Exit(1)
211174
}
212175

213176
// Set up our reconciler
@@ -257,3 +220,77 @@ func readKeycloakConfigFromSecretManager(ctx context.Context, secret string, kc
257220

258221
return yaml.Unmarshal(resp.GetPayload().GetData(), kc)
259222
}
223+
224+
func initKeycloakProvider(ctx context.Context, cfg config) (controller.OIDCService, error) {
225+
switch cfg.KeycloakProvider {
226+
case "dummy":
227+
return &controller.OidcDummy{}, nil
228+
case "gocloak":
229+
return gocloakProvider(ctx, cfg)
230+
case "terraform":
231+
return terraformProvider(ctx, cfg)
232+
default:
233+
return nil, fmt.Errorf("unknown keycloak provider %q", cfg.KeycloakProvider)
234+
}
235+
}
236+
237+
func terraformProvider(ctx context.Context, cfg config) (controller.OIDCService, error) {
238+
kcConfig, err := getKeycloakConfig(ctx, cfg.GCPSecret)
239+
if err != nil {
240+
return nil, err
241+
}
242+
243+
return keycloak.NewTerraformProviderWrapper(ctx,
244+
kcConfig.KeycloakUrl.String(),
245+
kcConfig.ClientId,
246+
kcConfig.ClientSecret,
247+
kcConfig.ClientRealm,
248+
)
249+
}
250+
251+
func gocloakProvider(ctx context.Context, cfg config) (controller.OIDCService, error) {
252+
kcConfig, err := getKeycloakConfig(ctx, cfg.GCPSecret)
253+
if err != nil {
254+
return nil, err
255+
}
256+
257+
// The oauth2/clientcredentials package provides a TokenSource which keeps our Keycloak token
258+
// up to date automatically.
259+
authConfig := &clientcredentials.Config{
260+
ClientID: kcConfig.ClientId,
261+
ClientSecret: kcConfig.ClientSecret,
262+
TokenURL: kcConfig.KeycloakUrl.JoinPath(
263+
"realms",
264+
kcConfig.ClientRealm,
265+
"protocol/openid-connect/token",
266+
).String(),
267+
}
268+
269+
return keycloak.NewGocloakWrapper(
270+
kcConfig.KeycloakUrl.String(),
271+
kcConfig.ClientRealm,
272+
authConfig.TokenSource(ctx),
273+
), nil
274+
}
275+
276+
func getKeycloakConfig(ctx context.Context, gcpSecret string) (*keycloakConfig, error) {
277+
kcConfig := &keycloakConfig{}
278+
279+
if gcpSecret != "" {
280+
if err := readKeycloakConfigFromSecretManager(ctx, gcpSecret, kcConfig); err != nil {
281+
return nil, fmt.Errorf("unable to fetch or parse config from secret manager: %w", err)
282+
}
283+
}
284+
285+
if err := env.ParseWithOptions(kcConfig, env.Options{
286+
Prefix: "KEYCLOAKERATOR_",
287+
}); err != nil {
288+
return nil, fmt.Errorf("error parsing environment variables: %s", err)
289+
}
290+
291+
if !kcConfig.Valid() {
292+
return nil, fmt.Errorf("missing one or more keycloak parameters")
293+
}
294+
295+
return kcConfig, nil
296+
}

go.mod

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
module github.com/statisticsnorway/keycloakerator
22

3-
go 1.21
3+
go 1.22.0
4+
5+
toolchain go1.23.1
46

57
require (
68
cloud.google.com/go/secretmanager v1.13.1
79
github.com/Nerzal/gocloak/v13 v13.9.0
810
github.com/caarlos0/env/v11 v11.0.1
11+
github.com/keycloak/terraform-provider-keycloak v0.0.0-20250210105921-9ee4f2d90096
912
github.com/onsi/ginkgo/v2 v2.14.0
1013
github.com/onsi/gomega v1.30.0
11-
golang.org/x/oauth2 v0.20.0
14+
golang.org/x/oauth2 v0.22.0
1215
gopkg.in/yaml.v3 v3.0.1
1316
k8s.io/api v0.29.0
1417
k8s.io/apimachinery v0.29.0
@@ -20,13 +23,14 @@ require (
2023
require (
2124
cloud.google.com/go/auth v0.4.1 // indirect
2225
cloud.google.com/go/auth/oauth2adapt v0.2.2 // indirect
23-
cloud.google.com/go/compute/metadata v0.3.0 // indirect
26+
cloud.google.com/go/compute/metadata v0.5.0 // indirect
2427
cloud.google.com/go/iam v1.1.8 // indirect
2528
github.com/beorn7/perks v1.0.1 // indirect
26-
github.com/cespare/xxhash/v2 v2.2.0 // indirect
29+
github.com/cespare/xxhash/v2 v2.3.0 // indirect
2730
github.com/davecgh/go-spew v1.1.1 // indirect
2831
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
2932
github.com/evanphx/json-patch/v5 v5.8.0 // indirect
33+
github.com/fatih/color v1.16.0 // indirect
3034
github.com/felixge/httpsnoop v1.0.4 // indirect
3135
github.com/fsnotify/fsnotify v1.7.0 // indirect
3236
github.com/go-logr/logr v1.4.1 // indirect
@@ -49,10 +53,18 @@ require (
4953
github.com/google/uuid v1.6.0 // indirect
5054
github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect
5155
github.com/googleapis/gax-go/v2 v2.12.4 // indirect
56+
github.com/hashicorp/errwrap v1.1.0 // indirect
57+
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
58+
github.com/hashicorp/go-hclog v1.6.3 // indirect
59+
github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
60+
github.com/hashicorp/go-version v1.7.0 // indirect
61+
github.com/hashicorp/terraform-plugin-log v0.9.0 // indirect
5262
github.com/imdario/mergo v0.3.6 // indirect
5363
github.com/josharian/intern v1.0.0 // indirect
5464
github.com/json-iterator/go v1.1.12 // indirect
5565
github.com/mailru/easyjson v0.7.7 // indirect
66+
github.com/mattn/go-colorable v0.1.13 // indirect
67+
github.com/mattn/go-isatty v0.0.20 // indirect
5668
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect
5769
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
5870
github.com/modern-go/reflect2 v1.0.2 // indirect
@@ -75,7 +87,7 @@ require (
7587
go.uber.org/zap v1.26.0 // indirect
7688
golang.org/x/crypto v0.31.0 // indirect
7789
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e // indirect
78-
golang.org/x/net v0.25.0 // indirect
90+
golang.org/x/net v0.33.0 // indirect
7991
golang.org/x/sync v0.10.0 // indirect
8092
golang.org/x/sys v0.28.0 // indirect
8193
golang.org/x/term v0.27.0 // indirect
@@ -85,10 +97,10 @@ require (
8597
gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
8698
google.golang.org/api v0.180.0 // indirect
8799
google.golang.org/genproto v0.0.0-20240401170217-c3f982113cda // indirect
88-
google.golang.org/genproto/googleapis/api v0.0.0-20240513163218-0867130af1f8 // indirect
89-
google.golang.org/genproto/googleapis/rpc v0.0.0-20240509183442-62759503f434 // indirect
90-
google.golang.org/grpc v1.63.2 // indirect
91-
google.golang.org/protobuf v1.34.1 // indirect
100+
google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142 // indirect
101+
google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 // indirect
102+
google.golang.org/grpc v1.67.1 // indirect
103+
google.golang.org/protobuf v1.35.1 // indirect
92104
gopkg.in/inf.v0 v0.9.1 // indirect
93105
gopkg.in/yaml.v2 v2.4.0 // indirect
94106
k8s.io/apiextensions-apiserver v0.29.0 // indirect

0 commit comments

Comments
 (0)