-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #58 from tidepool-org/noEnvCredentials
Remove need for credentials in ENV variables.
- Loading branch information
Showing
207 changed files
with
43,680 additions
and
14,436 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
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,10 @@ | ||
[[constraint]] | ||
# Required: the root import path of the project being constrained. | ||
name = "github.com/tidepool-org/go-common" | ||
# Recommended: the version constraint to enforce for the project. | ||
# Note that only one of "branch", "version" or "revision" can be specified. | ||
branch = "master" | ||
|
||
[prune] | ||
go-tests = true | ||
unused-packages = true |
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 |
---|---|---|
@@ -1,76 +1,107 @@ | ||
package clients | ||
|
||
import ( | ||
"crypto/hmac" | ||
"crypto/sha256" | ||
"encoding/base64" | ||
"fmt" | ||
"io/ioutil" | ||
"log" | ||
"net/http" | ||
"net/url" | ||
"strings" | ||
"time" | ||
|
||
"github.com/aws/aws-sdk-go/aws" | ||
"github.com/aws/aws-sdk-go/aws/awserr" | ||
"github.com/aws/aws-sdk-go/aws/session" | ||
"github.com/aws/aws-sdk-go/service/ses" | ||
) | ||
|
||
const ( | ||
// CharSet The character encoding for the email. | ||
CharSet = "UTF-8" | ||
|
||
// DefaultTextMessage will be sent to non-HTML email clients that receive our messages | ||
DefaultTextMessage = "You need an HTML client to read this email." | ||
) | ||
|
||
type ( | ||
// SesNotifier contains all information needed to send Amazon SES messages | ||
SesNotifier struct { | ||
Config *SesNotifierConfig | ||
SES *ses.SES | ||
} | ||
|
||
// SesNotifierConfig contains the static configuration for the Amazon SES service | ||
// Credentials come from the environment and are not passed in via configuration variables. | ||
SesNotifierConfig struct { | ||
EndPoint string `json:"serverEndpoint"` | ||
From string `json:"fromAddress"` | ||
SecretKey string `json:"secretKey"` | ||
AccessKey string `json:"accessKey"` | ||
From string `json:"fromAddress"` | ||
Region string `json:"region"` | ||
} | ||
) | ||
|
||
func NewSesNotifier(cfg *SesNotifierConfig) *SesNotifier { | ||
//NewSesNotifier creates a new Amazon SES notifier | ||
func NewSesNotifier(cfg *SesNotifierConfig) (*SesNotifier, error) { | ||
sess, err := session.NewSession(&aws.Config{ | ||
Region: aws.String(cfg.Region)}, | ||
) | ||
|
||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return &SesNotifier{ | ||
Config: cfg, | ||
} | ||
SES: ses.New(sess), | ||
}, nil | ||
} | ||
|
||
// Send a message to a list of recipients with a given subject | ||
func (c *SesNotifier) Send(to []string, subject string, msg string) (int, string) { | ||
var toAwsAddress = make([]*string, len(to)) | ||
for i, x := range to { | ||
toAwsAddress[i] = aws.String(x) | ||
} | ||
|
||
data := make(url.Values) | ||
data.Add("Action", "SendEmail") | ||
data.Add("Source", c.Config.From) | ||
data.Add("Destination.ToAddresses.member.1", strings.Join(to, ", ")) | ||
data.Add("Message.Subject.Data", subject) | ||
data.Add("Message.Body.Html.Data", msg) | ||
data.Add("AWSAccessKeyId", c.Config.AccessKey) | ||
|
||
return c.sesPost(data) | ||
} | ||
|
||
func (c *SesNotifier) generateAuthHeader(date string) string { | ||
h := hmac.New(sha256.New, []uint8(c.Config.SecretKey)) | ||
h.Write([]uint8(date)) | ||
signature := base64.StdEncoding.EncodeToString(h.Sum(nil)) | ||
return fmt.Sprintf("AWS3-HTTPS AWSAccessKeyId=%s, Algorithm=HmacSHA256, Signature=%s", c.Config.AccessKey, signature) | ||
} | ||
|
||
func (c *SesNotifier) sesPost(data url.Values) (int, string) { | ||
body := strings.NewReader(data.Encode()) | ||
req, err := http.NewRequest("POST", c.Config.EndPoint, body) | ||
if err != nil { | ||
return http.StatusInternalServerError, err.Error() | ||
input := &ses.SendEmailInput{ | ||
Destination: &ses.Destination{ | ||
CcAddresses: []*string{}, | ||
ToAddresses: toAwsAddress, | ||
}, | ||
Message: &ses.Message{ | ||
Body: &ses.Body{ | ||
Html: &ses.Content{ | ||
Charset: aws.String(CharSet), | ||
Data: aws.String(msg), | ||
}, | ||
Text: &ses.Content{ | ||
Charset: aws.String(CharSet), | ||
Data: aws.String(DefaultTextMessage), | ||
}, | ||
}, | ||
Subject: &ses.Content{ | ||
Charset: aws.String(CharSet), | ||
Data: aws.String(subject), | ||
}, | ||
}, | ||
Source: aws.String(c.Config.From), | ||
} | ||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded") | ||
|
||
date := time.Now().UTC().Format("Mon, 02 Jan 2006 15:04:05 -0700") | ||
req.Header.Set("Date", date) | ||
req.Header.Set("X-Amzn-Authorization", c.generateAuthHeader(date)) | ||
// Attempt to send the email. | ||
result, err := c.SES.SendEmail(input) | ||
|
||
r, err := http.DefaultClient.Do(req) | ||
// Display error messages if they occur. | ||
if err != nil { | ||
log.Printf("http error: %s", err) | ||
return http.StatusInternalServerError, err.Error() | ||
} | ||
if aerr, ok := err.(awserr.Error); ok { | ||
switch aerr.Code() { | ||
case ses.ErrCodeMessageRejected: | ||
log.Printf("%v: %v\n", ses.ErrCodeMessageRejected, aerr.Error()) | ||
case ses.ErrCodeMailFromDomainNotVerifiedException: | ||
log.Printf("%v: %v\n", ses.ErrCodeMailFromDomainNotVerifiedException, aerr.Error()) | ||
case ses.ErrCodeConfigurationSetDoesNotExistException: | ||
log.Printf("%v: %v\n", ses.ErrCodeConfigurationSetDoesNotExistException, aerr.Error()) | ||
default: | ||
log.Println(aerr.Error()) | ||
} | ||
} else { | ||
// Print the error, cast err to awserr.Error to get the Code and | ||
// Message from an error. | ||
log.Println(err.Error()) | ||
} | ||
|
||
resultbody, _ := ioutil.ReadAll(r.Body) | ||
r.Body.Close() | ||
|
||
return r.StatusCode, string(resultbody) | ||
return 400, result.String() | ||
} | ||
return 200, result.String() | ||
} |
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
Oops, something went wrong.