forked from keycloak/terraform-provider-keycloak
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
new resource: keycloak_openid_script_protocol_mapper (keycloak#453)
- Loading branch information
1 parent
30ec9b4
commit 35793cd
Showing
5 changed files
with
729 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
package keycloak | ||
|
||
import ( | ||
"fmt" | ||
"strconv" | ||
) | ||
|
||
type OpenIdScriptProtocolMapper struct { | ||
Id string | ||
Name string | ||
RealmId string | ||
ClientId string | ||
ClientScopeId string | ||
|
||
AddToIdToken bool | ||
AddToAccessToken bool | ||
AddToUserInfo bool | ||
|
||
Script string | ||
ClaimName string | ||
ClaimValueType string | ||
|
||
Multivalued bool // indicates whether is this an array of attributes or a single attribute | ||
} | ||
|
||
func (mapper *OpenIdScriptProtocolMapper) convertToGenericProtocolMapper() *protocolMapper { | ||
return &protocolMapper{ | ||
Id: mapper.Id, | ||
Name: mapper.Name, | ||
Protocol: "openid-connect", | ||
ProtocolMapper: "oidc-script-based-protocol-mapper", | ||
Config: map[string]string{ | ||
addToIdTokenField: strconv.FormatBool(mapper.AddToIdToken), | ||
addToAccessTokenField: strconv.FormatBool(mapper.AddToAccessToken), | ||
addToUserInfoField: strconv.FormatBool(mapper.AddToUserInfo), | ||
scriptField: mapper.Script, | ||
claimNameField: mapper.ClaimName, | ||
claimValueTypeField: mapper.ClaimValueType, | ||
multivaluedField: strconv.FormatBool(mapper.Multivalued), | ||
}, | ||
} | ||
} | ||
|
||
func (protocolMapper *protocolMapper) convertToOpenIdScriptProtocolMapper(realmId, clientId, clientScopeId string) (*OpenIdScriptProtocolMapper, error) { | ||
addToIdToken, err := strconv.ParseBool(protocolMapper.Config[addToIdTokenField]) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
addToAccessToken, err := strconv.ParseBool(protocolMapper.Config[addToAccessTokenField]) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
addToUserInfo, err := strconv.ParseBool(protocolMapper.Config[addToUserInfoField]) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
// multivalued's default is "", this is an issue when importing an existing mapper | ||
multivalued, err := parseBoolAndTreatEmptyStringAsFalse(protocolMapper.Config[multivaluedField]) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return &OpenIdScriptProtocolMapper{ | ||
Id: protocolMapper.Id, | ||
Name: protocolMapper.Name, | ||
RealmId: realmId, | ||
ClientId: clientId, | ||
ClientScopeId: clientScopeId, | ||
|
||
AddToIdToken: addToIdToken, | ||
AddToAccessToken: addToAccessToken, | ||
AddToUserInfo: addToUserInfo, | ||
|
||
Script: protocolMapper.Config[scriptField], | ||
ClaimName: protocolMapper.Config[claimNameField], | ||
ClaimValueType: protocolMapper.Config[claimValueTypeField], | ||
Multivalued: multivalued, | ||
}, nil | ||
} | ||
|
||
func (keycloakClient *KeycloakClient) GetOpenIdScriptProtocolMapper(realmId, clientId, clientScopeId, mapperId string) (*OpenIdScriptProtocolMapper, error) { | ||
var protocolMapper *protocolMapper | ||
|
||
err := keycloakClient.get(individualProtocolMapperPath(realmId, clientId, clientScopeId, mapperId), &protocolMapper, nil) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return protocolMapper.convertToOpenIdScriptProtocolMapper(realmId, clientId, clientScopeId) | ||
} | ||
|
||
func (keycloakClient *KeycloakClient) DeleteOpenIdScriptProtocolMapper(realmId, clientId, clientScopeId, mapperId string) error { | ||
return keycloakClient.delete(individualProtocolMapperPath(realmId, clientId, clientScopeId, mapperId), nil) | ||
} | ||
|
||
func (keycloakClient *KeycloakClient) NewOpenIdScriptProtocolMapper(mapper *OpenIdScriptProtocolMapper) 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) UpdateOpenIdScriptProtocolMapper(mapper *OpenIdScriptProtocolMapper) error { | ||
path := individualProtocolMapperPath(mapper.RealmId, mapper.ClientId, mapper.ClientScopeId, mapper.Id) | ||
|
||
return keycloakClient.put(path, mapper.convertToGenericProtocolMapper()) | ||
} | ||
|
||
func (keycloakClient *KeycloakClient) ValidateOpenIdScriptProtocolMapper(mapper *OpenIdScriptProtocolMapper) 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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
193 changes: 193 additions & 0 deletions
193
provider/resource_keycloak_openid_script_protocol_mapper.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,193 @@ | ||
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 resourceKeycloakOpenIdScriptProtocolMapper() *schema.Resource { | ||
return &schema.Resource{ | ||
Create: resourceKeycloakOpenIdScriptProtocolMapperCreate, | ||
Read: resourceKeycloakOpenIdScriptProtocolMapperRead, | ||
Update: resourceKeycloakOpenIdScriptProtocolMapperUpdate, | ||
Delete: resourceKeycloakOpenIdScriptProtocolMapperDelete, | ||
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, | ||
Description: "A human-friendly name that will appear in the Keycloak console.", | ||
}, | ||
"realm_id": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: true, | ||
Description: "The realm id where the associated client or client scope exists.", | ||
}, | ||
"client_id": { | ||
Type: schema.TypeString, | ||
Optional: true, | ||
ForceNew: true, | ||
Description: "The mapper's associated client. Cannot be used at the same time as client_scope_id.", | ||
ConflictsWith: []string{"client_scope_id"}, | ||
}, | ||
"client_scope_id": { | ||
Type: schema.TypeString, | ||
Optional: true, | ||
ForceNew: true, | ||
Description: "The mapper's associated client scope. Cannot be used at the same time as client_id.", | ||
ConflictsWith: []string{"client_id"}, | ||
}, | ||
"add_to_id_token": { | ||
Type: schema.TypeBool, | ||
Optional: true, | ||
Default: true, | ||
Description: "Indicates if the attribute should be a claim in the id token.", | ||
}, | ||
"add_to_access_token": { | ||
Type: schema.TypeBool, | ||
Optional: true, | ||
Default: true, | ||
Description: "Indicates if the attribute should be a claim in the access token.", | ||
}, | ||
"add_to_userinfo": { | ||
Type: schema.TypeBool, | ||
Optional: true, | ||
Default: true, | ||
Description: "Indicates if the attribute should appear in the userinfo response body.", | ||
}, | ||
"multivalued": { | ||
Type: schema.TypeBool, | ||
Optional: true, | ||
Default: false, | ||
Description: "Indicates whether this attribute is a single value or an array of values.", | ||
}, | ||
"script": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
Description: "JavaScript used to attach a value to a claim for OIDC tokens.", | ||
}, | ||
"claim_name": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
}, | ||
"claim_value_type": { | ||
Type: schema.TypeString, | ||
Optional: true, | ||
Description: "Claim type used when serializing tokens.", | ||
Default: "String", | ||
ValidateFunc: validation.StringInSlice([]string{"JSON", "String", "long", "int", "boolean"}, true), | ||
}, | ||
}, | ||
} | ||
} | ||
|
||
func mapFromDataToOpenIdScriptProtocolMapper(data *schema.ResourceData) *keycloak.OpenIdScriptProtocolMapper { | ||
return &keycloak.OpenIdScriptProtocolMapper{ | ||
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), | ||
AddToIdToken: data.Get("add_to_id_token").(bool), | ||
AddToAccessToken: data.Get("add_to_access_token").(bool), | ||
AddToUserInfo: data.Get("add_to_userinfo").(bool), | ||
|
||
Script: data.Get("script").(string), | ||
ClaimName: data.Get("claim_name").(string), | ||
ClaimValueType: data.Get("claim_value_type").(string), | ||
Multivalued: data.Get("multivalued").(bool), | ||
} | ||
} | ||
|
||
func mapFromOpenIdScriptMapperToData(mapper *keycloak.OpenIdScriptProtocolMapper, 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("add_to_id_token", mapper.AddToIdToken) | ||
data.Set("add_to_access_token", mapper.AddToAccessToken) | ||
data.Set("add_to_userinfo", mapper.AddToUserInfo) | ||
data.Set("script", mapper.Script) | ||
data.Set("claim_name", mapper.ClaimName) | ||
data.Set("claim_value_type", mapper.ClaimValueType) | ||
data.Set("multivalued", mapper.Multivalued) | ||
} | ||
|
||
func resourceKeycloakOpenIdScriptProtocolMapperCreate(data *schema.ResourceData, meta interface{}) error { | ||
keycloakClient := meta.(*keycloak.KeycloakClient) | ||
|
||
openIdScriptMapper := mapFromDataToOpenIdScriptProtocolMapper(data) | ||
|
||
err := keycloakClient.ValidateOpenIdScriptProtocolMapper(openIdScriptMapper) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
err = keycloakClient.NewOpenIdScriptProtocolMapper(openIdScriptMapper) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
mapFromOpenIdScriptMapperToData(openIdScriptMapper, data) | ||
|
||
return resourceKeycloakOpenIdScriptProtocolMapperRead(data, meta) | ||
} | ||
|
||
func resourceKeycloakOpenIdScriptProtocolMapperRead(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) | ||
|
||
openIdScriptMapper, err := keycloakClient.GetOpenIdScriptProtocolMapper(realmId, clientId, clientScopeId, data.Id()) | ||
if err != nil { | ||
return handleNotFoundError(err, data) | ||
} | ||
|
||
mapFromOpenIdScriptMapperToData(openIdScriptMapper, data) | ||
|
||
return nil | ||
} | ||
|
||
func resourceKeycloakOpenIdScriptProtocolMapperUpdate(data *schema.ResourceData, meta interface{}) error { | ||
keycloakClient := meta.(*keycloak.KeycloakClient) | ||
|
||
openIdScriptMapper := mapFromDataToOpenIdScriptProtocolMapper(data) | ||
|
||
err := keycloakClient.ValidateOpenIdScriptProtocolMapper(openIdScriptMapper) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
err = keycloakClient.UpdateOpenIdScriptProtocolMapper(openIdScriptMapper) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
return resourceKeycloakOpenIdScriptProtocolMapperRead(data, meta) | ||
} | ||
|
||
func resourceKeycloakOpenIdScriptProtocolMapperDelete(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.DeleteOpenIdScriptProtocolMapper(realmId, clientId, clientScopeId, data.Id()) | ||
} |
Oops, something went wrong.