Skip to content

Commit

Permalink
Add support for configuration of client scopes on realm level (#464)
Browse files Browse the repository at this point in the history
Fixes #463
  • Loading branch information
thomasdarimont authored Jan 23, 2021
1 parent d2ea860 commit 5f597ac
Show file tree
Hide file tree
Showing 5 changed files with 186 additions and 1 deletion.
5 changes: 5 additions & 0 deletions docs/resources/realm.md
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,11 @@ Each of these attributes are blocks with the following attributes:

- `internal_id` - (Computed) When importing realms created outside of this terraform provider, they could use generated arbitrary IDs for the internal realm id. Realms created by this provider always use the realm's name for its internal id.

## Default Client Scopes

- `default_default_client_scopes` - (Optional) A list of default default client scopes to be used for client definitions. Defaults to `[]` or keycloak's built-in default default client-scopes.
- `default_optional_client_scopes` - (Optional) A list of default optional client scopes to be used for client definitions. Defaults to `[]` or keycloak's built-in default optional client-scopes.

## Import

Realms can be imported using their name.
Expand Down
4 changes: 4 additions & 0 deletions keycloak/realm.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,10 @@ type Realm struct {
//extra attributes of a realm
Attributes map[string]interface{} `json:"attributes"`

// client-scope mapping defaults
DefaultDefaultClientScopes []string `json:"defaultDefaultClientScopes,omitempty"`
DefaultOptionalClientScopes []string `json:"defaultOptionalClientScopes,omitempty"`

BrowserSecurityHeaders BrowserSecurityHeaders `json:"browserSecurityHeaders"`

BruteForceProtected bool `json:"bruteForceProtected"`
Expand Down
17 changes: 16 additions & 1 deletion provider/data_source_keycloak_realm.go
Original file line number Diff line number Diff line change
Expand Up @@ -429,7 +429,22 @@ func dataSourceKeycloakRealm() *schema.Resource {
Optional: true,
Computed: true,
},

// default default client scopes
"default_default_client_scopes": {
Type: schema.TypeSet,
Elem: &schema.Schema{Type: schema.TypeString},
Computed: true,
Optional: true,
ForceNew: false,
},
// default optional client scopes
"default_optional_client_scopes": {
Type: schema.TypeSet,
Elem: &schema.Schema{Type: schema.TypeString},
Computed: true,
Optional: true,
ForceNew: false,
},
// WebAuthn
"web_authn_policy": {
Type: schema.TypeList,
Expand Down
36 changes: 36 additions & 0 deletions provider/resource_keycloak_realm.go
Original file line number Diff line number Diff line change
Expand Up @@ -524,6 +524,22 @@ func resourceKeycloakRealm() *schema.Resource {
Optional: true,
},

// default default client scopes
"default_default_client_scopes": {
Type: schema.TypeSet,
Elem: &schema.Schema{Type: schema.TypeString},
Optional: true,
ForceNew: false,
},

// default optional client scopes
"default_optional_client_scopes": {
Type: schema.TypeSet,
Elem: &schema.Schema{Type: schema.TypeString},
Optional: true,
ForceNew: false,
},

// WebAuthn
"web_authn_policy": {
Type: schema.TypeList,
Expand Down Expand Up @@ -857,6 +873,22 @@ func getRealmFromData(data *schema.ResourceData) (*keycloak.Realm, error) {
}
realm.Attributes = attributes

defaultDefaultClientScopes := make([]string, 0)
if v, ok := data.GetOk("default_default_client_scopes"); ok {
for _, defaultDefaultClientScope := range v.(*schema.Set).List() {
defaultDefaultClientScopes = append(defaultDefaultClientScopes, defaultDefaultClientScope.(string))
}
}
realm.DefaultDefaultClientScopes = defaultDefaultClientScopes

defaultOptionalClientScopes := make([]string, 0)
if v, ok := data.GetOk("default_optional_client_scopes"); ok {
for _, defaultOptionalClientScope := range v.(*schema.Set).List() {
defaultOptionalClientScopes = append(defaultOptionalClientScopes, defaultOptionalClientScope.(string))
}
}
realm.DefaultOptionalClientScopes = defaultOptionalClientScopes

//WebAuthn
if v, ok := data.GetOk("web_authn_policy"); ok {
webAuthnPolicy := v.([]interface{})[0].(map[string]interface{})
Expand Down Expand Up @@ -1116,6 +1148,10 @@ func setRealmData(data *schema.ResourceData, realm *keycloak.Realm) {
}
}
data.Set("attributes", attributes)

// default and optional client scope mappings
data.Set("default_default_client_scopes", realm.DefaultDefaultClientScopes)
data.Set("default_optional_client_scopes", realm.DefaultOptionalClientScopes)
}

func getBruteForceDetectionSettings(realm *keycloak.Realm) map[string]interface{} {
Expand Down
125 changes: 125 additions & 0 deletions provider/resource_keycloak_realm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -702,6 +702,131 @@ func TestAccKeycloakRealm_internalId(t *testing.T) {
})
}

func TestAccKeycloakRealm_default_client_scopes(t *testing.T) {

realmName := acctest.RandomWithPrefix("tf-acc")
defaultDefaultClientScope := []string{"profile"}
defaultOptionalClientScope := []string{"email", "roles"}

realm := &keycloak.Realm{
Realm: realmName,
}

resource.Test(t, resource.TestCase{
ProviderFactories: testAccProviderFactories,
PreCheck: func() { testAccPreCheck(t) },
CheckDestroy: testAccCheckKeycloakRealmDestroy(),
Steps: []resource.TestStep{
{
ResourceName: "keycloak_realm.realm",
ImportStateId: realmName,
ImportState: true,
Config: testKeycloakRealm_default_client_scopes(realmName, defaultDefaultClientScope, defaultOptionalClientScope),
PreConfig: func() {
err := keycloakClient.NewRealm(realm)
if err != nil {
t.Fatal(err)
}
},
Check: testAccCheckKeycloakRealm_default_client_scopes(realmName, defaultDefaultClientScope, defaultOptionalClientScope),
},
},
})

// test empty default client scope configuration
realmName2 := acctest.RandomWithPrefix("tf-acc")
defaultDefaultClientScope2 := []string{} // deliberately empty
defaultOptionalClientScope2 := []string{} // deliberately empty

realm2 := &keycloak.Realm{
Realm: realmName2,
}
resource.Test(t, resource.TestCase{
ProviderFactories: testAccProviderFactories,
PreCheck: func() { testAccPreCheck(t) },
CheckDestroy: testAccCheckKeycloakRealmDestroy(),
Steps: []resource.TestStep{
{
ResourceName: "keycloak_realm.realm",
ImportStateId: realmName2,
ImportState: true,
Config: testKeycloakRealm_default_client_scopes(realmName2, defaultDefaultClientScope2, defaultOptionalClientScope2),
PreConfig: func() {
err := keycloakClient.NewRealm(realm2)
if err != nil {
t.Fatal(err)
}
},
Check: testAccCheckKeycloakRealm_default_client_scopes(realmName2, defaultDefaultClientScope2, defaultOptionalClientScope2),
},
},
})
}

func testKeycloakRealm_default_client_scopes(realm string, defaultDefaultClientScopes []string, defaultOptionalClientScopes []string) string {

defaultDefaultClientScopesString := fmt.Sprintf("%s", arrayOfStringsForTerraformResource(defaultDefaultClientScopes))
defaultOptionalClientScopesString := fmt.Sprintf("%s", arrayOfStringsForTerraformResource(defaultOptionalClientScopes))

return fmt.Sprintf(`
resource "keycloak_realm" "realm" {
realm = "%s"
enabled = true
default_default_client_scopes = %s
default_optional_client_scopes = %s
}
`, realm, defaultDefaultClientScopesString, defaultOptionalClientScopesString)
}

func testAccCheckKeycloakRealm_default_client_scopes(resourceName string, defaultDefaultClientScope, defaultOptionalClientScope []string) resource.TestCheckFunc {
return func(s *terraform.State) error {
realm, err := getRealmFromState(s, resourceName)
if err != nil {
return err
}

if len(defaultDefaultClientScope) == 0 {
if len(realm.DefaultDefaultClientScopes) != 0 {
return fmt.Errorf("expected realm %s to have empty default default client scopes but was %s", realm.Realm, realm.DefaultDefaultClientScopes)
}
} else {
for _, expectedScope := range defaultDefaultClientScope {
found := false
for _, s := range realm.DefaultDefaultClientScopes {
if expectedScope == s {
found = true
break
}
}
if !found {
return fmt.Errorf("expected realm %s to have default default client scopes with value %s but was %s", realm.Realm, defaultDefaultClientScope, realm.DefaultDefaultClientScopes)
}
}
}

if len(defaultOptionalClientScope) == 0 {
if len(realm.DefaultOptionalClientScopes) != 0 {
return fmt.Errorf("expected realm %s to have empty default optional client scopes but was %s", realm.Realm, realm.DefaultOptionalClientScopes)
}
} else {
for _, expectedScope := range defaultOptionalClientScope {
found := false
for _, s := range realm.DefaultOptionalClientScopes {
if expectedScope == s {
found = true
break
}
}
if !found {
return fmt.Errorf("expected realm %s to have default optional client scopes with value %s but was %s", realm.Realm, defaultOptionalClientScope, realm.DefaultOptionalClientScopes)
}
}
}

return nil
}
}

func TestAccKeycloakRealm_webauthn(t *testing.T) {
realmName := acctest.RandomWithPrefix("tf-acc")
realmDisplayName := acctest.RandomWithPrefix("tf-acc")
Expand Down

0 comments on commit 5f597ac

Please sign in to comment.