Skip to content

Commit

Permalink
Import Role by Name
Browse files Browse the repository at this point in the history
  • Loading branch information
denniskniep committed Jan 12, 2025
1 parent 5cd3042 commit 2f8e68d
Show file tree
Hide file tree
Showing 9 changed files with 223 additions and 3 deletions.
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,21 @@ Build binary:
make build
```

### Develop locally

Start KIND Cluster `local-dev`, build source code and package into an image.
Deploy that image on the `local-dev` Cluster
```console
make local-deploy
```

```
kubectl apply -f ./local-deploy/manifests/keycloak-provider-secret.yaml
kubectl apply -f ./local-deploy/manifests/keycloak-provider-config.yaml
kubectl patch DeploymentRuntimeConfig runtimeconfig-provider-keycloak --type='merge' --patch-file ./local-deploy/manifests/keycloak-provider-deployment-runtime-config.patch.yaml
```
## Regression Tests
TODO: Add regression test docs
Expand Down
7 changes: 5 additions & 2 deletions config/external_name.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ Copyright 2022 Upbound Inc.

package config

import "github.com/crossplane/upjet/pkg/config"
import (
"github.com/crossplane-contrib/provider-keycloak/config/role"
"github.com/crossplane/upjet/pkg/config"
)

// ExternalNameConfigs contains all external name configurations for this
// provider.
Expand All @@ -30,7 +33,7 @@ var ExternalNameConfigs = map[string]config.ExternalName{
"keycloak_openid_client_service_account_role": config.IdentifierFromProvider,
"keycloak_realm": config.IdentifierFromProvider,
"keycloak_required_action": config.IdentifierFromProvider,
"keycloak_role": config.IdentifierFromProvider,
"keycloak_role": role.IdentifierByNameLookup,
"keycloak_user_groups": config.IdentifierFromProvider,
"keycloak_user_roles": config.IdentifierFromProvider,
"keycloak_users_permissions": config.IdentifierFromProvider,
Expand Down
72 changes: 71 additions & 1 deletion config/role/config.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
package role

import "github.com/crossplane/upjet/pkg/config"
import (
"bytes"
"context"
"errors"
"github.com/crossplane-contrib/provider-keycloak/internal/clients"
"github.com/crossplane/upjet/pkg/config"
"github.com/keycloak/terraform-provider-keycloak/keycloak"
"strings"
"text/template"
)

// Configure configures individual resources by adding custom ResourceConfigurators.
func Configure(p *config.Provider) {
Expand All @@ -13,3 +22,64 @@ func Configure(p *config.Provider) {
}
})
}

var IdentifierByNameLookup = config.ExternalName{

Check failure on line 26 in config/role/config.go

View workflow job for this annotation

GitHub Actions / lint

exported var `IdentifierByNameLookup` should have comment or be unexported (golint)
SetIdentifierArgumentFn: config.NopSetIdentifierArgument,
GetExternalNameFn: GetExternalNameFromRole,
GetIDFn: GetIdFromRole,
DisableNameInitializer: true,
}

func GetIdFromRole(ctx context.Context, externalName string, parameters map[string]any, terraformProviderConfig map[string]any) (string, error) {

Check failure on line 33 in config/role/config.go

View workflow job for this annotation

GitHub Actions / lint

exported function `GetIdFromRole` should have comment or be unexported (golint)

kcClient, err := clients.NewKeycloakClient(ctx, terraformProviderConfig)
if err != nil {
return "", err
}

realmId, realmIdExists := parameters["realm_id"]

Check failure on line 40 in config/role/config.go

View workflow job for this annotation

GitHub Actions / lint

var `realmId` should be `realmID` (golint)
if !realmIdExists {
return "", errors.New("realmId not set")
}

name, nameExists := parameters["name"]
if !nameExists {
return "", errors.New("name not set")
}

clientId, clientIdExists := parameters["client_id"]

Check failure on line 50 in config/role/config.go

View workflow job for this annotation

GitHub Actions / lint

var `clientId` should be `clientID` (golint)
if !clientIdExists {
clientId = ""
}

role, err := kcClient.GetRoleByName(ctx, realmId.(string), clientId.(string), name.(string))
if err != nil {
var apiErr *keycloak.ApiError
if errors.As(err, &apiErr) && apiErr.Code == 404 {
return "", nil
}

return "", err
}

return role.Id, nil
}

func GetExternalNameFromRole(tfState map[string]any) (string, error) {

Check failure on line 68 in config/role/config.go

View workflow job for this annotation

GitHub Actions / lint

exported function `GetExternalNameFromRole` should have comment or be unexported (golint)
t, err := template.New("getExternalName").Funcs(template.FuncMap{
"ToLower": strings.ToLower,
"ToUpper": strings.ToUpper,
}).Parse(`{{if eq .client_id ""}}{{ .realm_id }}/{{ .name }}{{else}}{{ .realm_id }}/{{ .client_id }}/{{ .name }}{{end}}`)

if err != nil {
return "", err
}

var buf bytes.Buffer
err = t.Execute(&buf, tfState)
if err != nil {
return "", err
}
externalName := buf.String()
return externalName, nil
}
69 changes: 69 additions & 0 deletions internal/clients/keycloak_client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package clients

import (
"context"
"fmt"
"github.com/crossplane/upjet/pkg/terraform"
"github.com/keycloak/terraform-provider-keycloak/keycloak"
)

func NewKeycloakClient(ctx context.Context, terraformProviderConfig map[string]any) (*keycloak.KeycloakClient, error) {

Check failure on line 10 in internal/clients/keycloak_client.go

View workflow job for this annotation

GitHub Actions / lint

exported function `NewKeycloakClient` should have comment or be unexported (golint)
config := terraformProviderConfig["configuration"].(terraform.ProviderConfiguration)

url := tryGetString(config, "url", "")
basePath := tryGetString(config, "base_path", "")
clientId := tryGetString(config, "client_id", "")

Check failure on line 15 in internal/clients/keycloak_client.go

View workflow job for this annotation

GitHub Actions / lint

var `clientId` should be `clientID` (golint)
clientSecret := tryGetString(config, "client_secret", "")
username := tryGetString(config, "username", "")
password := tryGetString(config, "password", "")
realm := tryGetString(config, "realm", "master")
initialLogin := tryGetBool(config, "initial_login", true)
clientTimeout := tryGetInt(config, "client_timeout", 15)
tlsInsecureSkipVerify := tryGetBool(config, "tls_insecure_skip_verify", false)
rootCaCertificate := tryGetString(config, "root_ca_certificate", "")
redHatSSO := tryGetBool(config, "initial_login", false)
additionalHeaders := tryGetMap(config, "additional_headers")
userAgent := fmt.Sprintf("Crossplane Keycloak Provider")

Check failure on line 26 in internal/clients/keycloak_client.go

View workflow job for this annotation

GitHub Actions / lint

S1039: unnecessary use of fmt.Sprintf (gosimple)

keycloakClient, err := keycloak.NewKeycloakClient(ctx, url, basePath, clientId, clientSecret, realm, username, password, initialLogin, clientTimeout, rootCaCertificate, tlsInsecureSkipVerify, userAgent, redHatSSO, additionalHeaders)
if err != nil {
return nil, err
}
return keycloakClient, nil

}

func tryGetString(m map[string]any, key string, defaultValue string) string {
value, ok := m[key]
if ok {
return value.(string)
}
return defaultValue
}

func tryGetBool(m map[string]any, key string, defaultValue bool) bool {
value, ok := m[key]
if ok {
return value.(bool)
}
return defaultValue
}

func tryGetInt(m map[string]any, key string, defaultValue int) int {
value, ok := m[key]
if ok {
return value.(int)
}
return defaultValue
}

func tryGetMap(m map[string]any, key string) map[string]string {
value, ok := m[key]
result := make(map[string]string)
if ok {
for k, v := range value.(map[string]interface{}) {
result[k] = v.(string)
}
}
return result
}
13 changes: 13 additions & 0 deletions local-deploy/manifests/keycloak-provider-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
apiVersion: keycloak.crossplane.io/v1beta1
kind: ProviderConfig
metadata:
name: keycloak-provider-config
namespace: upbound-system
spec:
credentials:
source: Secret
secretRef:
name: keycloak-credentials
key: credentials
namespace: upbound-system
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
spec:
deploymentTemplate:
spec:
replicas: 0
19 changes: 19 additions & 0 deletions local-deploy/manifests/keycloak-provider-secret.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
---
apiVersion: v1
kind: Secret
metadata:
name: keycloak-credentials
namespace: upbound-system
labels:
type: provider-credentials
type: Opaque
stringData:
credentials: |
{
"client_id":"admin-cli",
"username": "<username>",
"password": "<password>",
"url": "http://<host>:<port>",
"base_path": "/",
"realm": "master"
}
14 changes: 14 additions & 0 deletions local-deploy/manifests/testclientrole.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
apiVersion: role.keycloak.crossplane.io/v1alpha1
kind: Role
metadata:
name: test-client
namespace: upbound-system
spec:
forProvider:
realmId: "master" # The realm to which this user belongs
name: "abc" # The username for this user
clientId: "terraform"
description: "abc"
providerConfigRef:
name: "keycloak-provider-config" # Reference to the provider configuration
13 changes: 13 additions & 0 deletions local-deploy/manifests/testrole.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
apiVersion: role.keycloak.crossplane.io/v1alpha1
kind: Role
metadata:
name: test
namespace: upbound-system
spec:
forProvider:
realmId: "master" # The realm to which this user belongs
name: "abc" # The username for this user
description: "abc"
providerConfigRef:
name: "keycloak-provider-config" # Reference to the provider configuration

0 comments on commit 2f8e68d

Please sign in to comment.