Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions docs/resources/realm_keystore_rsa.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ resource "keycloak_realm_keystore_rsa" "keystore_rsa" {
algorithm = "RS256"
keystore_size = 2048
provider_id = "rsa"

extra_config = {
kid = "my-key-id"
}
}
```

Expand All @@ -44,6 +48,7 @@ resource "keycloak_realm_keystore_rsa" "keystore_rsa" {
- `algorithm` - (Optional) Intended algorithm for the key. Defaults to `RS256`. Use `RSA-OAEP` for encryption keys
- `keystore_size` - (Optional) Size for the generated keys. Defaults to `2048`.
- `provider_id` - (Optional) Use `rsa` for signing keys, `rsa-enc` for encryption keys
- `extra_config` - (Optional) Map of additional provider configuration options passed through to the Keycloak component config. For RSA keystores this can include keys like `kid`.

## Import

Expand Down
23 changes: 23 additions & 0 deletions keycloak/realm_keystore_rsa.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ type RealmKeystoreRsa struct {
PrivateKey string
Certificate string
ProviderId string
ExtraConfig map[string]interface{}
}

func convertFromRealmKeystoreRsaToComponent(realmKey *RealmKeystoreRsa) *component {
Expand All @@ -43,6 +44,13 @@ func convertFromRealmKeystoreRsaToComponent(realmKey *RealmKeystoreRsa) *compone
},
}

// merge extra config
if realmKey.ExtraConfig != nil {
for k, v := range realmKey.ExtraConfig {
componentConfig[k] = []string{fmt.Sprint(v)}
}
}

return &component{
Id: realmKey.Id,
Name: realmKey.Name,
Expand Down Expand Up @@ -86,6 +94,21 @@ func convertFromComponentToRealmKeystoreRsa(component *component, realmId string
ProviderId: component.ProviderId,
}

// capture extra config (anything not in the known keys)
known := map[string]struct{}{
"active": {}, "enabled": {}, "priority": {}, "algorithm": {}, "privateKey": {}, "certificate": {},
}
extra := map[string]interface{}{}
for k, vals := range component.Config {
if _, ok := known[k]; ok {
continue
}
if len(vals) > 0 {
extra[k] = vals[0]
}
}
realmKey.ExtraConfig = extra

return realmKey, nil
}

Expand Down
7 changes: 7 additions & 0 deletions provider/resource_keycloak_realm_keystore_rsa.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,10 @@ func resourceKeycloakRealmKeystoreRsa() *schema.Resource {
Description: "RSA key provider id",
ForceNew: true,
},
"extra_config": {
Type: schema.TypeMap,
Optional: true,
},
},
}
}
Expand All @@ -95,6 +99,8 @@ func getRealmKeystoreRsaFromData(data *schema.ResourceData) *keycloak.RealmKeyst
ProviderId: data.Get("provider_id").(string),
}

mapper.ExtraConfig = getExtraConfigFromData(data)

return mapper
}

Expand All @@ -113,6 +119,7 @@ func setRealmKeystoreRsaData(data *schema.ResourceData, realmKey *keycloak.Realm
data.Set("private_key", realmKey.PrivateKey)
data.Set("certificate", realmKey.Certificate)
}
setExtraConfigData(data, realmKey.ExtraConfig)
}

func resourceKeycloakRealmKeystoreRsaCreate(ctx context.Context, data *schema.ResourceData, meta interface{}) diag.Diagnostics {
Expand Down
77 changes: 77 additions & 0 deletions provider/resource_keycloak_realm_keystore_rsa_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,30 @@ func TestAccKeycloakRealmKeystoreRsa_algorithmValidation(t *testing.T) {
})
}

func TestAccKeycloakRealmKeystoreRsa_extraConfigKid(t *testing.T) {
t.Parallel()

rsaName := acctest.RandomWithPrefix("tf-acc")
kid := acctest.RandomWithPrefix("tf-acc")
privateKey, certificate := generateKeyAndCert(2048)

resource.Test(t, resource.TestCase{
ProviderFactories: testAccProviderFactories,
PreCheck: func() { testAccPreCheck(t) },
CheckDestroy: testAccCheckRealmKeystoreRsaDestroy(),
Steps: []resource.TestStep{
{
Config: testKeycloakRealmKeystoreRsa_withKidExtraConfig(rsaName, privateKey, certificate, kid),
Check: resource.ComposeTestCheckFunc(
testAccCheckRealmKeystoreRsaExists("keycloak_realm_keystore_rsa.realm_rsa"),
testAccCheckRealmKeystoreRsaKidInRealmKeys("keycloak_realm_keystore_rsa.realm_rsa", kid),
),
ExpectNonEmptyPlan: true,
},
},
})
}

func testAccCheckRealmKeystoreRsaExists(resourceName string) resource.TestCheckFunc {
return func(s *terraform.State) error {
_, err := getKeycloakRealmKeystoreRsaFromState(s, resourceName)
Expand All @@ -123,6 +147,36 @@ func testAccCheckRealmKeystoreRsaExists(resourceName string) resource.TestCheckF
}
}

func testAccCheckRealmKeystoreRsaKidInRealmKeys(resourceName, expectedKid string) resource.TestCheckFunc {
return func(s *terraform.State) error {
fetchedKeystore, err := getKeycloakRealmKeystoreRsaFromState(s, resourceName)
if err != nil {
return err
}

keys, err := keycloakClient.GetRealmKeys(testCtx, fetchedKeystore.RealmId)
if err != nil {
return fmt.Errorf("error fetching realm keys: %w", err)
}

var candidates []keycloak.Key
for _, k := range keys.Keys {
if k.Algorithm != nil && *k.Algorithm == fetchedKeystore.Algorithm &&
k.Certificate != nil && *k.Certificate == fetchedKeystore.Certificate {
candidates = append(candidates, k)
}
}

for _, c := range candidates {
if c.Kid != nil && *c.Kid == expectedKid {
return nil
}
}

return fmt.Errorf("could not find expected kid in realm keys. expected kid=%s", expectedKid)
}
}

func testAccCheckRealmKeystoreRsaFetch(resourceName string, keystore *keycloak.RealmKeystoreRsa) resource.TestCheckFunc {
return func(s *terraform.State) error {
fetchedKeystore, err := getKeycloakRealmKeystoreRsaFromState(s, resourceName)
Expand Down Expand Up @@ -258,3 +312,26 @@ resource "keycloak_realm_keystore_rsa" "realm_rsa" {
}
`, testAccRealmUserFederation.Realm, rsaName, attr, val, privateKey, certificate, provider)
}

func testKeycloakRealmKeystoreRsa_withKidExtraConfig(rsaName, privateKey, certificate, kid string) string {
return fmt.Sprintf(`
data "keycloak_realm" "realm" {
realm = "%s"
}

resource "keycloak_realm_keystore_rsa" "realm_rsa" {
name = "%s"
realm_id = data.keycloak_realm.realm.id

priority = 100
algorithm = "RS256"
private_key = "%s"
certificate = "%s"
provider_id = "rsa"

extra_config = {
"kid" = "%s"
}
}
`, testAccRealmUserFederation.Realm, rsaName, privateKey, certificate, kid)
}