-
Notifications
You must be signed in to change notification settings - Fork 330
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_saml_script_protocol_mapper (#473)
- Loading branch information
Showing
5 changed files
with
646 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
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,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 | ||
} |
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
168 changes: 168 additions & 0 deletions
168
provider/resource_keycloak_saml_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,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()) | ||
} |
Oops, something went wrong.