Skip to content

Commit 0448f48

Browse files
authored
Feature/423 add hardcoded attribute mapper (#950)
* initial feature Signed-off-by: angeloxx <[email protected]> * Tested with success Signed-off-by: angeloxx <[email protected]> * typo in test Signed-off-by: angeloxx <[email protected]> * fix record Signed-off-by: angeloxx <[email protected]> * move to the new org Signed-off-by: angeloxx <[email protected]> --------- Signed-off-by: angeloxx <[email protected]>
1 parent 83ddf6a commit 0448f48

5 files changed

+438
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
---
2+
page_title: "keycloak_hardcoded_attribute_mapper Resource"
3+
---
4+
5+
# keycloak_hardcoded_attribute_mapper Resource
6+
7+
Allows for creating and managing hardcoded attribute mappers for Keycloak users federated via LDAP.
8+
9+
The user model hardcoded attribute mapper will set the specified value to the attribute.
10+
11+
12+
## Example Usage
13+
14+
```hcl
15+
resource "keycloak_realm" "realm" {
16+
realm = "my-realm"
17+
enabled = true
18+
}
19+
20+
resource "keycloak_ldap_user_federation" "ldap_user_federation" {
21+
name = "openldap"
22+
realm_id = keycloak_realm.realm.id
23+
24+
username_ldap_attribute = "cn"
25+
rdn_ldap_attribute = "cn"
26+
uuid_ldap_attribute = "entryDN"
27+
user_object_classes = [
28+
"simpleSecurityObject",
29+
"organizationalRole"
30+
]
31+
32+
connection_url = "ldap://openldap"
33+
users_dn = "dc=example,dc=org"
34+
bind_dn = "cn=admin,dc=example,dc=org"
35+
bind_credential = "admin"
36+
37+
sync_registrations = true
38+
}
39+
40+
resource "keycloak_hardcoded_attribute_mapper" "email_verified" {
41+
realm_id = keycloak_realm.realm.id
42+
ldap_user_federation_id = keycloak_ldap_user_federation.ldap_user_federation.id
43+
name = "email_verified"
44+
attribute_name = "email_verified"
45+
attribute_value = "true"
46+
}
47+
```
48+
49+
## Argument Reference
50+
51+
- `realm_id` - (Required) The realm that this LDAP mapper will exist in.
52+
- `ldap_user_federation_id` - (Required) The ID of the LDAP user federation provider to attach this mapper to.
53+
- `name` - (Required) Display name of this mapper when displayed in the console.
54+
- `attribute_name` - (Required) The name of the user model attribute to set.
55+
- `attribute_value` - (Required) The value to set to model attribute. You can hardcode any value like 'foo'.
56+
57+
## Import
58+
59+
LDAP mappers can be imported using the format `{{realm_id}}/{{ldap_user_federation_id}}/{{attribute__mapper_id}}`.
60+
The ID of the LDAP user federation provider and the mapper can be found within the Keycloak GUI, and they are typically GUIDs.
61+
62+
Example:
63+
64+
```bash
65+
$ terraform import keycloak_hardcoded_attribute_mapper.email_verified my-realm/af2a6ca3-e4d7-49c3-b08b-1b3c70b4b860/3d923ece-1a91-4bf7-adaf-3b82f2a12b67
66+
```
+76
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
package keycloak
2+
3+
import (
4+
"context"
5+
"fmt"
6+
)
7+
8+
type HardcodedAttributeMapper struct {
9+
Id string
10+
Name string
11+
RealmId string
12+
LdapUserFederationId string
13+
AttributeName string
14+
AttributeValue string
15+
}
16+
17+
func convertFromHardcodedAttributeMapperToComponent(hardcodedMapper *HardcodedAttributeMapper) *component {
18+
return &component{
19+
Id: hardcodedMapper.Id,
20+
Name: hardcodedMapper.Name,
21+
ProviderId: "hardcoded-attribute-mapper",
22+
ProviderType: "org.keycloak.storage.ldap.mappers.LDAPStorageMapper",
23+
ParentId: hardcodedMapper.LdapUserFederationId,
24+
25+
Config: map[string][]string{
26+
"user.model.attribute": {
27+
hardcodedMapper.AttributeName,
28+
},
29+
"attribute.value": {
30+
hardcodedMapper.AttributeValue,
31+
},
32+
},
33+
}
34+
}
35+
36+
func convertFromComponentToHardcodedAttributeMapper(component *component, realmId string) *HardcodedAttributeMapper {
37+
return &HardcodedAttributeMapper{
38+
Id: component.Id,
39+
Name: component.Name,
40+
RealmId: realmId,
41+
LdapUserFederationId: component.ParentId,
42+
43+
AttributeName: component.getConfig("user.model.attribute"),
44+
AttributeValue: component.getConfig("attribute.value"),
45+
}
46+
}
47+
48+
func (keycloakClient *KeycloakClient) NewHardcodedAttributeMapper(ctx context.Context, hardcodedMapper *HardcodedAttributeMapper) error {
49+
_, location, err := keycloakClient.post(ctx, fmt.Sprintf("/realms/%s/components", hardcodedMapper.RealmId), convertFromHardcodedAttributeMapperToComponent(hardcodedMapper))
50+
if err != nil {
51+
return err
52+
}
53+
54+
hardcodedMapper.Id = getIdFromLocationHeader(location)
55+
56+
return nil
57+
}
58+
59+
func (keycloakClient *KeycloakClient) GetHardcodedAttributeMapper(ctx context.Context, realmId, id string) (*HardcodedAttributeMapper, error) {
60+
var component *component
61+
62+
err := keycloakClient.get(ctx, fmt.Sprintf("/realms/%s/components/%s", realmId, id), &component, nil)
63+
if err != nil {
64+
return nil, err
65+
}
66+
67+
return convertFromComponentToHardcodedAttributeMapper(component, realmId), nil
68+
}
69+
70+
func (keycloakClient *KeycloakClient) UpdateHardcodedAttributeMapper(ctx context.Context, hardcodedMapper *HardcodedAttributeMapper) error {
71+
return keycloakClient.put(ctx, fmt.Sprintf("/realms/%s/components/%s", hardcodedMapper.RealmId, hardcodedMapper.Id), convertFromHardcodedAttributeMapperToComponent(hardcodedMapper))
72+
}
73+
74+
func (keycloakClient *KeycloakClient) DeleteHardcodedAttributeMapper(ctx context.Context, realmId, id string) error {
75+
return keycloakClient.delete(ctx, fmt.Sprintf("/realms/%s/components/%s", realmId, id), nil)
76+
}

provider/provider.go

+1
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ func KeycloakProvider(client *keycloak.KeycloakClient) *schema.Provider {
5353
"keycloak_openid_client_scope": resourceKeycloakOpenidClientScope(),
5454
"keycloak_ldap_user_federation": resourceKeycloakLdapUserFederation(),
5555
"keycloak_ldap_user_attribute_mapper": resourceKeycloakLdapUserAttributeMapper(),
56+
"keycloak_hardcoded_attribute_mapper": resourceKeycloakHardcodedAttributeMapper(),
5657
"keycloak_ldap_group_mapper": resourceKeycloakLdapGroupMapper(),
5758
"keycloak_ldap_role_mapper": resourceKeycloakLdapRoleMapper(),
5859
"keycloak_ldap_hardcoded_role_mapper": resourceKeycloakLdapHardcodedRoleMapper(),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
package provider
2+
3+
import (
4+
"context"
5+
6+
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
7+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
8+
"github.com/keycloak/terraform-provider-keycloak/keycloak"
9+
)
10+
11+
func resourceKeycloakHardcodedAttributeMapper() *schema.Resource {
12+
return &schema.Resource{
13+
CreateContext: resourceKeycloakHardcodedAttributeMapperCreate,
14+
ReadContext: resourceKeycloakHardcodedAttributeMapperRead,
15+
UpdateContext: resourceKeycloakHardcodedAttributeMapperUpdate,
16+
DeleteContext: resourceKeycloakHardcodedAttributeMapperDelete,
17+
// This resource can be imported using {{realm}}/{{provider_id}}/{{mapper_id}}. The Provider and Mapper IDs are displayed in the GUI
18+
Importer: &schema.ResourceImporter{
19+
StateContext: resourceKeycloakLdapGenericMapperImport,
20+
},
21+
Schema: map[string]*schema.Schema{
22+
"name": {
23+
Type: schema.TypeString,
24+
Required: true,
25+
Description: "Display name of the mapper when displayed in the console.",
26+
},
27+
"realm_id": {
28+
Type: schema.TypeString,
29+
Required: true,
30+
ForceNew: true,
31+
Description: "The realm in which the ldap user federation provider exists.",
32+
},
33+
"ldap_user_federation_id": {
34+
Type: schema.TypeString,
35+
Required: true,
36+
ForceNew: true,
37+
Description: "The ldap user federation provider to attach this mapper to.",
38+
},
39+
"attribute_name": {
40+
Type: schema.TypeString,
41+
Required: true,
42+
ForceNew: true,
43+
Description: "Name of the user schema attribute",
44+
},
45+
"attribute_value": {
46+
Type: schema.TypeString,
47+
Required: true,
48+
ForceNew: true,
49+
Description: "Value of the attribute. You can hardcode any value like 'foo'",
50+
},
51+
},
52+
}
53+
}
54+
55+
func getHardcodedAttributeMapperFromData(data *schema.ResourceData) *keycloak.HardcodedAttributeMapper {
56+
return &keycloak.HardcodedAttributeMapper{
57+
Id: data.Id(),
58+
Name: data.Get("name").(string),
59+
RealmId: data.Get("realm_id").(string),
60+
LdapUserFederationId: data.Get("ldap_user_federation_id").(string),
61+
AttributeName: data.Get("attribute_name").(string),
62+
AttributeValue: data.Get("attribute_value").(string),
63+
}
64+
}
65+
66+
func setHardcodedAttributeMapperData(data *schema.ResourceData, ldapMapper *keycloak.HardcodedAttributeMapper) {
67+
data.SetId(ldapMapper.Id)
68+
data.Set("name", ldapMapper.Name)
69+
data.Set("realm_id", ldapMapper.RealmId)
70+
data.Set("ldap_user_federation_id", ldapMapper.LdapUserFederationId)
71+
data.Set("attribute_name", ldapMapper.AttributeName)
72+
data.Set("attribute_value", ldapMapper.AttributeValue)
73+
}
74+
75+
func resourceKeycloakHardcodedAttributeMapperCreate(ctx context.Context, data *schema.ResourceData, meta interface{}) diag.Diagnostics {
76+
keycloakClient := meta.(*keycloak.KeycloakClient)
77+
78+
ldapMapper := getHardcodedAttributeMapperFromData(data)
79+
80+
err := keycloakClient.NewHardcodedAttributeMapper(ctx, ldapMapper)
81+
if err != nil {
82+
return diag.FromErr(err)
83+
}
84+
85+
setHardcodedAttributeMapperData(data, ldapMapper)
86+
87+
return resourceKeycloakHardcodedAttributeMapperRead(ctx, data, meta)
88+
}
89+
90+
func resourceKeycloakHardcodedAttributeMapperRead(ctx context.Context, data *schema.ResourceData, meta interface{}) diag.Diagnostics {
91+
keycloakClient := meta.(*keycloak.KeycloakClient)
92+
93+
realmId := data.Get("realm_id").(string)
94+
id := data.Id()
95+
96+
ldapMapper, err := keycloakClient.GetHardcodedAttributeMapper(ctx, realmId, id)
97+
if err != nil {
98+
return handleNotFoundError(ctx, err, data)
99+
}
100+
101+
setHardcodedAttributeMapperData(data, ldapMapper)
102+
103+
return diag.FromErr(nil)
104+
}
105+
106+
func resourceKeycloakHardcodedAttributeMapperUpdate(ctx context.Context, data *schema.ResourceData, meta interface{}) diag.Diagnostics {
107+
keycloakClient := meta.(*keycloak.KeycloakClient)
108+
109+
ldapMapper := getHardcodedAttributeMapperFromData(data)
110+
111+
err := keycloakClient.UpdateHardcodedAttributeMapper(ctx, ldapMapper)
112+
if err != nil {
113+
return diag.FromErr(err)
114+
}
115+
116+
setHardcodedAttributeMapperData(data, ldapMapper)
117+
118+
return diag.FromErr(nil)
119+
}
120+
121+
func resourceKeycloakHardcodedAttributeMapperDelete(ctx context.Context, data *schema.ResourceData, meta interface{}) diag.Diagnostics {
122+
keycloakClient := meta.(*keycloak.KeycloakClient)
123+
124+
realmId := data.Get("realm_id").(string)
125+
id := data.Id()
126+
127+
err := keycloakClient.DeleteHardcodedAttributeMapper(ctx, realmId, id)
128+
129+
return diag.FromErr(err)
130+
}

0 commit comments

Comments
 (0)