Skip to content
This repository has been archived by the owner on May 19, 2020. It is now read-only.

Commit

Permalink
Merge pull request #1308 from sharms/fix-tls-errors
Browse files Browse the repository at this point in the history
Add TLS smtp send
  • Loading branch information
sharms authored Apr 13, 2018
2 parents 0a69384 + 9768759 commit 3da4312
Show file tree
Hide file tree
Showing 9 changed files with 129 additions and 35 deletions.
3 changes: 0 additions & 3 deletions controllers/root_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"strings"
"testing"

"github.com/cloudfoundry-community/go-cfenv"
"github.com/govau/cf-common/env"

"github.com/18F/cg-dashboard/controllers"
Expand All @@ -13,10 +12,8 @@ import (

func TestPing(t *testing.T) {
response, request := NewTestRequest("GET", "/ping", nil)
app, _ := cfenv.Current()
router, _, err := controllers.InitApp(
env.NewVarSet(env.WithMapLookup(GetMockCompleteEnvVars())),
app,
)
if err != nil {
t.Fatal(err)
Expand Down
5 changes: 2 additions & 3 deletions controllers/routes.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package controllers

import (
"github.com/cloudfoundry-community/go-cfenv"
"github.com/gocraft/web"
"github.com/govau/cf-common/env"

Expand Down Expand Up @@ -71,10 +70,10 @@ func InitRouter(settings *helpers.Settings, templates *helpers.Templates, mailer
}

// InitApp takes in envars and sets up the router and settings that will be used for the unstarted server.
func InitApp(envVars *env.VarSet, app *cfenv.App) (*web.Router, *helpers.Settings, error) {
func InitApp(envVars *env.VarSet) (*web.Router, *helpers.Settings, error) {
// Initialize the settings.
settings := helpers.Settings{}
if err := settings.InitSettings(envVars, app); err != nil {
if err := settings.InitSettings(envVars); err != nil {
return nil, nil, err
}
mailer, err := mailer.InitSMTPMailer(settings)
Expand Down
3 changes: 0 additions & 3 deletions controllers/routes_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package controllers_test
import (
"testing"

"github.com/cloudfoundry-community/go-cfenv"
"github.com/govau/cf-common/env"

"github.com/18F/cg-dashboard/controllers"
Expand Down Expand Up @@ -39,10 +38,8 @@ var initAppTests = []initAppTest{

func TestInitApp(t *testing.T) {
for _, test := range initAppTests {
app, _ := cfenv.Current()
router, settings, err := controllers.InitApp(
env.NewVarSet(env.WithMapLookup(test.envVars)),
app,
)
if (router == nil) != test.returnRouterNil {
t.Errorf("Test %s did not return correct router value. Expected %t, Actual %t", test.testName, test.returnRouterNil, (router == nil))
Expand Down
2 changes: 2 additions & 0 deletions helpers/env_vars.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ var (
SMTPPassEnvVar = "SMTP_PASS"
// SMTPFromEnvVar is SMTP from address for UAA invites
SMTPFromEnvVar = "SMTP_FROM"
// SMTPCertEnvVar is cert for TLS connection
SMTPCertEnvVar = "SMTP_CERT"
// TICSecretEnvVar is the shared secret with CF API proxy for forwarding client IPs
TICSecretEnvVar = "TIC_SECRET"
// CSRFKeyEnvVar is used for CSRF token. Must be 32 bytes, hex-encoded, e.g. openssl rand -hex 32
Expand Down
6 changes: 4 additions & 2 deletions helpers/settings.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (
"fmt"
"net/http"

"github.com/cloudfoundry-community/go-cfenv"
"github.com/gorilla/sessions"
"github.com/govau/cf-common/env"
"golang.org/x/net/context"
Expand Down Expand Up @@ -61,6 +60,8 @@ type Settings struct {
SMTPPass string
// SMTP from address for UAA invites
SMTPFrom string
// SMTPCert is x509 TLS cert
SMTPCert string
// Shared secret with CF API proxy
TICSecret string
// CSRFKey used for gorilla CSRF validation
Expand All @@ -84,7 +85,7 @@ func (s *Settings) CreateContext() context.Context {

// InitSettings attempts to populate all the fields of the Settings struct. It will return an error if it fails,
// otherwise it returns nil for success.
func (s *Settings) InitSettings(envVars *env.VarSet, app *cfenv.App) (retErr error) {
func (s *Settings) InitSettings(envVars *env.VarSet) (retErr error) {
defer func() {
// While .MustString() is convenient in readability below, we'd prefer
// to convert this to an error for upstream callers.
Expand Down Expand Up @@ -174,6 +175,7 @@ func (s *Settings) InitSettings(envVars *env.VarSet, app *cfenv.App) (retErr err
s.SMTPPass = envVars.String(SMTPPassEnvVar, "")
s.SMTPPort = envVars.String(SMTPPortEnvVar, "")
s.SMTPUser = envVars.String(SMTPUserEnvVar, "")
s.SMTPCert = envVars.String(SMTPCertEnvVar, "")
s.TICSecret = envVars.String(TICSecretEnvVar, "")
return nil
}
94 changes: 86 additions & 8 deletions helpers/settings_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package helpers_test
import (
"testing"

"github.com/cloudfoundry-community/go-cfenv"
"github.com/govau/cf-common/env"

"github.com/18F/cg-dashboard/helpers"
Expand Down Expand Up @@ -31,8 +30,30 @@ var initSettingsTests = []initSettingsTest{
helpers.CSRFKeyEnvVar: "00112233445566778899aabbccddeeff",
helpers.SMTPFromEnvVar: "[email protected]",
helpers.SMTPHostEnvVar: "localhost",
helpers.SecureCookiesEnvVar: "1",
helpers.TICSecretEnvVar: "tic",
helpers.SMTPCertEnvVar: `-----BEGIN CERTIFICATE-----
MIIDrjCCApYCCQDdihKIIO0hnTANBgkqhkiG9w0BAQUFADCBmDELMAkGA1UEBhMC
VVMxCzAJBgNVBAgTAkRDMRMwEQYDVQQHEwpXYXNoaW5ndG9uMQwwCgYDVQQKEwNH
U0ExEDAOBgNVBAsTB1RUUy0xOEYxGjAYBgNVBAMTEXNtdHAuZnIuY2xvdWQuZ292
MSswKQYJKoZIhvcNAQkBFhxjbG91ZC1nb3Ytb3BlcmF0aW9uc0Bnc2EuZ292MB4X
DTE4MDQxMTE5MTQ0NFoXDTE5MDQxMTE5MTQ0NFowgZgxCzAJBgNVBAYTAlVTMQsw
CQYDVQQIEwJEQzETMBEGA1UEBxMKV2FzaGluZ3RvbjEMMAoGA1UEChMDR1NBMRAw
DgYDVQQLEwdUVFMtMThGMRowGAYDVQQDExFzbXRwLmZyLmNsb3VkLmdvdjErMCkG
CSqGSIb3DQEJARYcY2xvdWQtZ292LW9wZXJhdGlvbnNAZ3NhLmdvdjCCASIwDQYJ
KoZIhvcNAQEBBQADggEPADCCAQoCggEBAKTzjVa6lJyMUmyZeT31emnH3smX3/zC
67r61EnArvjB9f3e/L2u6f7650OY/ywsfBz8hXrI5XpdhCbyqrfI+XpkDKFoABsu
y8anGbiDEwh8PLS4UckRZXDxG/8HBptgpkzNBoClr7XNGQa+Mk4j69949sx4uK/1
KiMXzKnaFxuuH7t7aNgzLr8eGzwqCtJqGuzEhsl6gPVDvvUlgtSItLiu5FoQLyau
Fkcmkoca0eyCEFW9R/zN39B0cpipMPsyDzyUJ+gP7jU7c2Z+gzNNbiN3qaNIU6pf
olBDW70Rz6/ww8qItkazKw8OaSJV6fM6mgwlc+4IN72qug9XS4kopNECAwEAATAN
BgkqhkiG9w0BAQUFAAOCAQEAKqPNWkXt4CJFlfsX+iBNynJTGiArsJR6FCwaNv6f
+a31WWSCrxkvIepreUNqyGuhs0d7cT1MZMtkM9051nNFbxLqIIi7b3LZWNzwQf9G
jRDrM8aUwh8idRUaJRWQQ+KgkOsY0iyttfLI0OLZzj39NldtDFKJrDpA4QuAKI9/
KpMer71/GuBE6mgdKOMAgyD7D0eoPxeks0sxzs6MqC+AcMeBrpbQWCKLHaOMhgZk
bGfWcTPRDkXJmVtA44UuNANU+Wc9aHD5ODMFU+LG3TOEK0GCcgxCrksXfwTYrNN2
81D90R6hWY2GDdOx+T2KPAWuAZT6IgABaI9rU4/OGvisRA==
-----END CERTIFICATE-----`,
helpers.SecureCookiesEnvVar: "1",
helpers.TICSecretEnvVar: "tic",
},
wantNilError: true,
},
Expand All @@ -51,9 +72,31 @@ var initSettingsTests = []initSettingsTest{
helpers.CSRFKeyEnvVar: "00112233445566778899aabbccddeeff",
helpers.SMTPFromEnvVar: "[email protected]",
helpers.SMTPHostEnvVar: "localhost",
helpers.SecureCookiesEnvVar: "0",
helpers.LocalCFEnvVar: "1",
helpers.TICSecretEnvVar: "tic",
helpers.SMTPCertEnvVar: `-----BEGIN CERTIFICATE-----
MIIDrjCCApYCCQDdihKIIO0hnTANBgkqhkiG9w0BAQUFADCBmDELMAkGA1UEBhMC
VVMxCzAJBgNVBAgTAkRDMRMwEQYDVQQHEwpXYXNoaW5ndG9uMQwwCgYDVQQKEwNH
U0ExEDAOBgNVBAsTB1RUUy0xOEYxGjAYBgNVBAMTEXNtdHAuZnIuY2xvdWQuZ292
MSswKQYJKoZIhvcNAQkBFhxjbG91ZC1nb3Ytb3BlcmF0aW9uc0Bnc2EuZ292MB4X
DTE4MDQxMTE5MTQ0NFoXDTE5MDQxMTE5MTQ0NFowgZgxCzAJBgNVBAYTAlVTMQsw
CQYDVQQIEwJEQzETMBEGA1UEBxMKV2FzaGluZ3RvbjEMMAoGA1UEChMDR1NBMRAw
DgYDVQQLEwdUVFMtMThGMRowGAYDVQQDExFzbXRwLmZyLmNsb3VkLmdvdjErMCkG
CSqGSIb3DQEJARYcY2xvdWQtZ292LW9wZXJhdGlvbnNAZ3NhLmdvdjCCASIwDQYJ
KoZIhvcNAQEBBQADggEPADCCAQoCggEBAKTzjVa6lJyMUmyZeT31emnH3smX3/zC
67r61EnArvjB9f3e/L2u6f7650OY/ywsfBz8hXrI5XpdhCbyqrfI+XpkDKFoABsu
y8anGbiDEwh8PLS4UckRZXDxG/8HBptgpkzNBoClr7XNGQa+Mk4j69949sx4uK/1
KiMXzKnaFxuuH7t7aNgzLr8eGzwqCtJqGuzEhsl6gPVDvvUlgtSItLiu5FoQLyau
Fkcmkoca0eyCEFW9R/zN39B0cpipMPsyDzyUJ+gP7jU7c2Z+gzNNbiN3qaNIU6pf
olBDW70Rz6/ww8qItkazKw8OaSJV6fM6mgwlc+4IN72qug9XS4kopNECAwEAATAN
BgkqhkiG9w0BAQUFAAOCAQEAKqPNWkXt4CJFlfsX+iBNynJTGiArsJR6FCwaNv6f
+a31WWSCrxkvIepreUNqyGuhs0d7cT1MZMtkM9051nNFbxLqIIi7b3LZWNzwQf9G
jRDrM8aUwh8idRUaJRWQQ+KgkOsY0iyttfLI0OLZzj39NldtDFKJrDpA4QuAKI9/
KpMer71/GuBE6mgdKOMAgyD7D0eoPxeks0sxzs6MqC+AcMeBrpbQWCKLHaOMhgZk
bGfWcTPRDkXJmVtA44UuNANU+Wc9aHD5ODMFU+LG3TOEK0GCcgxCrksXfwTYrNN2
81D90R6hWY2GDdOx+T2KPAWuAZT6IgABaI9rU4/OGvisRA==
-----END CERTIFICATE-----`,
helpers.SecureCookiesEnvVar: "0",
helpers.LocalCFEnvVar: "1",
helpers.TICSecretEnvVar: "tic",
},
wantNilError: true,
},
Expand Down Expand Up @@ -216,16 +259,51 @@ var initSettingsTests = []initSettingsTest{
},
wantNilError: false,
},
{
testName: "Invalid SMTP Certificate",
envVars: map[string]string{
helpers.ClientIDEnvVar: "ID",
helpers.ClientSecretEnvVar: "Secret",
helpers.HostnameEnvVar: "hostname",
helpers.LoginURLEnvVar: "loginurl",
helpers.UAAURLEnvVar: "uaaurl",
helpers.APIURLEnvVar: "apiurl",
helpers.SessionAuthenticationEnvVar: "00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff",
helpers.CSRFKeyEnvVar: "00112233445566778899aabbccddeeff",
helpers.SMTPFromEnvVar: "[email protected]",
helpers.SMTPCertEnvVar: "12345",
},
wantNilError: false,
},
{
testName: "Basic Valid Local CF Settings missing optional SMTP cert",
envVars: map[string]string{
helpers.ClientIDEnvVar: "ID",
helpers.ClientSecretEnvVar: "Secret",
helpers.HostnameEnvVar: "hostname",
helpers.LoginURLEnvVar: "loginurl",
helpers.UAAURLEnvVar: "uaaurl",
helpers.APIURLEnvVar: "apiurl",
helpers.LogURLEnvVar: "logurl",
helpers.SessionEncryptionEnvVar: "00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff",
helpers.SessionAuthenticationEnvVar: "00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff",
helpers.CSRFKeyEnvVar: "00112233445566778899aabbccddeeff",
helpers.SMTPFromEnvVar: "[email protected]",
helpers.SMTPHostEnvVar: "localhost",
helpers.SecureCookiesEnvVar: "0",
helpers.LocalCFEnvVar: "1",
helpers.TICSecretEnvVar: "tic",
},
wantNilError: true,
},
}

func TestInitSettings(t *testing.T) {
app, _ := cfenv.Current()
for _, tt := range initSettingsTests {
t.Run(tt.testName, func(t *testing.T) {
s := helpers.Settings{}
err := s.InitSettings(
env.NewVarSet(env.WithMapLookup(tt.envVars)),
app,
)
if (err == nil) != tt.wantNilError {
t.Errorf("return value: got %t, want %t", (err == nil), tt.wantNilError)
Expand Down
6 changes: 1 addition & 5 deletions helpers/testhelpers/testhelpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import (
"testing"
"time"

"github.com/cloudfoundry-community/go-cfenv"
"github.com/gocraft/web"
"github.com/gorilla/sessions"
"github.com/govau/cf-common/env"
Expand Down Expand Up @@ -114,8 +113,7 @@ func EchoResponseHandler(rw http.ResponseWriter, response *http.Response) {
func CreateRouterWithMockSession(sessionData map[string]interface{}, envVars map[string]string) (*web.Router, *MockSessionStore) {
// Initialize settings.
settings := helpers.Settings{}
app, _ := cfenv.Current()
settings.InitSettings(env.NewVarSet(env.WithMapLookup(envVars)), app)
settings.InitSettings(env.NewVarSet(env.WithMapLookup(envVars)))

// Initialize a new session store.
store := MockSessionStore{}
Expand Down Expand Up @@ -362,10 +360,8 @@ func PrepareExternalServerCall(t *testing.T, c *controllers.SecureContext, testS

// Assign settings to context
mockSettings := &helpers.Settings{}
app, _ := cfenv.Current()
mockSettings.InitSettings(
env.NewVarSet(env.WithMapLookup(test.EnvVars)),
app,
)
c.Settings = mockSettings

Expand Down
43 changes: 33 additions & 10 deletions mailer/mailer.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package mailer

import (
"crypto/tls"
"crypto/x509"
"net/smtp"

"github.com/18F/cg-dashboard/helpers"
Expand All @@ -14,21 +16,35 @@ type Mailer interface {

// InitSMTPMailer creates a new SMTP Mailer
func InitSMTPMailer(settings helpers.Settings) (Mailer, error) {
var tlsConfig *tls.Config
if settings.SMTPCert != "" {
pool := x509.NewCertPool()
pool.AppendCertsFromPEM([]byte(settings.SMTPCert))
tlsConfig = &tls.Config{
ServerName: settings.SMTPHost,
RootCAs: pool,
}

}
return &smtpMailer{
smtpHost: settings.SMTPHost,
smtpPort: settings.SMTPPort,
smtpUser: settings.SMTPUser,
smtpPass: settings.SMTPPass,
smtpFrom: settings.SMTPFrom,
smtpHost: settings.SMTPHost,
smtpPort: settings.SMTPPort,
smtpUser: settings.SMTPUser,
smtpPass: settings.SMTPPass,
smtpFrom: settings.SMTPFrom,
smtpCert: settings.SMTPCert,
tlsConfig: tlsConfig,
}, nil
}

type smtpMailer struct {
smtpHost string
smtpPort string
smtpUser string
smtpPass string
smtpFrom string
smtpHost string
smtpPort string
smtpUser string
smtpPass string
smtpFrom string
smtpCert string
tlsConfig *tls.Config
}

func (s *smtpMailer) SendEmail(emailAddress, subject string, body []byte) error {
Expand All @@ -37,5 +53,12 @@ func (s *smtpMailer) SendEmail(emailAddress, subject string, body []byte) error
e.To = []string{" <" + emailAddress + ">"}
e.HTML = body
e.Subject = subject

addr := s.smtpHost + ":" + s.smtpPass
auth := smtp.PlainAuth("", s.smtpUser, s.smtpPass, s.smtpHost)

if s.tlsConfig != nil {
return e.SendWithTLS(addr, auth, s.tlsConfig)
}
return e.Send(s.smtpHost+":"+s.smtpPort, smtp.PlainAuth("", s.smtpUser, s.smtpPass, s.smtpHost))
}
2 changes: 1 addition & 1 deletion server.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ func startApp(port string, app *cfenv.App) {
envVars = makeDefaultEnvVarSet(app)
}

router, settings, err := controllers.InitApp(envVars, app)
router, settings, err := controllers.InitApp(envVars)
if err != nil {
fmt.Println(err.Error())
// Terminate the program with a non-zero value number.
Expand Down

0 comments on commit 3da4312

Please sign in to comment.