Skip to content

Commit e87dacd

Browse files
authored
feat: Add OAUTH authentication option to SMTP servers (#1162) (#1194)
This patch leaves the auth argument to smtp_server as it is, but adds a new token_auth block to let users configure an auth token through Terraform. * Add OAUTH authentication option to SMTP servers * Add tests for SMTP OAUTH Implements #1162 Fixes #1194 Signed-off-by: Lucy McPhail <[email protected]>
1 parent 07a3331 commit e87dacd

File tree

5 files changed

+190
-20
lines changed

5 files changed

+190
-20
lines changed

docs/resources/realm.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,9 +158,16 @@ This block supports the following arguments:
158158
- `envelope_from` - (Optional) The email address uses for bounces.
159159
- `starttls` - (Optional) When `true`, enables StartTLS. Defaults to `false`.
160160
- `ssl` - (Optional) When `true`, enables SSL. Defaults to `false`.
161-
- `auth` - (Optional) Enables authentication to the SMTP server. This block supports the following arguments:
161+
- `auth` - (Optional) Enables authentication to the SMTP server. Cannot be set alongside `token_auth`. This block supports the following arguments:
162162
- `username` - (Required) The SMTP server username.
163163
- `password` - (Required) The SMTP server password.
164+
- `token_auth` - (Optional) Enables authentication to the SMTP server through OAUTH2. Cannot be set alongside `auth`. This block supports the following arguments:
165+
- `username` - (Required) The SMTP server username.
166+
- `url` - (Required) The auth token URL.
167+
- `client_id` - (Required) The auth token client ID.
168+
- `client_secret` - (Required) The auth token client secret.
169+
- `scope` - (Required) The auth token scope.
170+
164171

165172
### Internationalization
166173

keycloak/realm.go

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -158,18 +158,23 @@ type BrowserSecurityHeaders struct {
158158
}
159159

160160
type SmtpServer struct {
161-
StartTls types.KeycloakBoolQuoted `json:"starttls,omitempty"`
162-
Auth types.KeycloakBoolQuoted `json:"auth,omitempty"`
163-
Port string `json:"port,omitempty"`
164-
Host string `json:"host,omitempty"`
165-
ReplyTo string `json:"replyTo,omitempty"`
166-
ReplyToDisplayName string `json:"replyToDisplayName,omitempty"`
167-
From string `json:"from,omitempty"`
168-
FromDisplayName string `json:"fromDisplayName,omitempty"`
169-
EnvelopeFrom string `json:"envelopeFrom,omitempty"`
170-
Ssl types.KeycloakBoolQuoted `json:"ssl,omitempty"`
171-
User string `json:"user,omitempty"`
172-
Password string `json:"password,omitempty"`
161+
StartTls types.KeycloakBoolQuoted `json:"starttls,omitempty"`
162+
Auth types.KeycloakBoolQuoted `json:"auth,omitempty"`
163+
Port string `json:"port,omitempty"`
164+
Host string `json:"host,omitempty"`
165+
ReplyTo string `json:"replyTo,omitempty"`
166+
ReplyToDisplayName string `json:"replyToDisplayName,omitempty"`
167+
From string `json:"from,omitempty"`
168+
FromDisplayName string `json:"fromDisplayName,omitempty"`
169+
EnvelopeFrom string `json:"envelopeFrom,omitempty"`
170+
Ssl types.KeycloakBoolQuoted `json:"ssl,omitempty"`
171+
User string `json:"user,omitempty"`
172+
Password string `json:"password,omitempty"`
173+
AuthType string `json:"authType,omitempty"`
174+
AuthTokenUrl string `json:"authTokenUrl,omitempty"`
175+
AuthTokenClientId string `json:"authTokenClientId,omitempty"`
176+
AuthTokenClientSecret string `json:"authTokenClientSecret,omitempty"`
177+
AuthTokenScope string `json:"authTokenScope,omitempty"`
173178
}
174179

175180
func (keycloakClient *KeycloakClient) NewRealm(ctx context.Context, realm *Realm) error {

provider/data_source_keycloak_realm.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,36 @@ func dataSourceKeycloakRealm() *schema.Resource {
229229
},
230230
},
231231
},
232+
"token_auth": {
233+
Type: schema.TypeList,
234+
Computed: true,
235+
Optional: true,
236+
Elem: &schema.Resource{
237+
Schema: map[string]*schema.Schema{
238+
"username": {
239+
Type: schema.TypeString,
240+
Computed: true,
241+
},
242+
"url": {
243+
Type: schema.TypeString,
244+
Computed: true,
245+
},
246+
"client_id": {
247+
Type: schema.TypeString,
248+
Computed: true,
249+
},
250+
"client_secret": {
251+
Type: schema.TypeString,
252+
Computed: true,
253+
Sensitive: true,
254+
},
255+
"scope": {
256+
Type: schema.TypeString,
257+
Computed: true,
258+
},
259+
},
260+
},
261+
},
232262
},
233263
},
234264
},

provider/resource_keycloak_realm.go

Lines changed: 67 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -286,9 +286,10 @@ func resourceKeycloakRealm() *schema.Resource {
286286
Optional: true,
287287
},
288288
"auth": {
289-
Type: schema.TypeList,
290-
Optional: true,
291-
MaxItems: 1,
289+
Type: schema.TypeList,
290+
Optional: true,
291+
ConflictsWith: []string{"smtp_server.0.token_auth"},
292+
MaxItems: 1,
292293
Elem: &schema.Resource{
293294
Schema: map[string]*schema.Schema{
294295
"username": {
@@ -306,6 +307,40 @@ func resourceKeycloakRealm() *schema.Resource {
306307
},
307308
},
308309
},
310+
"token_auth": {
311+
Type: schema.TypeList,
312+
Optional: true,
313+
ConflictsWith: []string{"smtp_server.0.auth"},
314+
MaxItems: 1,
315+
Elem: &schema.Resource{
316+
Schema: map[string]*schema.Schema{
317+
"username": {
318+
Type: schema.TypeString,
319+
Required: true,
320+
},
321+
"url": {
322+
Type: schema.TypeString,
323+
Required: true,
324+
},
325+
"client_id": {
326+
Type: schema.TypeString,
327+
Required: true,
328+
},
329+
"client_secret": {
330+
Type: schema.TypeString,
331+
Required: true,
332+
Sensitive: true,
333+
DiffSuppressFunc: func(_, authTokenClientSecret, _ string, _ *schema.ResourceData) bool {
334+
return authTokenClientSecret == "**********"
335+
},
336+
},
337+
"scope": {
338+
Type: schema.TypeString,
339+
Required: true,
340+
},
341+
},
342+
},
343+
},
309344
},
310345
},
311346
},
@@ -809,12 +844,25 @@ func getRealmFromData(data *schema.ResourceData, keycloakVersion *version.Versio
809844
}
810845

811846
authConfig := smtpSettings["auth"].([]interface{})
847+
tokenAuthConfig := smtpSettings["token_auth"].([]interface{})
848+
812849
if len(authConfig) == 1 {
813850
auth := authConfig[0].(map[string]interface{})
814851

815852
smtpServer.Auth = true
853+
smtpServer.AuthType = "basic"
816854
smtpServer.User = auth["username"].(string)
817855
smtpServer.Password = auth["password"].(string)
856+
} else if len(tokenAuthConfig) == 1 {
857+
tokenAuth := tokenAuthConfig[0].(map[string]interface{})
858+
859+
smtpServer.Auth = true
860+
smtpServer.AuthType = "token"
861+
smtpServer.User = tokenAuth["username"].(string)
862+
smtpServer.AuthTokenUrl = tokenAuth["url"].(string)
863+
smtpServer.AuthTokenClientId = tokenAuth["client_id"].(string)
864+
smtpServer.AuthTokenClientSecret = tokenAuth["client_secret"].(string)
865+
smtpServer.AuthTokenScope = tokenAuth["scope"].(string)
818866
} else {
819867
smtpServer.Auth = false
820868
}
@@ -1241,12 +1289,24 @@ func setRealmData(data *schema.ResourceData, realm *keycloak.Realm, keycloakVers
12411289
smtpSettings["ssl"] = realm.SmtpServer.Ssl
12421290

12431291
if realm.SmtpServer.Auth {
1244-
auth := make(map[string]interface{})
1292+
if realm.SmtpServer.AuthType == "token" {
1293+
token_auth := make(map[string]interface{})
1294+
1295+
token_auth["username"] = realm.SmtpServer.User
1296+
token_auth["url"] = realm.SmtpServer.AuthTokenUrl
1297+
token_auth["client_id"] = realm.SmtpServer.AuthTokenClientId
1298+
token_auth["client_secret"] = realm.SmtpServer.AuthTokenClientSecret
1299+
token_auth["scope"] = realm.SmtpServer.AuthTokenScope
12451300

1246-
auth["username"] = realm.SmtpServer.User
1247-
auth["password"] = realm.SmtpServer.Password
1301+
smtpSettings["token_auth"] = []interface{}{token_auth}
1302+
} else {
1303+
auth := make(map[string]interface{})
12481304

1249-
smtpSettings["auth"] = []interface{}{auth}
1305+
auth["username"] = realm.SmtpServer.User
1306+
auth["password"] = realm.SmtpServer.Password
1307+
1308+
smtpSettings["auth"] = []interface{}{auth}
1309+
}
12501310
}
12511311

12521312
data.Set("smtp_server", []interface{}{smtpSettings})

provider/resource_keycloak_realm_test.go

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,46 @@ func TestAccKeycloakRealm_SmtpServerUpdate(t *testing.T) {
151151
},
152152
})
153153
}
154+
func TestAccKeycloakRealm_SmtpServerOauth(t *testing.T) {
155+
realm := acctest.RandomWithPrefix("tf-acc")
156+
realmDisplayNameHtml := acctest.RandomWithPrefix("tf-acc")
157+
158+
resource.Test(t, resource.TestCase{
159+
ProviderFactories: testAccProviderFactories,
160+
PreCheck: func() { testAccPreCheck(t) },
161+
CheckDestroy: testAccCheckKeycloakRealmDestroy(),
162+
Steps: []resource.TestStep{
163+
{
164+
Config: testKeycloakRealm_WithSmtpServerWithOauth(realm, "myhost.com", "My Host", "user"),
165+
Check: testAccCheckKeycloakRealmSmtp("keycloak_realm.realm", "myhost.com", "My Host", "user"),
166+
},
167+
{
168+
Config: testKeycloakRealm_basic(realm, realm, realmDisplayNameHtml),
169+
Check: testAccCheckKeycloakRealmSmtp("keycloak_realm.realm", "", "", ""),
170+
},
171+
},
172+
})
173+
}
174+
175+
func TestAccKeycloakRealm_SmtpServerOauthUpdate(t *testing.T) {
176+
realm := acctest.RandomWithPrefix("tf-acc")
177+
178+
resource.Test(t, resource.TestCase{
179+
ProviderFactories: testAccProviderFactories,
180+
PreCheck: func() { testAccPreCheck(t) },
181+
CheckDestroy: testAccCheckKeycloakRealmDestroy(),
182+
Steps: []resource.TestStep{
183+
{
184+
Config: testKeycloakRealm_WithSmtpServerWithOauth(realm, "myhost.com", "My Host", "user"),
185+
Check: testAccCheckKeycloakRealmSmtp("keycloak_realm.realm", "myhost.com", "My Host", "user"),
186+
},
187+
{
188+
Config: testKeycloakRealm_WithSmtpServerWithOauth(realm, "myhost2.com", "My Host2", "user2"),
189+
Check: testAccCheckKeycloakRealmSmtp("keycloak_realm.realm", "myhost2.com", "My Host2", "user2"),
190+
},
191+
},
192+
})
193+
}
154194

155195
func TestAccKeycloakRealm_SmtpServerInvalid(t *testing.T) {
156196
realm := acctest.RandomWithPrefix("tf-acc")
@@ -1344,6 +1384,34 @@ resource "keycloak_realm" "realm" {
13441384
`, realm, realm, host, from, user)
13451385
}
13461386

1387+
func testKeycloakRealm_WithSmtpServerWithOauth(realm, host, from, user string) string {
1388+
return fmt.Sprintf(`
1389+
resource "keycloak_realm" "realm" {
1390+
realm = "%s"
1391+
enabled = true
1392+
display_name = "%s"
1393+
smtp_server {
1394+
host = "%s"
1395+
port = 25
1396+
from_display_name = "Tom"
1397+
from = "%s"
1398+
reply_to_display_name = "Tom"
1399+
reply_to = "[email protected]"
1400+
ssl = true
1401+
starttls = true
1402+
envelope_from = "[email protected]"
1403+
token_auth {
1404+
username = "%s"
1405+
url = "wibble.com"
1406+
client_id = "wibble"
1407+
client_secret = "wobble"
1408+
scope = "wiggle"
1409+
}
1410+
}
1411+
}
1412+
`, realm, realm, host, from, user)
1413+
}
1414+
13471415
func testKeycloakRealm_WithOTP(realm, otpType, algorithm string, period int) string {
13481416
return fmt.Sprintf(`
13491417
resource "keycloak_realm" "realm" {

0 commit comments

Comments
 (0)