Skip to content

Commit

Permalink
feat: add keycloak_realm_keystore_custom resource
Browse files Browse the repository at this point in the history
  • Loading branch information
rruxandra committed Mar 3, 2024
1 parent 3f6b75b commit c241735
Show file tree
Hide file tree
Showing 5 changed files with 588 additions and 0 deletions.
59 changes: 59 additions & 0 deletions docs/resources/realm_keystore_custom.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
---
page_title: "keycloak_realm_keystore_custom Resources"
---

# keycloak\_realm\_keystore\_custom Resources

Allows for creating and managing custom Realm keystores within Keycloak.

A realm keystore manages keys that are used by Keycloak to perform cryptographic signatures and encryption.

## Example Usage

```hcl
resource "keycloak_realm" "realm" {
realm = "my-realm"
}
resource "keycloak_realm_keystore_custom" "keystore_custom" {
name = "my-custom-keystore"
realm_id = keycloak_realm.realm.id
enabled = true
active = true
priority = 100
provider_id = "custom-keystore"
provider_type = "org.company.keys.KeyProvider"
extra_config = {
"key1" = "value1"
"key2" = "value2"
}
}
```

## Argument Reference

- `name` - (Required) Display name of provider when linked in admin console.
- `realm_id` - (Required) The realm this keystore exists in.
- `enabled` - (Optional) When `false`, key is not accessible in this realm. Defaults to `true`.
- `active` - (Optional) When `false`, key in not used for signing. Defaults to `true`.
- `priority` - (Optional) Priority for the provider. Defaults to `0`
- `providerId` - (Required) The ID of the keystore provider.
- `providerType` - (Required) The type of the keystore provider.
- `extra_config` - (Optional) A map of key/value pairs to add extra configuration attributes to this keystore.
``` hcl
extra_config = {
"custom.attribute" = "value"
}
```

## Import

Realm keys can be imported using realm name and keystore id, you can find it in web UI.

Example:

```bash
$ terraform import keycloak_realm_keystore_custom.keystore_custom my-realm/618cfba7-49aa-4c09-9a19-2f699b576f0b
```
140 changes: 140 additions & 0 deletions keycloak/realm_keystore_custom.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
package keycloak

import (
"context"
"fmt"
"strconv"
)

type RealmKeystoreCustom struct {
Id string
Name string
RealmId string

Active bool
Enabled bool
Priority int

ProviderId string
ProviderType string

ExtraConfig map[string]interface{} `json:"-"`
}

func convertFromRealmKeystoreCustomToComponent(realmKey *RealmKeystoreCustom) *component {
componentConfig := map[string][]string{
"active": {
strconv.FormatBool(realmKey.Active),
},
"enabled": {
strconv.FormatBool(realmKey.Enabled),
},
"priority": {
strconv.Itoa(realmKey.Priority),
},
"providerId": {
realmKey.ProviderId,
},
"providerType": {
realmKey.ProviderType,
},
}

for key, value := range realmKey.ExtraConfig {
if strVal, ok := value.(string); ok {
componentConfig[key] = []string{strVal}
} else if boolVal, ok := value.(bool); ok {
componentConfig[key] = []string{strconv.FormatBool(boolVal)}
} else if intVal, ok := value.(int); ok {
componentConfig[key] = []string{strconv.Itoa(intVal)}
}
}

return &component{
Id: realmKey.Id,
Name: realmKey.Name,
ParentId: realmKey.RealmId,
ProviderId: realmKey.ProviderId,
ProviderType: realmKey.ProviderType,
Config: componentConfig,
}
}

func convertFromComponentToRealmKeystoreCustom(component *component, realmId string) (*RealmKeystoreCustom, error) {
active, err := parseBoolAndTreatEmptyStringAsFalse(component.getConfig("active"))
if err != nil {
return nil, err
}

enabled, err := parseBoolAndTreatEmptyStringAsFalse(component.getConfig("enabled"))
if err != nil {
return nil, err
}

priority := 0 // Default priority
if component.getConfig("priority") != "" {
priority, err = strconv.Atoi(component.getConfig("priority"))
if err != nil {
return nil, err
}
}

providerId := component.ProviderId
providerType := component.ProviderType

realmKey := &RealmKeystoreCustom{
Id: component.Id,
Name: component.Name,
RealmId: realmId,

Active: active,
Enabled: enabled,
Priority: priority,
ProviderId: providerId,
ProviderType: providerType,
ExtraConfig: make(map[string]interface{}),
}

for key, value := range component.Config {
if len(value) > 0 {
switch key {
case "active", "enabled", "priority":
continue
default:
realmKey.ExtraConfig[key] = value[0]
}
}
}

return realmKey, nil
}

func (keycloakClient *KeycloakClient) NewRealmKeystoreCustom(ctx context.Context, realmKey *RealmKeystoreCustom) error {
_, location, err := keycloakClient.post(ctx, fmt.Sprintf("/realms/%s/components", realmKey.RealmId), convertFromRealmKeystoreCustomToComponent(realmKey))
if err != nil {
return err
}

realmKey.Id = getIdFromLocationHeader(location)

return nil
}

func (keycloakClient *KeycloakClient) GetRealmKeystoreCustom(ctx context.Context, realmId, id string) (*RealmKeystoreCustom, error) {
var component *component

err := keycloakClient.get(ctx, fmt.Sprintf("/realms/%s/components/%s", realmId, id), &component, nil)
if err != nil {
return nil, err
}

return convertFromComponentToRealmKeystoreCustom(component, realmId)
}

func (keycloakClient *KeycloakClient) UpdateRealmKeystoreCustom(ctx context.Context, realmKey *RealmKeystoreCustom) error {
return keycloakClient.put(ctx, fmt.Sprintf("/realms/%s/components/%s", realmKey.RealmId, realmKey.Id), convertFromRealmKeystoreCustomToComponent(realmKey))
}

func (keycloakClient *KeycloakClient) DeleteRealmKeystoreCustom(ctx context.Context, realmId, id string) error {
return keycloakClient.delete(ctx, fmt.Sprintf("/realms/%s/components/%s", realmId, id), nil)
}
1 change: 1 addition & 0 deletions provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ func KeycloakProvider(client *keycloak.KeycloakClient) *schema.Provider {
"keycloak_realm_keystore_java_keystore": resourceKeycloakRealmKeystoreJavaKeystore(),
"keycloak_realm_keystore_rsa": resourceKeycloakRealmKeystoreRsa(),
"keycloak_realm_keystore_rsa_generated": resourceKeycloakRealmKeystoreRsaGenerated(),
"keycloak_realm_keystore_custom": resourceKeycloakRealmKeystoreCustom(),
"keycloak_realm_user_profile": resourceKeycloakRealmUserProfile(),
"keycloak_required_action": resourceKeycloakRequiredAction(),
"keycloak_group": resourceKeycloakGroup(),
Expand Down
171 changes: 171 additions & 0 deletions provider/resource_keycloak_realm_keystore_custom.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
package provider

import (
"context"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/mrparkers/terraform-provider-keycloak/keycloak"
"reflect"
)

func resourceKeycloakRealmKeystoreCustom() *schema.Resource {
return &schema.Resource{
CreateContext: resourceKeycloakRealmKeystoreCustomCreate,
ReadContext: resourceKeycloakRealmKeystoreCustomRead,
UpdateContext: resourceKeycloakRealmKeystoreCustomUpdate,
DeleteContext: resourceKeycloakRealmKeystoreCustomDelete,
Importer: &schema.ResourceImporter{
StateContext: resourceKeycloakRealmKeystoreGenericImport,
},
Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Required: true,
Description: "Display name of provider when linked in admin console.",
},
"realm_id": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"active": {
Type: schema.TypeBool,
Optional: true,
Default: true,
Description: "Set if the keys are enabled",
},
"enabled": {
Type: schema.TypeBool,
Optional: true,
Default: true,
Description: "Set if the keys are enabled",
},
"priority": {
Type: schema.TypeInt,
Optional: true,
Default: 0,
Description: "Priority for the provider",
},
"provider_id": {
Type: schema.TypeString,
Required: true,
Description: "The ID of the custom provider",
},
"provider_type": {
Type: schema.TypeString,
Required: true,
Description: "The type of the custom provider",
},
"extra_config": {
Type: schema.TypeMap,
Optional: true,
ValidateDiagFunc: validateExtraConfig(reflect.ValueOf(&keycloak.RealmKeystoreCustom{}).Elem()),
},
},
}
}

func getRealmKeystoreCustomFromData(data *schema.ResourceData) (*keycloak.RealmKeystoreCustom, error) {
keystore := &keycloak.RealmKeystoreCustom{
Id: data.Id(),
Name: data.Get("name").(string),
RealmId: data.Get("realm_id").(string),

Active: data.Get("active").(bool),
Enabled: data.Get("enabled").(bool),
Priority: data.Get("priority").(int),
ProviderId: data.Get("provider_id").(string),
ProviderType: data.Get("provider_type").(string),

ExtraConfig: getExtraConfigFromData(data),
}

return keystore, nil
}

func setRealmKeystoreCustomData(data *schema.ResourceData, realmKey *keycloak.RealmKeystoreCustom) error {
data.SetId(realmKey.Id)

data.Set("name", realmKey.Name)
data.Set("realm_id", realmKey.RealmId)

data.Set("active", realmKey.Active)
data.Set("enabled", realmKey.Enabled)
data.Set("priority", realmKey.Priority)
data.Set("provider_id", realmKey.ProviderId)
data.Set("provider_type", realmKey.ProviderType)

setExtraConfigData(data, realmKey.ExtraConfig)

return nil
}

func resourceKeycloakRealmKeystoreCustomCreate(ctx context.Context, data *schema.ResourceData, meta interface{}) diag.Diagnostics {
keycloakClient := meta.(*keycloak.KeycloakClient)

realmKey, err := getRealmKeystoreCustomFromData(data)
if err != nil {
return diag.FromErr(err)
}

err = keycloakClient.NewRealmKeystoreCustom(ctx, realmKey)
if err != nil {
return diag.FromErr(err)
}

err = setRealmKeystoreCustomData(data, realmKey)
if err != nil {
return diag.FromErr(err)
}

return resourceKeycloakRealmKeystoreCustomRead(ctx, data, meta)
}

func resourceKeycloakRealmKeystoreCustomRead(ctx context.Context, data *schema.ResourceData, meta interface{}) diag.Diagnostics {
keycloakClient := meta.(*keycloak.KeycloakClient)

realmId := data.Get("realm_id").(string)
id := data.Id()

realmKey, err := keycloakClient.GetRealmKeystoreCustom(ctx, realmId, id)
if err != nil {
return handleNotFoundError(ctx, err, data)
}

err = setRealmKeystoreCustomData(data, realmKey)
if err != nil {
return diag.FromErr(err)
}

return nil
}

func resourceKeycloakRealmKeystoreCustomUpdate(ctx context.Context, data *schema.ResourceData, meta interface{}) diag.Diagnostics {
keycloakClient := meta.(*keycloak.KeycloakClient)

realmKey, err := getRealmKeystoreCustomFromData(data)
if err != nil {
return diag.FromErr(err)
}

err = keycloakClient.UpdateRealmKeystoreCustom(ctx, realmKey)
if err != nil {
return diag.FromErr(err)
}

err = setRealmKeystoreCustomData(data, realmKey)
if err != nil {
return diag.FromErr(err)
}

return nil
}

func resourceKeycloakRealmKeystoreCustomDelete(ctx context.Context, data *schema.ResourceData, meta interface{}) diag.Diagnostics {
keycloakClient := meta.(*keycloak.KeycloakClient)

realmId := data.Get("realm_id").(string)
id := data.Id()

return diag.FromErr(keycloakClient.DeleteRealmKeystoreCustom(ctx, realmId, id))
}
Loading

0 comments on commit c241735

Please sign in to comment.