diff --git a/docs/resources/keycloak_generic_client_role_mapper.md b/docs/resources/keycloak_generic_client_role_mapper.md index 123bb0299..fc9bd2083 100644 --- a/docs/resources/keycloak_generic_client_role_mapper.md +++ b/docs/resources/keycloak_generic_client_role_mapper.md @@ -7,7 +7,7 @@ the token or assertion. When `full_scope_allowed` is set to `false` for a client, role scope mapping allows you to limit the roles that get declared inside an access token for a client. -### Example Usage (Realm role) +### Example Usage (Realm Role to Client) ```hcl resource "keycloak_realm" "realm" { @@ -38,8 +38,7 @@ resource "keycloak_generic_client_role_mapper" "client_role_mapper" { } ``` -### Example Usage (Client role) - +### Example Usage (Client Role to Client) ```hcl resource "keycloak_realm" "realm" { @@ -88,11 +87,75 @@ resource "keycloak_generic_client_role_mapper" "client_b_role_mapper" { } ``` +### Example Usage (Realm Role to Client Scope) + +```hcl +resource "keycloak_realm" "realm" { + realm = "my-realm" + enabled = true +} + +resource "keycloak_openid_client_scope" "client_scope" { + realm_id = keycloak_realm.realm.id + name = "my-client-scope" +} + +resource "keycloak_role" "realm_role" { + realm_id = keycloak_realm.realm.id + name = "my-realm-role" + description = "My Realm Role" +} + +resource "keycloak_generic_client_role_mapper" "client_role_mapper" { + realm_id = keycloak_realm.realm.id + client_scope_id = keycloak_openid_client_scope.client_scope.id + role_id = keycloak_role.realm_role.id +} +``` + +### Example Usage (Client Role to Client Scope) + +```hcl +resource "keycloak_realm" "realm" { + realm = "my-realm" + enabled = true +} + +resource "keycloak_openid_client" "client" { + realm_id = keycloak_realm.realm.id + client_id = "client" + name = "client" + + enabled = true + + access_type = "BEARER-ONLY" +} + +resource "keycloak_role" "client_role" { + realm_id = keycloak_realm.realm.id + client_id = keycloak_openid_client.client.id + name = "my-client-role" + description = "My Client Role" +} + +resource "keycloak_openid_client_scope" "client_scope" { + realm_id = keycloak_realm.realm.id + name = "my-client-scope" +} + +resource "keycloak_generic_client_role_mapper" "client_b_role_mapper" { + realm_id = keycloak_realm.realm.id + client_scope_id = keycloak_client_scope.client_scope.id + role_id = keycloak_role.client_role.id +} +``` + ### Argument Reference The following arugments are supported: - `realm_id` - (Required) The realm this role mapper exists within -- `client_id` - (Required) The ID of the client this role mapper is created for +- `client_id` - (Optional) The ID of the client this role mapper is added to +- `client_scope_id` - (Optional) The ID of the client scope this role mapper is added to - `role_id` - (Required) The ID of the role to be added to this role mapper diff --git a/provider/resource_keycloak_generic_client_role_mapper.go b/provider/resource_keycloak_generic_client_role_mapper.go index 9b39c8793..cbdc60f66 100644 --- a/provider/resource_keycloak_generic_client_role_mapper.go +++ b/provider/resource_keycloak_generic_client_role_mapper.go @@ -2,6 +2,7 @@ package provider import ( "fmt" + "strings" "github.com/hashicorp/terraform-plugin-sdk/helper/schema" "github.com/mrparkers/terraform-provider-keycloak/keycloak" @@ -12,7 +13,9 @@ func resourceKeycloakGenericClientRoleMapper() *schema.Resource { Create: resourceKeycloakGenericClientRoleMapperCreate, Read: resourceKeycloakGenericClientRoleMapperRead, Delete: resourceKeycloakGenericClientRoleMapperDelete, - + Importer: &schema.ResourceImporter{ + State: resourceKeycloakGenericClientRoleMapperImport, + }, Schema: map[string]*schema.Schema{ "realm_id": { Type: schema.TypeString, @@ -108,3 +111,27 @@ func resourceKeycloakGenericClientRoleMapperDelete(data *schema.ResourceData, me return keycloakClient.DeleteRoleScopeMapping(realmId, clientId, clientScopeId, role) } + +func resourceKeycloakGenericClientRoleMapperImport(d *schema.ResourceData, _ interface{}) ([]*schema.ResourceData, error) { + parts := strings.Split(d.Id(), "/") + + if len(parts) != 6 { + return nil, fmt.Errorf("Invalid import. Supported import formats: {{realmId}}/client/{{clientId}}/scope-mappings/{{roleClientId}}/{{roleId}}, {{realmId}}/client-scope/{{clientScopeId}}/scope-mappings/{{roleClientId}}/{{roleId}}") + } + + parentResourceType := parts[1] + parentResourceId := parts[2] + + d.Set("realm_id", parts[0]) + + if parentResourceType == "client" { + d.Set("client_id", parentResourceId) + } else if parentResourceType == "client-scope" { + d.Set("client_scope_id", parentResourceId) + } else { + return nil, fmt.Errorf("the associated parent resource must be either a client or a client-scope") + } + + d.Set("role_id", parts[5]) + return []*schema.ResourceData{d}, nil +} diff --git a/provider/resource_keycloak_generic_client_role_mapper_test.go b/provider/resource_keycloak_generic_client_role_mapper_test.go index 7983f5ccb..f299bb752 100644 --- a/provider/resource_keycloak_generic_client_role_mapper_test.go +++ b/provider/resource_keycloak_generic_client_role_mapper_test.go @@ -2,11 +2,12 @@ package provider import ( "fmt" + "testing" + "github.com/hashicorp/terraform-plugin-sdk/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/terraform" "github.com/mrparkers/terraform-provider-keycloak/keycloak" - "testing" ) func TestGenericRoleMapper_basic(t *testing.T) { @@ -27,6 +28,32 @@ func TestGenericRoleMapper_basic(t *testing.T) { }) } +func TestGenericRoleMapper_import(t *testing.T) { + realmName := "terraform-" + acctest.RandString(10) + parentClientName := "client1-" + acctest.RandString(10) + parentRoleName := "role-" + acctest.RandString(10) + childClientName := "client2-" + acctest.RandString(10) + + resourceName := "keycloak_generic_client_role_mapper.child-client-with-parent-client-role" + + resource.Test(t, resource.TestCase{ + Providers: testAccProviders, + PreCheck: func() { testAccPreCheck(t) }, + Steps: []resource.TestStep{ + { + Config: testKeycloakGenericRoleMapping_basic(realmName, parentClientName, parentRoleName, childClientName), + Check: testAccCheckKeycloakScopeMappingExists(resourceName), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateIdFunc: getGenericRoleMapperId(resourceName), + }, + }, + }) +} + func TestGenericRoleMapperClientScope_basic(t *testing.T) { realmName := "terraform-" + acctest.RandString(10) clientName := "client-" + acctest.RandString(10) @@ -45,6 +72,32 @@ func TestGenericRoleMapperClientScope_basic(t *testing.T) { }) } +func TestGenericRoleMapperClientScope_import(t *testing.T) { + realmName := "terraform-" + acctest.RandString(10) + clientName := "client-" + acctest.RandString(10) + roleName := "role-" + acctest.RandString(10) + clientScopeName := "clientscope-" + acctest.RandString(10) + + resourceName := "keycloak_generic_client_role_mapper.clientscope-with-client-role" + + resource.Test(t, resource.TestCase{ + Providers: testAccProviders, + PreCheck: func() { testAccPreCheck(t) }, + Steps: []resource.TestStep{ + { + Config: testKeycloakGenericRoleMappingClientScope_basic(realmName, clientName, roleName, clientScopeName), + Check: testAccCheckKeycloakScopeMappingExists(resourceName), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateIdFunc: getGenericRoleMapperId(resourceName), + }, + }, + }) +} + func TestGenericRoleMapper_createAfterManualDestroy(t *testing.T) { var role = &keycloak.Role{} var childClient = &keycloak.GenericClient{} @@ -259,3 +312,14 @@ func getOpenidClientScopeFromState(s *terraform.State, resourceName string) (*ke return client, nil } + +func getGenericRoleMapperId(resourceName string) resource.ImportStateIdFunc { + return func(s *terraform.State) (string, error) { + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return "", fmt.Errorf("resource not found: %s", resourceName) + } + + return rs.Primary.ID, nil + } +}