Skip to content

Commit

Permalink
new resource: keycloak_ldap_hardcoded_attribute_mapper (#725)
Browse files Browse the repository at this point in the history
  • Loading branch information
Useurmind authored Sep 2, 2022
1 parent 0bd5e25 commit 47ea21e
Show file tree
Hide file tree
Showing 7 changed files with 461 additions and 1 deletion.
65 changes: 65 additions & 0 deletions docs/resources/ldap_hardcoded_attribute_mapper.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
---
page_title: "keycloak_ldap_hardcoded_attribute_mapper Resource"
---

# keycloak_ldap_hardcoded_attribute_mapper Resource

Allows for creating and managing hardcoded attribute mappers for Keycloak users federated via LDAP.

The LDAP hardcoded attribute mapper will set the specified value to the LDAP attribute.

**NOTE**: This mapper is supported just if syncRegistrations is enabled.

## Example Usage (simple string)

```hcl
resource "keycloak_realm" "realm" {
realm = "my-realm"
enabled = true
}
resource "keycloak_ldap_user_federation" "ldap_user_federation" {
name = "openldap"
realm_id = keycloak_realm.realm.id
username_ldap_attribute = "cn"
rdn_ldap_attribute = "cn"
uuid_ldap_attribute = "entryDN"
user_object_classes = [
"simpleSecurityObject",
"organizationalRole"
]
connection_url = "ldap://openldap"
users_dn = "dc=example,dc=org"
bind_dn = "cn=admin,dc=example,dc=org"
bind_credential = "admin"
}
resource "keycloak_ldap_hardcoded_attribute_mapper" "assign_bar_to_foo" {
realm_id = keycloak_realm.realm.id
ldap_user_federation_id = keycloak_ldap_user_federation.ldap_user_federation.id
name = "assign-foo-to-bar"
attribute_name = "foo"
attribute_value = "bar"
}
```

## Argument Reference

- `realm_id` - (Required) The realm that this LDAP mapper will exist in.
- `ldap_user_federation_id` - (Required) The ID of the LDAP user federation provider to attach this mapper to.
- `name` - (Required) Display name of this mapper when displayed in the console.
- `attribute_name` - (Required) The name of the LDAP attribute to set.
- `attribute_value` - (Optional) The value to set to the LDAP attribute. You can hardcode any value like 'foo'.

## Import

LDAP mappers can be imported using the format `{{realm_id}}/{{ldap_user_federation_id}}/{{ldap_mapper_id}}`.
The ID of the LDAP user federation provider and the mapper can be found within the Keycloak GUI, and they are typically GUIDs.

Example:

```bash
$ terraform import keycloak_ldap_hardcoded_attribute_mapper.assign_bar_to_foo my-realm/af2a6ca3-e4d7-49c3-b08b-1b3c70b4b860/3d923ece-1a91-4bf7-adaf-3b82f2a12b67
```
86 changes: 86 additions & 0 deletions keycloak/ldap_hardcoded_attribute_mapper.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package keycloak

import (
"context"
"fmt"
)

type LdapHardcodedAttributeMapper struct {
Id string
Name string
RealmId string
LdapUserFederationId string
AttributeName string
AttributeValue string
}

func convertFromLdapHardcodedAttributeMapperToComponent(ldapMapper *LdapHardcodedAttributeMapper) *component {
return &component{
Id: ldapMapper.Id,
Name: ldapMapper.Name,
ProviderId: "hardcoded-ldap-attribute-mapper",
ProviderType: "org.keycloak.storage.ldap.mappers.LDAPStorageMapper",
ParentId: ldapMapper.LdapUserFederationId,

Config: map[string][]string{
"ldap.attribute.name": {
ldapMapper.AttributeName,
},
"ldap.attribute.value": {
ldapMapper.AttributeValue,
},
},
}
}

func convertFromComponentToLdapHardcodedAttributeMapper(component *component, realmId string) *LdapHardcodedAttributeMapper {
return &LdapHardcodedAttributeMapper{
Id: component.Id,
Name: component.Name,
RealmId: realmId,
LdapUserFederationId: component.ParentId,

AttributeName: component.getConfig("ldap.attribute.name"),
AttributeValue: component.getConfig("ldap.attribute.value"),
}
}

func (keycloakClient *KeycloakClient) ValidateLdapHardcodedAttributeMapper(ldapMapper *LdapHardcodedAttributeMapper) error {
if len(ldapMapper.AttributeName) == 0 {
return fmt.Errorf("validation error: hardcoded attribute name must not be empty")
}
if len(ldapMapper.AttributeValue) == 0 {
return fmt.Errorf("validation error: hardcoded attribute value must not be empty")
}
return nil
}

func (keycloakClient *KeycloakClient) NewLdapHardcodedAttributeMapper(ctx context.Context, ldapMapper *LdapHardcodedAttributeMapper) error {
_, location, err := keycloakClient.post(ctx, fmt.Sprintf("/realms/%s/components", ldapMapper.RealmId), convertFromLdapHardcodedAttributeMapperToComponent(ldapMapper))
if err != nil {
return err
}

ldapMapper.Id = getIdFromLocationHeader(location)

return nil
}

func (keycloakClient *KeycloakClient) GetLdapHardcodedAttributeMapper(ctx context.Context, realmId, id string) (*LdapHardcodedAttributeMapper, error) {
var component *component

err := keycloakClient.get(ctx, fmt.Sprintf("/realms/%s/components/%s", realmId, id), &component, nil)
if err != nil {
return nil, err
}

return convertFromComponentToLdapHardcodedAttributeMapper(component, realmId), nil
}

func (keycloakClient *KeycloakClient) UpdateLdapHardcodedAttributeMapper(ctx context.Context, ldapMapper *LdapHardcodedAttributeMapper) error {
return keycloakClient.put(ctx, fmt.Sprintf("/realms/%s/components/%s", ldapMapper.RealmId, ldapMapper.Id), convertFromLdapHardcodedAttributeMapperToComponent(ldapMapper))
}

func (keycloakClient *KeycloakClient) DeleteLdapHardcodedAttributeMapper(ctx context.Context, realmId, id string) error {
return keycloakClient.delete(ctx, fmt.Sprintf("/realms/%s/components/%s", realmId, id), nil)
}
3 changes: 3 additions & 0 deletions keycloak/ldap_user_federation.go
Original file line number Diff line number Diff line change
Expand Up @@ -493,6 +493,9 @@ func (keycloakClient *KeycloakClient) GetLdapUserFederationMappers(ctx context.C
case "hardcoded-ldap-role-mapper":
mapper := convertFromComponentToLdapHardcodedRoleMapper(component, realmId)
ldapUserFederationMappers = append(ldapUserFederationMappers, mapper)
case "hardcoded-ldap-attribute-mapper":
mapper := convertFromComponentToLdapHardcodedAttributeMapper(component, realmId)
ldapUserFederationMappers = append(ldapUserFederationMappers, mapper)
case "msad-lds-user-account-control-mapper":
mapper, err := convertFromComponentToLdapMsadLdsUserAccountControlMapper(component, realmId)
if err != nil {
Expand Down
1 change: 1 addition & 0 deletions provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ func KeycloakProvider(client *keycloak.KeycloakClient) *schema.Provider {
"keycloak_ldap_group_mapper": resourceKeycloakLdapGroupMapper(),
"keycloak_ldap_role_mapper": resourceKeycloakLdapRoleMapper(),
"keycloak_ldap_hardcoded_role_mapper": resourceKeycloakLdapHardcodedRoleMapper(),
"keycloak_ldap_hardcoded_attribute_mapper": resourceKeycloakLdapHardcodedAttributeMapper(),
"keycloak_ldap_hardcoded_group_mapper": resourceKeycloakLdapHardcodedGroupMapper(),
"keycloak_ldap_msad_user_account_control_mapper": resourceKeycloakLdapMsadUserAccountControlMapper(),
"keycloak_ldap_msad_lds_user_account_control_mapper": resourceKeycloakLdapMsadLdsUserAccountControlMapper(),
Expand Down
140 changes: 140 additions & 0 deletions provider/resource_keycloak_ldap_hardcoded_attribute_mapper.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
package provider

import (
"context"

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

func resourceKeycloakLdapHardcodedAttributeMapper() *schema.Resource {
return &schema.Resource{
CreateContext: resourceKeycloakLdapHardcodedAttributeMapperCreate,
ReadContext: resourceKeycloakLdapHardcodedAttributeMapperRead,
UpdateContext: resourceKeycloakLdapHardcodedAttributeMapperUpdate,
DeleteContext: resourceKeycloakLdapHardcodedAttributeMapperDelete,
// This resource can be imported using {{realm}}/{{provider_id}}/{{mapper_id}}. The Provider and Mapper IDs are displayed in the GUI
Importer: &schema.ResourceImporter{
StateContext: resourceKeycloakLdapGenericMapperImport,
},
Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Required: true,
Description: "Display name of the mapper when displayed in the console.",
},
"realm_id": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
Description: "The realm in which the ldap user federation provider exists.",
},
"ldap_user_federation_id": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
Description: "The ldap user federation provider to attach this mapper to.",
},
"attribute_name": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
Description: "Name of the LDAP attribute",
},
"attribute_value": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
Description: "Value of the LDAP attribute. You can hardcode any value like 'foo'",
},
},
}
}

func getLdapHardcodedAttributeMapperFromData(data *schema.ResourceData) *keycloak.LdapHardcodedAttributeMapper {
return &keycloak.LdapHardcodedAttributeMapper{
Id: data.Id(),
Name: data.Get("name").(string),
RealmId: data.Get("realm_id").(string),
LdapUserFederationId: data.Get("ldap_user_federation_id").(string),
AttributeName: data.Get("attribute_name").(string),
AttributeValue: data.Get("attribute_value").(string),
}
}

func setLdapHardcodedAttributeMapperData(data *schema.ResourceData, ldapMapper *keycloak.LdapHardcodedAttributeMapper) {
data.SetId(ldapMapper.Id)
data.Set("name", ldapMapper.Name)
data.Set("realm_id", ldapMapper.RealmId)
data.Set("ldap_user_federation_id", ldapMapper.LdapUserFederationId)
data.Set("attribute_name", ldapMapper.AttributeName)
data.Set("attribute_value", ldapMapper.AttributeValue)
}

func resourceKeycloakLdapHardcodedAttributeMapperCreate(ctx context.Context, data *schema.ResourceData, meta interface{}) diag.Diagnostics {
keycloakClient := meta.(*keycloak.KeycloakClient)

ldapMapper := getLdapHardcodedAttributeMapperFromData(data)

err := keycloakClient.ValidateLdapHardcodedAttributeMapper(ldapMapper)
if err != nil {
return diag.FromErr(err)
}

err = keycloakClient.NewLdapHardcodedAttributeMapper(ctx, ldapMapper)
if err != nil {
return diag.FromErr(err)
}

setLdapHardcodedAttributeMapperData(data, ldapMapper)

return resourceKeycloakLdapHardcodedAttributeMapperRead(ctx, data, meta)
}

func resourceKeycloakLdapHardcodedAttributeMapperRead(ctx context.Context, data *schema.ResourceData, meta interface{}) diag.Diagnostics {
keycloakClient := meta.(*keycloak.KeycloakClient)

realmId := data.Get("realm_id").(string)
id := data.Id()

ldapMapper, err := keycloakClient.GetLdapHardcodedAttributeMapper(ctx, realmId, id)
if err != nil {
return handleNotFoundError(ctx, err, data)
}

setLdapHardcodedAttributeMapperData(data, ldapMapper)

return diag.FromErr(nil)
}

func resourceKeycloakLdapHardcodedAttributeMapperUpdate(ctx context.Context, data *schema.ResourceData, meta interface{}) diag.Diagnostics {
keycloakClient := meta.(*keycloak.KeycloakClient)

ldapMapper := getLdapHardcodedAttributeMapperFromData(data)

err := keycloakClient.ValidateLdapHardcodedAttributeMapper(ldapMapper)
if err != nil {
return diag.FromErr(err)
}

err = keycloakClient.UpdateLdapHardcodedAttributeMapper(ctx, ldapMapper)
if err != nil {
return diag.FromErr(err)
}

setLdapHardcodedAttributeMapperData(data, ldapMapper)

return diag.FromErr(nil)
}

func resourceKeycloakLdapHardcodedAttributeMapperDelete(ctx context.Context, data *schema.ResourceData, meta interface{}) diag.Diagnostics {
keycloakClient := meta.(*keycloak.KeycloakClient)

realmId := data.Get("realm_id").(string)
id := data.Id()

err := keycloakClient.DeleteLdapHardcodedAttributeMapper(ctx, realmId, id)

return diag.FromErr(err)
}
Loading

0 comments on commit 47ea21e

Please sign in to comment.