Skip to content

Commit

Permalink
add attribute support for keycloak_role resource (#475)
Browse files Browse the repository at this point in the history
  • Loading branch information
dbolack authored Feb 28, 2021
1 parent caf63d9 commit 915b23f
Show file tree
Hide file tree
Showing 6 changed files with 123 additions and 6 deletions.
27 changes: 27 additions & 0 deletions docs/resources/role.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ resource "keycloak_role" "realm_role" {
realm_id = keycloak_realm.realm.id
name = "my-realm-role"
description = "My Realm Role"
attributes = {
key = "value"
}
}
```

Expand Down Expand Up @@ -49,6 +52,9 @@ resource "keycloak_role" "client_role" {
client_id = keycloak_client.openid_client.id
name = "my-client-role"
description = "My Client Role"
attributes = {
key = "value"
}
}
```

Expand All @@ -65,21 +71,33 @@ resource "keycloak_realm" "realm" {
resource "keycloak_role" "create_role" {
realm_id = keycloak_realm.realm.id
name = "create"
attributes = {
key = "value"
}
}
resource "keycloak_role" "read_role" {
realm_id = keycloak_realm.realm.id
name = "read"
attributes = {
key = "value"
}
}
resource "keycloak_role" "update_role" {
realm_id = keycloak_realm.realm.id
name = "update"
attributes = {
key = "value"
}
}
resource "keycloak_role" "delete_role" {
realm_id = keycloak_realm.realm.id
name = "delete"
attributes = {
key = "value"
}
}
# client role
Expand All @@ -102,6 +120,10 @@ resource "keycloak_role" "client_role" {
client_id = keycloak_client.openid_client.id
name = "my-client-role"
description = "My Client Role"
attributes = {
key = "value"
}
}
resource "keycloak_role" "admin_role" {
Expand All @@ -114,6 +136,10 @@ resource "keycloak_role" "admin_role" {
keycloak_role.delete_role.id,
keycloak_role.client_role.id,
]
attributes = {
key = "value"
}
}
```

Expand All @@ -124,6 +150,7 @@ resource "keycloak_role" "admin_role" {
- `client_id` - (Optional) When specified, this role will be created as a client role attached to the client with the provided ID
- `description` - (Optional) The description of the role
- `composite_roles` - (Optional) When specified, this role will be a composite role, composed of all roles that have an ID present within this list.
- `attributes` - (Optional) Attribute key/value pairs


## Import
Expand Down
3 changes: 2 additions & 1 deletion keycloak/keycloak_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"crypto/x509"
"encoding/json"
"fmt"
"github.com/hashicorp/go-version"
"io/ioutil"
"log"
"net/http"
Expand All @@ -15,6 +14,8 @@ import (
"strings"
"time"

"github.com/hashicorp/go-version"

"golang.org/x/net/publicsuffix"
)

Expand Down
5 changes: 4 additions & 1 deletion keycloak/role.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ type Role struct {
ClientRole bool `json:"clientRole"`
ContainerId string `json:"containerId"`
Composite bool `json:"composite"`
//extra attributes of a role
Attributes map[string][]string `json:"attributes"`
}

type UsersInRole struct {
Expand Down Expand Up @@ -58,7 +60,8 @@ func (keycloakClient *KeycloakClient) CreateRole(role *Role) error {

role.Id = createdRole.Id

return nil
// seems like role attributes aren't respected on create, so a following update is needed
return keycloakClient.UpdateRole(role)
}

func (keycloakClient *KeycloakClient) GetRealmRoles(realmId string) ([]*Role, error) {
Expand Down
10 changes: 10 additions & 0 deletions provider/data_source_keycloak_role.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,16 @@ func dataSourceKeycloakRole() *schema.Resource {
Type: schema.TypeString,
Computed: true,
},
"composite_roles": {
Type: schema.TypeSet,
Elem: &schema.Schema{Type: schema.TypeString},
Set: schema.HashString,
Computed: true,
},
"attributes": {
Type: schema.TypeMap,
Computed: true,
},
},
}
}
Expand Down
25 changes: 21 additions & 4 deletions provider/resource_keycloak_role.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ package provider

import (
"fmt"
"strings"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/mrparkers/terraform-provider-keycloak/keycloak"
"strings"
)

func resourceKeycloakRole() *schema.Resource {
Expand Down Expand Up @@ -43,29 +44,47 @@ func resourceKeycloakRole() *schema.Resource {
Set: schema.HashString,
Optional: true,
},
// misc attributes
"attributes": {
Type: schema.TypeMap,
Optional: true,
},
},
}
}

func mapFromDataToRole(data *schema.ResourceData) *keycloak.Role {
attributes := map[string][]string{}
if v, ok := data.GetOk("attributes"); ok {
for key, value := range v.(map[string]interface{}) {
attributes[key] = splitLen(value.(string), MAX_ATTRIBUTE_VALUE_LEN)
}
}

role := &keycloak.Role{
Id: data.Id(),
RealmId: data.Get("realm_id").(string),
ClientId: data.Get("client_id").(string),
Name: data.Get("name").(string),
Description: data.Get("description").(string),
Attributes: attributes,
}

return role
}

func mapFromRoleToData(data *schema.ResourceData, role *keycloak.Role) {
attributes := map[string]string{}
for k, v := range role.Attributes {
attributes[k] = strings.Join(v, "")
}
data.SetId(role.Id)

data.Set("realm_id", role.RealmId)
data.Set("client_id", role.ClientId)
data.Set("name", role.Name)
data.Set("description", role.Description)
data.Set("attributes", attributes)
}

func resourceKeycloakRoleCreate(data *schema.ResourceData, meta interface{}) error {
Expand Down Expand Up @@ -206,9 +225,7 @@ func resourceKeycloakRoleUpdate(data *schema.ResourceData, meta interface{}) err
}
}

mapFromRoleToData(data, role)

return nil
return resourceKeycloakRoleRead(data, meta)
}

func resourceKeycloakRoleDelete(data *schema.ResourceData, meta interface{}) error {
Expand Down
59 changes: 59 additions & 0 deletions provider/resource_keycloak_role_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,34 @@ func TestAccKeycloakRole_composites(t *testing.T) {
})
}

func TestAccKeycloakRole_basicWithAttributes(t *testing.T) {
t.Parallel()
roleName := acctest.RandomWithPrefix("tf-acc")
attributeName := acctest.RandomWithPrefix("tf-acc")
attributeValue := acctest.RandomWithPrefix("tf-acc")

resource.Test(t, resource.TestCase{
ProviderFactories: testAccProviderFactories,
PreCheck: func() { testAccPreCheck(t) },
CheckDestroy: testAccCheckKeycloakRoleDestroy(),
Steps: []resource.TestStep{
{
Config: testKeycloakRole_basicWithAttributes(roleName, attributeName, attributeValue),
Check: resource.ComposeTestCheckFunc(
testAccCheckKeycloakRoleExists("keycloak_role.role"),
testAccCheckKeycloakRoleHasAttribute("keycloak_role.role", attributeName, attributeValue),
),
},
{
ResourceName: "keycloak_role.role",
ImportState: true,
ImportStateVerify: true,
ImportStateIdPrefix: testAccRealm.Realm + "/",
},
},
})
}

func testAccCheckKeycloakRoleExists(resourceName string) resource.TestCheckFunc {
return func(s *terraform.State) error {
_, err := getRoleFromState(s, resourceName)
Expand Down Expand Up @@ -310,6 +338,21 @@ func testAccCheckKeycloakRoleFetch(resourceName string, role *keycloak.Role) res
}
}

func testAccCheckKeycloakRoleHasAttribute(resourceName, attributeName, attributeValue string) resource.TestCheckFunc {
return func(state *terraform.State) error {
role, err := getRoleFromState(state, resourceName)
if err != nil {
return err
}

if len(role.Attributes) != 1 || role.Attributes[attributeName][0] != attributeValue {
return fmt.Errorf("expected role %s to have attribute %s with value %s", role.Name, attributeName, attributeValue)
}

return nil
}
}

func testAccCheckKeycloakRoleHasComposites(resourceName string, compositeRoleNames []string) resource.TestCheckFunc {
return func(state *terraform.State) error {
role, err := getRoleFromState(state, resourceName)
Expand Down Expand Up @@ -519,3 +562,19 @@ resource "keycloak_role" "role_with_composites" {
}
`, testAccRealm.Realm, clientOne, clientTwo, roleOne, roleTwo, roleThree, roleFour, roleWithComposites, tfComposites)
}

func testKeycloakRole_basicWithAttributes(role, attributeName, attributeValue string) string {
return fmt.Sprintf(`
data "keycloak_realm" "realm" {
realm = "%s"
}
resource "keycloak_role" "role" {
name = "%s"
realm_id = data.keycloak_realm.realm.id
attributes = {
"%s" = "%s"
}
}
`, testAccRealm.Realm, role, attributeName, attributeValue)
}

0 comments on commit 915b23f

Please sign in to comment.