diff --git a/keycloak/protocol_mapper.go b/keycloak/protocol_mapper.go index 848e9daca..56495f1c0 100644 --- a/keycloak/protocol_mapper.go +++ b/keycloak/protocol_mapper.go @@ -25,7 +25,9 @@ var ( includedClientAudienceField = "included.client.audience" includedCustomAudienceField = "included.custom.audience" multivaluedField = "multivalued" + samlScriptField = "Script" // needs to start with uppercase S for SAML script mapper scriptField = "script" + singleValueAttributeField = "single" userAttributeField = "user.attribute" userPropertyField = "user.attribute" userRealmRoleMappingRolePrefixField = "usermodel.realmRoleMapping.rolePrefix" diff --git a/keycloak/saml_script_protocol_mapper.go b/keycloak/saml_script_protocol_mapper.go new file mode 100644 index 000000000..ed7e1fd04 --- /dev/null +++ b/keycloak/saml_script_protocol_mapper.go @@ -0,0 +1,112 @@ +package keycloak + +import ( + "fmt" + "strconv" +) + +type SamlScriptProtocolMapper struct { + Id string + Name string + RealmId string + ClientId string + ClientScopeId string + + SingleValueAttribute bool + + SamlScript string + FriendlyName string + SamlAttributeName string + SamlAttributeNameFormat string +} + +func (mapper *SamlScriptProtocolMapper) convertToGenericProtocolMapper() *protocolMapper { + return &protocolMapper{ + Id: mapper.Id, + Name: mapper.Name, + Protocol: "saml", + ProtocolMapper: "saml-javascript-mapper", + Config: map[string]string{ + attributeNameField: mapper.SamlAttributeName, + attributeNameFormatField: mapper.SamlAttributeNameFormat, + friendlyNameField: mapper.FriendlyName, + samlScriptField: mapper.SamlScript, + singleValueAttributeField: strconv.FormatBool(mapper.SingleValueAttribute), + }, + } +} + +func (protocolMapper *protocolMapper) convertToSamlScriptProtocolMapper(realmId, clientId, clientScopeId string) (*SamlScriptProtocolMapper, error) { + singleValueAttribute, err := strconv.ParseBool(protocolMapper.Config[singleValueAttributeField]) + if err != nil { + return nil, err + } + + return &SamlScriptProtocolMapper{ + Id: protocolMapper.Id, + Name: protocolMapper.Name, + RealmId: realmId, + ClientId: clientId, + ClientScopeId: clientScopeId, + + SingleValueAttribute: singleValueAttribute, + + SamlScript: protocolMapper.Config[samlScriptField], + FriendlyName: protocolMapper.Config[friendlyNameField], + SamlAttributeName: protocolMapper.Config[attributeNameField], + SamlAttributeNameFormat: protocolMapper.Config[attributeNameFormatField], + }, nil +} + +func (keycloakClient *KeycloakClient) GetSamlScriptProtocolMapper(realmId, clientId, clientScopeId, mapperId string) (*SamlScriptProtocolMapper, error) { + var protocolMapper *protocolMapper + + err := keycloakClient.get(individualProtocolMapperPath(realmId, clientId, clientScopeId, mapperId), &protocolMapper, nil) + if err != nil { + return nil, err + } + + return protocolMapper.convertToSamlScriptProtocolMapper(realmId, clientId, clientScopeId) +} + +func (keycloakClient *KeycloakClient) DeleteSamlScriptProtocolMapper(realmId, clientId, clientScopeId, mapperId string) error { + return keycloakClient.delete(individualProtocolMapperPath(realmId, clientId, clientScopeId, mapperId), nil) +} + +func (keycloakClient *KeycloakClient) NewSamlScriptProtocolMapper(mapper *SamlScriptProtocolMapper) error { + path := protocolMapperPath(mapper.RealmId, mapper.ClientId, mapper.ClientScopeId) + + _, location, err := keycloakClient.post(path, mapper.convertToGenericProtocolMapper()) + if err != nil { + return err + } + + mapper.Id = getIdFromLocationHeader(location) + + return nil +} + +func (keycloakClient *KeycloakClient) UpdateSamlScriptProtocolMapper(mapper *SamlScriptProtocolMapper) error { + path := individualProtocolMapperPath(mapper.RealmId, mapper.ClientId, mapper.ClientScopeId, mapper.Id) + + return keycloakClient.put(path, mapper.convertToGenericProtocolMapper()) +} + +func (keycloakClient *KeycloakClient) ValidateSamlScriptProtocolMapper(mapper *SamlScriptProtocolMapper) error { + if mapper.ClientId == "" && mapper.ClientScopeId == "" { + return fmt.Errorf("validation error: one of ClientId or ClientScopeId must be set") + } + + protocolMappers, err := keycloakClient.listGenericProtocolMappers(mapper.RealmId, mapper.ClientId, mapper.ClientScopeId) + if err != nil { + return err + } + + for _, protocolMapper := range protocolMappers { + if protocolMapper.Name == mapper.Name && protocolMapper.Id != mapper.Id { + return fmt.Errorf("validation error: a protocol mapper with name %s already exists for this client", mapper.Name) + } + } + + return nil +} diff --git a/provider/provider.go b/provider/provider.go index 3780f6638..74d8b3f0a 100644 --- a/provider/provider.go +++ b/provider/provider.go @@ -67,6 +67,7 @@ func KeycloakProvider(client *keycloak.KeycloakClient) *schema.Provider { "keycloak_generic_client_role_mapper": resourceKeycloakGenericClientRoleMapper(), "keycloak_saml_user_attribute_protocol_mapper": resourceKeycloakSamlUserAttributeProtocolMapper(), "keycloak_saml_user_property_protocol_mapper": resourceKeycloakSamlUserPropertyProtocolMapper(), + "keycloak_saml_script_protocol_mapper": resourceKeycloakSamlScriptProtocolMapper(), "keycloak_hardcoded_attribute_identity_provider_mapper": resourceKeycloakHardcodedAttributeIdentityProviderMapper(), "keycloak_hardcoded_role_identity_provider_mapper": resourceKeycloakHardcodedRoleIdentityProviderMapper(), "keycloak_attribute_importer_identity_provider_mapper": resourceKeycloakAttributeImporterIdentityProviderMapper(), diff --git a/provider/resource_keycloak_saml_script_protocol_mapper.go b/provider/resource_keycloak_saml_script_protocol_mapper.go new file mode 100644 index 000000000..2e6340777 --- /dev/null +++ b/provider/resource_keycloak_saml_script_protocol_mapper.go @@ -0,0 +1,168 @@ +package provider + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + "github.com/mrparkers/terraform-provider-keycloak/keycloak" +) + +func resourceKeycloakSamlScriptProtocolMapper() *schema.Resource { + return &schema.Resource{ + Create: resourceKeycloakSamlScriptProtocolMapperCreate, + Read: resourceKeycloakSamlScriptProtocolMapperRead, + Update: resourceKeycloakSamlScriptProtocolMapperUpdate, + Delete: resourceKeycloakSamlScriptProtocolMapperDelete, + Importer: &schema.ResourceImporter{ + // import a mapper tied to a client: + // {{realmId}}/client/{{clientId}}/{{protocolMapperId}} + // or a client scope: + // {{realmId}}/client-scope/{{clientScopeId}}/{{protocolMapperId}} + State: genericProtocolMapperImport, + }, + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + }, + "realm_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "client_id": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ConflictsWith: []string{"client_scope_id"}, + }, + "client_scope_id": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ConflictsWith: []string{"client_id"}, + }, + "single_value_attribute": { + Type: schema.TypeBool, + Optional: true, + Default: true, + }, + "script": { + Type: schema.TypeString, + Required: true, + }, + "friendly_name": { + Type: schema.TypeString, + Optional: true, + }, + "saml_attribute_name": { + Type: schema.TypeString, + Required: true, + }, + "saml_attribute_name_format": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice(keycloakSamlUserAttributeProtocolMapperNameFormats, false), + }, + }, + } +} + +func mapFromDataToSamlScriptProtocolMapper(data *schema.ResourceData) *keycloak.SamlScriptProtocolMapper { + return &keycloak.SamlScriptProtocolMapper{ + Id: data.Id(), + Name: data.Get("name").(string), + RealmId: data.Get("realm_id").(string), + ClientId: data.Get("client_id").(string), + ClientScopeId: data.Get("client_scope_id").(string), + + SingleValueAttribute: data.Get("single_value_attribute").(bool), + + SamlScript: data.Get("script").(string), + FriendlyName: data.Get("friendly_name").(string), + SamlAttributeName: data.Get("saml_attribute_name").(string), + SamlAttributeNameFormat: data.Get("saml_attribute_name_format").(string), + } +} + +func mapFromSamlScriptMapperToData(mapper *keycloak.SamlScriptProtocolMapper, data *schema.ResourceData) { + data.SetId(mapper.Id) + data.Set("name", mapper.Name) + data.Set("realm_id", mapper.RealmId) + + if mapper.ClientId != "" { + data.Set("client_id", mapper.ClientId) + } else { + data.Set("client_scope_id", mapper.ClientScopeId) + } + + data.Set("single_value_attribute", mapper.SingleValueAttribute) + data.Set("script", mapper.SamlScript) + data.Set("friendly_name", mapper.FriendlyName) + data.Set("saml_attribute_name", mapper.SamlAttributeName) + data.Set("saml_attribute_name_format", mapper.SamlAttributeNameFormat) +} + +func resourceKeycloakSamlScriptProtocolMapperCreate(data *schema.ResourceData, meta interface{}) error { + keycloakClient := meta.(*keycloak.KeycloakClient) + + samlScriptMapper := mapFromDataToSamlScriptProtocolMapper(data) + + err := keycloakClient.ValidateSamlScriptProtocolMapper(samlScriptMapper) + if err != nil { + return err + } + + err = keycloakClient.NewSamlScriptProtocolMapper(samlScriptMapper) + if err != nil { + return err + } + + mapFromSamlScriptMapperToData(samlScriptMapper, data) + + return resourceKeycloakSamlScriptProtocolMapperRead(data, meta) +} + +func resourceKeycloakSamlScriptProtocolMapperRead(data *schema.ResourceData, meta interface{}) error { + keycloakClient := meta.(*keycloak.KeycloakClient) + + realmId := data.Get("realm_id").(string) + clientId := data.Get("client_id").(string) + clientScopeId := data.Get("client_scope_id").(string) + + samlScriptMapper, err := keycloakClient.GetSamlScriptProtocolMapper(realmId, clientId, clientScopeId, data.Id()) + if err != nil { + return handleNotFoundError(err, data) + } + + mapFromSamlScriptMapperToData(samlScriptMapper, data) + + return nil +} + +func resourceKeycloakSamlScriptProtocolMapperUpdate(data *schema.ResourceData, meta interface{}) error { + keycloakClient := meta.(*keycloak.KeycloakClient) + + samlScriptMapper := mapFromDataToSamlScriptProtocolMapper(data) + + err := keycloakClient.ValidateSamlScriptProtocolMapper(samlScriptMapper) + if err != nil { + return err + } + + err = keycloakClient.UpdateSamlScriptProtocolMapper(samlScriptMapper) + if err != nil { + return err + } + + return resourceKeycloakSamlScriptProtocolMapperRead(data, meta) +} + +func resourceKeycloakSamlScriptProtocolMapperDelete(data *schema.ResourceData, meta interface{}) error { + keycloakClient := meta.(*keycloak.KeycloakClient) + + realmId := data.Get("realm_id").(string) + clientId := data.Get("client_id").(string) + clientScopeId := data.Get("client_scope_id").(string) + + return keycloakClient.DeleteSamlScriptProtocolMapper(realmId, clientId, clientScopeId, data.Id()) +} diff --git a/provider/resource_keycloak_saml_script_protocol_mapper_test.go b/provider/resource_keycloak_saml_script_protocol_mapper_test.go new file mode 100644 index 000000000..649c79455 --- /dev/null +++ b/provider/resource_keycloak_saml_script_protocol_mapper_test.go @@ -0,0 +1,363 @@ +package provider + +import ( + "fmt" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/mrparkers/terraform-provider-keycloak/keycloak" + "testing" +) + +func TestAccKeycloakSamlScriptProtocolMapper_basicClient(t *testing.T) { + t.Parallel() + + clientId := acctest.RandomWithPrefix("tf-acc") + mapperName := acctest.RandomWithPrefix("tf-acc") + + resourceName := "keycloak_saml_script_protocol_mapper.script_mapper_client" + + resource.Test(t, resource.TestCase{ + ProviderFactories: testAccProviderFactories, + PreCheck: func() { testAccPreCheck(t) }, + CheckDestroy: testAccKeycloakSamlScriptProtocolMapperDestroy(), + Steps: []resource.TestStep{ + { + Config: testKeycloakSamlScriptProtocolMapper_basic_client(clientId, mapperName), + Check: testKeycloakSamlScriptProtocolMapperExists(resourceName), + }, + }, + }) +} + +func TestAccKeycloakSamlScriptProtocolMapper_basicClientScope(t *testing.T) { + t.Parallel() + + clientScopeId := acctest.RandomWithPrefix("tf-acc") + mapperName := acctest.RandomWithPrefix("tf-acc") + + resourceName := "keycloak_saml_script_protocol_mapper.script_mapper_client_scope" + + resource.Test(t, resource.TestCase{ + ProviderFactories: testAccProviderFactories, + PreCheck: func() { testAccPreCheck(t) }, + CheckDestroy: testAccKeycloakSamlScriptProtocolMapperDestroy(), + Steps: []resource.TestStep{ + { + Config: testKeycloakSamlScriptProtocolMapper_basic_clientScope(clientScopeId, mapperName), + Check: testKeycloakSamlScriptProtocolMapperExists(resourceName), + }, + }, + }) +} + +func TestAccKeycloakSamlScriptProtocolMapper_import(t *testing.T) { + t.Parallel() + + clientId := acctest.RandomWithPrefix("tf-acc") + clientScopeId := acctest.RandomWithPrefix("tf-acc") + mapperName := acctest.RandomWithPrefix("tf-acc") + + clientResourceName := "keycloak_saml_script_protocol_mapper.script_mapper_client" + clientScopeResourceName := "keycloak_saml_script_protocol_mapper.script_mapper_client_scope" + + resource.Test(t, resource.TestCase{ + ProviderFactories: testAccProviderFactories, + PreCheck: func() { testAccPreCheck(t) }, + CheckDestroy: testAccKeycloakSamlScriptProtocolMapperDestroy(), + Steps: []resource.TestStep{ + { + Config: testKeycloakSamlScriptProtocolMapper_import(clientId, clientScopeId, mapperName), + Check: resource.ComposeTestCheckFunc( + testKeycloakSamlScriptProtocolMapperExists(clientResourceName), + testKeycloakSamlScriptProtocolMapperExists(clientScopeResourceName), + ), + }, + { + ResourceName: clientResourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateIdFunc: getGenericProtocolMapperIdForClient(clientResourceName), + }, + { + ResourceName: clientScopeResourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateIdFunc: getGenericProtocolMapperIdForClientScope(clientScopeResourceName), + }, + }, + }) +} + +func TestAccKeycloakSamlScriptProtocolMapper_update(t *testing.T) { + t.Parallel() + + clientId := acctest.RandomWithPrefix("tf-acc") + mapperName := acctest.RandomWithPrefix("tf-acc") + + attributeName := acctest.RandomWithPrefix("tf-acc") + updatedAttributeName := acctest.RandomWithPrefix("tf-acc") + resourceName := "keycloak_saml_script_protocol_mapper.script_mapper" + + resource.Test(t, resource.TestCase{ + ProviderFactories: testAccProviderFactories, + PreCheck: func() { testAccPreCheck(t) }, + CheckDestroy: testAccKeycloakSamlScriptProtocolMapperDestroy(), + Steps: []resource.TestStep{ + { + Config: testKeycloakSamlScriptProtocolMapper_claim(clientId, mapperName, attributeName), + Check: testKeycloakSamlScriptProtocolMapperExists(resourceName), + }, + { + Config: testKeycloakSamlScriptProtocolMapper_claim(clientId, mapperName, updatedAttributeName), + Check: testKeycloakSamlScriptProtocolMapperExists(resourceName), + }, + }, + }) +} + +func TestAccKeycloakSamlScriptProtocolMapper_createAfterManualDestroy(t *testing.T) { + t.Parallel() + + var mapper = &keycloak.SamlScriptProtocolMapper{} + + clientId := acctest.RandomWithPrefix("tf-acc") + mapperName := acctest.RandomWithPrefix("tf-acc") + + resourceName := "keycloak_saml_script_protocol_mapper.script_mapper_client" + + resource.Test(t, resource.TestCase{ + ProviderFactories: testAccProviderFactories, + PreCheck: func() { testAccPreCheck(t) }, + CheckDestroy: testAccKeycloakSamlScriptProtocolMapperDestroy(), + Steps: []resource.TestStep{ + { + Config: testKeycloakSamlScriptProtocolMapper_basic_client(clientId, mapperName), + Check: testKeycloakSamlScriptProtocolMapperFetch(resourceName, mapper), + }, + { + PreConfig: func() { + keycloakClient := testAccProvider.Meta().(*keycloak.KeycloakClient) + + err := keycloakClient.DeleteSamlScriptProtocolMapper(mapper.RealmId, mapper.ClientId, mapper.ClientScopeId, mapper.Id) + if err != nil { + t.Error(err) + } + }, + Config: testKeycloakSamlScriptProtocolMapper_basic_client(clientId, mapperName), + Check: testKeycloakSamlScriptProtocolMapperExists(resourceName), + }, + }, + }) +} + +func TestAccKeycloakSamlScriptProtocolMapper_updateClientIdForceNew(t *testing.T) { + t.Parallel() + + clientId := acctest.RandomWithPrefix("tf-acc") + updatedClientId := acctest.RandomWithPrefix("tf-acc") + mapperName := acctest.RandomWithPrefix("tf-acc") + + attributeName := acctest.RandomWithPrefix("tf-acc") + resourceName := "keycloak_saml_script_protocol_mapper.script_mapper" + + resource.Test(t, resource.TestCase{ + ProviderFactories: testAccProviderFactories, + PreCheck: func() { testAccPreCheck(t) }, + CheckDestroy: testAccKeycloakSamlScriptProtocolMapperDestroy(), + Steps: []resource.TestStep{ + { + Config: testKeycloakSamlScriptProtocolMapper_claim(clientId, mapperName, attributeName), + Check: testKeycloakSamlScriptProtocolMapperExists(resourceName), + }, + { + Config: testKeycloakSamlScriptProtocolMapper_claim(updatedClientId, mapperName, attributeName), + Check: testKeycloakSamlScriptProtocolMapperExists(resourceName), + }, + }, + }) +} + +func TestAccKeycloakSamlScriptProtocolMapper_updateClientScopeForceNew(t *testing.T) { + t.Parallel() + + mapperName := acctest.RandomWithPrefix("tf-acc") + clientScopeId := acctest.RandomWithPrefix("tf-acc") + newClientScopeId := acctest.RandomWithPrefix("tf-acc") + resourceName := "keycloak_saml_script_protocol_mapper.script_mapper_client_scope" + + resource.Test(t, resource.TestCase{ + ProviderFactories: testAccProviderFactories, + PreCheck: func() { testAccPreCheck(t) }, + CheckDestroy: testAccKeycloakSamlScriptProtocolMapperDestroy(), + Steps: []resource.TestStep{ + { + Config: testKeycloakSamlScriptProtocolMapper_basic_clientScope(clientScopeId, mapperName), + Check: testKeycloakSamlScriptProtocolMapperExists(resourceName), + }, + { + Config: testKeycloakSamlScriptProtocolMapper_basic_clientScope(newClientScopeId, mapperName), + Check: testKeycloakSamlScriptProtocolMapperExists(resourceName), + }, + }, + }) +} + +func testAccKeycloakSamlScriptProtocolMapperDestroy() resource.TestCheckFunc { + return func(state *terraform.State) error { + for resourceName, rs := range state.RootModule().Resources { + if rs.Type != "keycloak_saml_script_protocol_mapper" { + continue + } + + mapper, _ := getSamlScriptMapperUsingState(state, resourceName) + + if mapper != nil { + return fmt.Errorf("saml script protocol mapper with id %s still exists", rs.Primary.ID) + } + } + + return nil + } +} + +func testKeycloakSamlScriptProtocolMapperExists(resourceName string) resource.TestCheckFunc { + return func(state *terraform.State) error { + _, err := getSamlScriptMapperUsingState(state, resourceName) + if err != nil { + return err + } + + return nil + } +} + +func testKeycloakSamlScriptProtocolMapperFetch(resourceName string, mapper *keycloak.SamlScriptProtocolMapper) resource.TestCheckFunc { + return func(state *terraform.State) error { + fetchedMapper, err := getSamlScriptMapperUsingState(state, resourceName) + if err != nil { + return err + } + + mapper.Id = fetchedMapper.Id + mapper.ClientId = fetchedMapper.ClientId + mapper.ClientScopeId = fetchedMapper.ClientScopeId + mapper.RealmId = fetchedMapper.RealmId + + return nil + } +} + +func getSamlScriptMapperUsingState(state *terraform.State, resourceName string) (*keycloak.SamlScriptProtocolMapper, error) { + rs, ok := state.RootModule().Resources[resourceName] + if !ok { + return nil, fmt.Errorf("resource not found in TF state: %s ", resourceName) + } + + id := rs.Primary.ID + realm := rs.Primary.Attributes["realm_id"] + clientId := rs.Primary.Attributes["client_id"] + clientScopeId := rs.Primary.Attributes["client_scope_id"] + + keycloakClient := testAccProvider.Meta().(*keycloak.KeycloakClient) + + return keycloakClient.GetSamlScriptProtocolMapper(realm, clientId, clientScopeId, id) +} + +func testKeycloakSamlScriptProtocolMapper_basic_client(clientId, mapperName string) string { + return fmt.Sprintf(` +data "keycloak_realm" "realm" { + realm = "%s" +} + +resource "keycloak_saml_client" "saml_client" { + realm_id = data.keycloak_realm.realm.id + client_id = "%s" +} + +resource "keycloak_saml_script_protocol_mapper" "script_mapper_client" { + name = "%s" + realm_id = data.keycloak_realm.realm.id + client_id = keycloak_saml_client.saml_client.id + script = "exports = 'foo';" + saml_attribute_name = "bar" + saml_attribute_name_format = "Unspecified" +}`, testAccRealm.Realm, clientId, mapperName) +} + +func testKeycloakSamlScriptProtocolMapper_basic_clientScope(clientScopeId, mapperName string) string { + return fmt.Sprintf(` +data "keycloak_realm" "realm" { + realm = "%s" +} + +resource "keycloak_saml_client_scope" "client_scope" { + name = "%s" + realm_id = data.keycloak_realm.realm.id +} + +resource "keycloak_saml_script_protocol_mapper" "script_mapper_client_scope" { + name = "%s" + realm_id = data.keycloak_realm.realm.id + client_scope_id = keycloak_saml_client_scope.client_scope.id + script = "exports = 'foo';" + saml_attribute_name = "bar" + saml_attribute_name_format = "Unspecified" +}`, testAccRealm.Realm, clientScopeId, mapperName) +} + +func testKeycloakSamlScriptProtocolMapper_import(clientId, clientScopeId, mapperName string) string { + return fmt.Sprintf(` +data "keycloak_realm" "realm" { + realm = "%s" +} + +resource "keycloak_saml_client" "saml_client" { + realm_id = data.keycloak_realm.realm.id + client_id = "%s" +} + +resource "keycloak_saml_script_protocol_mapper" "script_mapper_client" { + name = "%s" + realm_id = data.keycloak_realm.realm.id + client_id = keycloak_saml_client.saml_client.id + script = "exports = 'foo';" + saml_attribute_name = "bar" + saml_attribute_name_format = "Unspecified" +} + +resource "keycloak_saml_client_scope" "client_scope" { + name = "%s" + realm_id = data.keycloak_realm.realm.id +} + +resource "keycloak_saml_script_protocol_mapper" "script_mapper_client_scope" { + name = "%s" + realm_id = data.keycloak_realm.realm.id + client_scope_id = keycloak_saml_client_scope.client_scope.id + script = "exports = 'foo';" + saml_attribute_name = "bar" + saml_attribute_name_format = "Unspecified" +}`, testAccRealm.Realm, clientId, mapperName, clientScopeId, mapperName) +} + +func testKeycloakSamlScriptProtocolMapper_claim(clientId, mapperName, attributeName string) string { + return fmt.Sprintf(` +data "keycloak_realm" "realm" { + realm = "%s" +} + +resource "keycloak_saml_client" "saml_client" { + realm_id = data.keycloak_realm.realm.id + client_id = "%s" +} + +resource "keycloak_saml_script_protocol_mapper" "script_mapper" { + name = "%s" + realm_id = data.keycloak_realm.realm.id + client_id = keycloak_saml_client.saml_client.id + script = "exports = '%s';" + saml_attribute_name = "bar" + saml_attribute_name_format = "Unspecified" +}`, testAccRealm.Realm, clientId, mapperName, attributeName) +}