Skip to content

Commit f8d0b99

Browse files
authored
Merge pull request #163 from SumoLogic/emichaeli-add-saml-configuration-resource
Add SAML configuration resource
2 parents 815d46c + 7e97a91 commit f8d0b99

File tree

6 files changed

+759
-16
lines changed

6 files changed

+759
-16
lines changed

sumologic/provider.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ func Provider() terraform.ResourceProvider {
7171
"sumologic_subdomain": resourceSumologicSubdomain(),
7272
"sumologic_dashboard": resourceSumologicDashboard(),
7373
"sumologic_password_policy": resourceSumologicPasswordPolicy(),
74+
"sumologic_saml_configuration": resourceSumologicSamlConfiguration(),
7475
},
7576
DataSourcesMap: map[string]*schema.Resource{
7677
"sumologic_caller_identity": dataSourceSumologicCallerIdentity(),
Lines changed: 283 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,283 @@
1+
package sumologic
2+
3+
import (
4+
"log"
5+
6+
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
7+
)
8+
9+
func resourceSumologicSamlConfiguration() *schema.Resource {
10+
return &schema.Resource{
11+
Create: resourceSumologicSamlConfigurationCreate,
12+
Read: resourceSumologicSamlConfigurationRead,
13+
Update: resourceSumologicSamlConfigurationUpdate,
14+
Delete: resourceSumologicSamlConfigurationDelete,
15+
Importer: &schema.ResourceImporter{
16+
State: schema.ImportStatePassthrough,
17+
},
18+
19+
Schema: map[string]*schema.Schema{
20+
"sp_initiated_login_path": {
21+
Type: schema.TypeString,
22+
Optional: true,
23+
Default: "",
24+
},
25+
"configuration_name": {
26+
Type: schema.TypeString,
27+
Required: true,
28+
},
29+
"issuer": {
30+
Type: schema.TypeString,
31+
Required: true,
32+
},
33+
"sp_initiated_login_enabled": {
34+
Type: schema.TypeBool,
35+
Optional: true,
36+
Default: false,
37+
},
38+
"authn_request_url": {
39+
Type: schema.TypeString,
40+
Optional: true,
41+
Default: "",
42+
},
43+
"x509cert1": {
44+
Type: schema.TypeString,
45+
Required: true,
46+
},
47+
"x509cert2": {
48+
Type: schema.TypeString,
49+
Optional: true,
50+
Default: "",
51+
},
52+
"x509cert3": {
53+
Type: schema.TypeString,
54+
Optional: true,
55+
Default: "",
56+
},
57+
"on_demand_provisioning_enabled": {
58+
Type: schema.TypeList,
59+
MaxItems: 1,
60+
Optional: true,
61+
Elem: &schema.Resource{
62+
Schema: getOnDemandProvisioningEnabledSchema(),
63+
},
64+
},
65+
"roles_attribute": {
66+
Type: schema.TypeString,
67+
Optional: true,
68+
Default: "",
69+
},
70+
"logout_enabled": {
71+
Type: schema.TypeBool,
72+
Optional: true,
73+
Default: false,
74+
},
75+
"logout_url": {
76+
Type: schema.TypeString,
77+
Optional: true,
78+
Default: "",
79+
},
80+
"email_attribute": {
81+
Type: schema.TypeString,
82+
Optional: true,
83+
Default: "",
84+
},
85+
"debug_mode": {
86+
Type: schema.TypeBool,
87+
Optional: true,
88+
Default: false,
89+
},
90+
"sign_authn_request": {
91+
Type: schema.TypeBool,
92+
Optional: true,
93+
Default: false,
94+
},
95+
"disable_requested_authn_context": {
96+
Type: schema.TypeBool,
97+
Optional: true,
98+
Default: false,
99+
},
100+
"is_redirect_binding": {
101+
Type: schema.TypeBool,
102+
Optional: true,
103+
Default: false,
104+
},
105+
"certificate": {
106+
Type: schema.TypeString,
107+
Computed: true,
108+
},
109+
},
110+
}
111+
}
112+
113+
func getOnDemandProvisioningEnabledSchema() map[string]*schema.Schema {
114+
return map[string]*schema.Schema{
115+
"first_name_attribute": {
116+
Type: schema.TypeString,
117+
Optional: true,
118+
Default: "",
119+
},
120+
"last_name_attribute": {
121+
Type: schema.TypeString,
122+
Optional: true,
123+
Default: "",
124+
},
125+
"on_demand_provisioning_roles": {
126+
Type: schema.TypeList,
127+
Required: true,
128+
Elem: &schema.Schema{
129+
Type: schema.TypeString,
130+
},
131+
},
132+
}
133+
}
134+
135+
func resourceSumologicSamlConfigurationRead(d *schema.ResourceData, meta interface{}) error {
136+
c := meta.(*Client)
137+
138+
id := d.Id()
139+
samlConfiguration, err := c.GetSamlConfiguration(id)
140+
141+
if err != nil {
142+
return err
143+
}
144+
145+
if samlConfiguration == nil {
146+
log.Printf("[WARN] SamlConfiguration not found, removing from state: %v - %v", id, err)
147+
d.SetId("")
148+
return nil
149+
}
150+
151+
setSamlConfiguration(d, samlConfiguration)
152+
return nil
153+
}
154+
155+
func resourceSumologicSamlConfigurationCreate(d *schema.ResourceData, meta interface{}) error {
156+
c := meta.(*Client)
157+
158+
if d.Id() == "" {
159+
samlConfiguration := resourceToSamlConfiguration(d)
160+
161+
createdSamlConfiguration, err := c.CreateSamlConfiguration(samlConfiguration)
162+
if err != nil {
163+
return err
164+
}
165+
166+
d.SetId(createdSamlConfiguration.ID)
167+
}
168+
169+
return resourceSumologicSamlConfigurationRead(d, meta)
170+
}
171+
172+
func resourceSumologicSamlConfigurationDelete(d *schema.ResourceData, meta interface{}) error {
173+
c := meta.(*Client)
174+
175+
return c.DeleteSamlConfiguration(d.Id())
176+
}
177+
178+
func resourceSumologicSamlConfigurationUpdate(d *schema.ResourceData, meta interface{}) error {
179+
c := meta.(*Client)
180+
181+
samlConfiguration := resourceToSamlConfiguration(d)
182+
183+
err := c.UpdateSamlConfiguration(d.Id(), samlConfiguration)
184+
if err != nil {
185+
return err
186+
}
187+
188+
return resourceSumologicSamlConfigurationRead(d, meta)
189+
}
190+
191+
func setSamlConfiguration(d *schema.ResourceData, samlConfiguration *SamlConfiguration) {
192+
d.Set("sp_initiated_login_path", samlConfiguration.SpInitiatedLoginPath)
193+
d.Set("configuration_name", samlConfiguration.ConfigurationName)
194+
d.Set("issuer", samlConfiguration.Issuer)
195+
d.Set("sp_initiated_login_enabled", samlConfiguration.SpInitiatedLoginEnabled)
196+
d.Set("authn_request_url", samlConfiguration.AuthnRequestUrl)
197+
d.Set("x509cert1", samlConfiguration.X509cert1)
198+
d.Set("x509cert2", samlConfiguration.X509cert2)
199+
d.Set("x509cert3", samlConfiguration.X509cert3)
200+
setOnDemandProvisioningEnabled(d, samlConfiguration.OnDemandProvisioningEnabled)
201+
d.Set("roles_attribute", samlConfiguration.RolesAttribute)
202+
d.Set("logout_enabled", samlConfiguration.LogoutEnabled)
203+
d.Set("logout_url", samlConfiguration.LogoutUrl)
204+
d.Set("email_attribute", samlConfiguration.EmailAttribute)
205+
d.Set("debug_mode", samlConfiguration.DebugMode)
206+
d.Set("sign_authn_request", samlConfiguration.SignAuthnRequest)
207+
d.Set("disable_requested_authn_context", samlConfiguration.DisableRequestedAuthnContext)
208+
d.Set("is_redirect_binding", samlConfiguration.IsRedirectBinding)
209+
210+
d.Set("certificate", samlConfiguration.Certificate)
211+
}
212+
213+
func getTerraformOnDemandProvisioningEnabled(onDemandProvisioningEnabled *OnDemandProvisioningEnabled) []map[string]interface{} {
214+
tfOnDemandProvisioningEnabledMap := make(map[string]interface{})
215+
tfOnDemandProvisioningEnabledMap["first_name_attribute"] = onDemandProvisioningEnabled.FirstNameAttribute
216+
tfOnDemandProvisioningEnabledMap["last_name_attribute"] = onDemandProvisioningEnabled.LastNameAttribute
217+
tfOnDemandProvisioningEnabledMap["on_demand_provisioning_roles"] = onDemandProvisioningEnabled.OnDemandProvisioningRoles
218+
219+
tfOnDemandProvisioningEnabled := make([]map[string]interface{}, 1)
220+
tfOnDemandProvisioningEnabled[0] = tfOnDemandProvisioningEnabledMap
221+
return tfOnDemandProvisioningEnabled
222+
}
223+
224+
func resourceToSamlConfiguration(d *schema.ResourceData) SamlConfiguration {
225+
var samlConfiguration SamlConfiguration
226+
samlConfiguration.SpInitiatedLoginPath = d.Get("sp_initiated_login_path").(string)
227+
samlConfiguration.ConfigurationName = d.Get("configuration_name").(string)
228+
samlConfiguration.Issuer = d.Get("issuer").(string)
229+
samlConfiguration.SpInitiatedLoginEnabled = d.Get("sp_initiated_login_enabled").(bool)
230+
samlConfiguration.AuthnRequestUrl = d.Get("authn_request_url").(string)
231+
samlConfiguration.X509cert1 = d.Get("x509cert1").(string)
232+
samlConfiguration.X509cert2 = d.Get("x509cert2").(string)
233+
samlConfiguration.X509cert3 = d.Get("x509cert3").(string)
234+
if val, ok := d.GetOk("on_demand_provisioning_enabled"); ok {
235+
obj := val.([]interface{})[0]
236+
samlConfiguration.OnDemandProvisioningEnabled = getOnDemandProvisioningEnabled(obj.(map[string]interface{}))
237+
}
238+
samlConfiguration.RolesAttribute = d.Get("roles_attribute").(string)
239+
samlConfiguration.LogoutEnabled = d.Get("logout_enabled").(bool)
240+
samlConfiguration.LogoutUrl = d.Get("logout_url").(string)
241+
samlConfiguration.EmailAttribute = d.Get("email_attribute").(string)
242+
samlConfiguration.DebugMode = d.Get("debug_mode").(bool)
243+
samlConfiguration.SignAuthnRequest = d.Get("sign_authn_request").(bool)
244+
samlConfiguration.DisableRequestedAuthnContext = d.Get("disable_requested_authn_context").(bool)
245+
samlConfiguration.IsRedirectBinding = d.Get("is_redirect_binding").(bool)
246+
return samlConfiguration
247+
}
248+
249+
func setOnDemandProvisioningEnabled(d *schema.ResourceData, obj *OnDemandProvisioningEnabled) {
250+
// The API responds with an empty OnDemandProvisioningEnabled object even if it wasn't provided in the request.
251+
// If we set the state with that empty object, then subsequent `terraform plan` will show an update is needed
252+
// if it's not specified in the configuration. We also can't set an empty OnDemandProvisioningEnabled as the
253+
// default since it'll result in an invalid request body. For this reason, don't set the state if an empty
254+
// OnDemandProvisioningEnabled is returned.
255+
if !isOnDemandProvisioningEnabledNilOrEmpty(obj) {
256+
onDemandProvisioningEnabled := getTerraformOnDemandProvisioningEnabled(obj)
257+
if err := d.Set("on_demand_provisioning_enabled", onDemandProvisioningEnabled); err != nil {
258+
log.Printf("[ERROR] in setting on_demand_provisioning_enabled: %v", err)
259+
}
260+
}
261+
}
262+
263+
func isOnDemandProvisioningEnabledNilOrEmpty(obj *OnDemandProvisioningEnabled) bool {
264+
if obj == nil {
265+
return true
266+
}
267+
268+
return obj.FirstNameAttribute == "" && obj.LastNameAttribute == "" && len(obj.OnDemandProvisioningRoles) == 0
269+
}
270+
271+
func getOnDemandProvisioningEnabled(resourceMap map[string]interface{}) *OnDemandProvisioningEnabled {
272+
var onDemandProvisioningEnabled OnDemandProvisioningEnabled
273+
onDemandProvisioningEnabled.FirstNameAttribute = resourceMap["first_name_attribute"].(string)
274+
onDemandProvisioningEnabled.LastNameAttribute = resourceMap["last_name_attribute"].(string)
275+
// https://stackoverflow.com/questions/37329246/how-to-convert-string-from-interface-to-string-in-golang
276+
rolesCollection := resourceMap["on_demand_provisioning_roles"].([]interface{})
277+
roles := make([]string, len(rolesCollection))
278+
for i, v := range rolesCollection {
279+
roles[i] = v.(string)
280+
}
281+
onDemandProvisioningEnabled.OnDemandProvisioningRoles = roles
282+
return &onDemandProvisioningEnabled
283+
}

0 commit comments

Comments
 (0)