Skip to content

Commit

Permalink
Merge branch 'master' into vtopc/DE-1366
Browse files Browse the repository at this point in the history
  • Loading branch information
vtopc authored Dec 1, 2024
2 parents acf05f4 + cb0a543 commit c8b5533
Show file tree
Hide file tree
Showing 8 changed files with 78 additions and 52 deletions.
19 changes: 10 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import (
var yourDomain = "your-domain-name" // e.g. mg.yourcompany.com

// You can find the Private API Key in your Account Menu, under "Settings":
// (https://app.mailgun.com/app/account/security)
// (https://app.mailgun.com/settings/api_security)
var privateAPIKey = "your-private-key"

func main() {
Expand Down Expand Up @@ -78,7 +78,7 @@ import (

func main() {
// You can find the Private API Key in your Account Menu, under "Settings":
// (https://app.mailgun.com/app/account/security)
// (https://app.mailgun.com/settings/api_security)
mg := mailgun.NewMailgun("your-domain.com", "your-private-key")

it := mg.ListEvents(&mailgun.ListEventOptions{Limit: 100})
Expand Down Expand Up @@ -134,7 +134,7 @@ import (

func main() {
// You can find the Private API Key in your Account Menu, under "Settings":
// (https://app.mailgun.com/app/account/security)
// (https://app.mailgun.com/settings/api_security)
mg := mailgun.NewMailgun("your-domain.com", "your-private-key")

begin := time.Now().Add(time.Second * -3)
Expand Down Expand Up @@ -209,8 +209,9 @@ import (

func main() {
// You can find the Private API Key in your Account Menu, under "Settings":
// (https://app.mailgun.com/app/account/security)
// (https://app.mailgun.com/settings/api_security)
mg := mailgun.NewMailgun("your-domain.com", "private-api-key")
mg.SetWebhookSigningKey("webhook-signing-key")

http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {

Expand Down Expand Up @@ -278,7 +279,7 @@ import (
var yourDomain = "your-domain-name" // e.g. mg.yourcompany.com

// You can find the Private API Key in your Account Menu, under "Settings":
// (https://app.mailgun.com/app/account/security)
// (https://app.mailgun.com/settings/api_security)
var privateAPIKey = "your-private-key"

func main() {
Expand Down Expand Up @@ -337,7 +338,7 @@ import (
var yourDomain = "your-domain-name" // e.g. mg.yourcompany.com

// You can find the Private API Key in your Account Menu, under "Settings":
// (https://app.mailgun.com/app/account/security)
// (https://app.mailgun.com/settings/api_security)
var privateAPIKey = "your-private-key"

func main() {
Expand Down Expand Up @@ -371,7 +372,7 @@ func main() {
```

The official mailgun documentation includes examples using this library. Go
[here](https://documentation.mailgun.com/en/latest/api_reference.html#api-reference)
[here](https://documentation.mailgun.com/docs/mailgun/api-reference/)
and click on the "Go" button at the top of the page.

### EU Region
Expand All @@ -389,8 +390,8 @@ mg.SetAPIBase(mailgun.APIBaseEU)
To run the tests various environment variables must be set. These are:

* `MG_DOMAIN` is the domain name - this is a value registered in the Mailgun admin interface.
* `MG_PUBLIC_API_KEY` is the Public Validation API key - you can get this value from the Mailgun [security page](https://app.mailgun.com/app/account/security)
* `MG_API_KEY` is the Private API key - you can get this value from the Mailgun [security page](https://app.mailgun.com/app/account/security)
* `MG_PUBLIC_API_KEY` is the Public Validation API key - you can get this value from the Mailgun [security page](https://app.mailgun.com/settings/api_security)
* `MG_API_KEY` is the Private API key - you can get this value from the Mailgun [security page](https://app.mailgun.com/settings/api_security)
* `MG_EMAIL_TO` is the email address used in various sending tests.

and finally
Expand Down
5 changes: 3 additions & 2 deletions domains_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@ import (
)

const (
testDomain = "mailgun.test"
testKey = "api-fake-key"
testDomain = "mailgun.test"
testKey = "api-fake-key"
testWebhookSigningKey = "webhook-signing-key"
)

func TestListDomains(t *testing.T) {
Expand Down
3 changes: 2 additions & 1 deletion examples/examples.go
Original file line number Diff line number Diff line change
Expand Up @@ -904,8 +904,9 @@ func UpdateWebhook(domain, apiKey string) error {
return mg.UpdateWebhook(ctx, "clicked", []string{"https://your_domain.com/clicked"})
}

func VerifyWebhookSignature(domain, apiKey, timestamp, token, signature string) (bool, error) {
func VerifyWebhookSignature(domain, apiKey, webhookSigningKey, timestamp, token, signature string) (bool, error) {
mg := mailgun.NewMailgun(domain, apiKey)
mg.SetWebhookSigningKey(webhookSigningKey)

return mg.VerifyWebhookSignature(mailgun.Signature{
TimeStamp: timestamp,
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ require (
github.com/go-chi/chi/v5 v5.1.0
github.com/json-iterator/go v1.1.12
github.com/mailgun/errors v0.4.0
github.com/stretchr/testify v1.9.0
github.com/stretchr/testify v1.10.0
)

require (
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8 h1:h+EGohizhe9XlX18rfpa8k8RAc5XyaeamM+0VHRd4lc=
golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
Expand Down
84 changes: 52 additions & 32 deletions mailgun.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,19 @@
// For further information please see the Mailgun documentation at
// http://documentation.mailgun.com/
//
// Original Author: Michael Banzon
// Contributions: Samuel A. Falvo II <sam.falvo %at% rackspace.com>
// Derrick J. Wippler <thrawn01 %at% gmail.com>
// Original Author: Michael Banzon
// Contributions: Samuel A. Falvo II <sam.falvo %at% rackspace.com>
// Derrick J. Wippler <thrawn01 %at% gmail.com>
//
// Examples
// # Examples
//
// All functions and method have a corresponding test, so if you don't find an
// example for a function you'd like to know more about, please check for a
// corresponding test. Of course, contributions to the documentation are always
// welcome as well. Feel free to submit a pull request or open a Github issue
// if you cannot find an example to suit your needs.
//
// List iterators
// # List iterators
//
// Most methods that begin with `List` return an iterator which simplfies
// paging through large result sets returned by the mailgun API. Most `List`
Expand All @@ -28,23 +28,22 @@
//
// For example, the following iterates over all pages of events 100 items at a time
//
// mg := mailgun.NewMailgun("your-domain.com", "your-api-key")
// it := mg.ListEvents(&mailgun.ListEventOptions{Limit: 100})
// mg := mailgun.NewMailgun("your-domain.com", "your-api-key")
// it := mg.ListEvents(&mailgun.ListEventOptions{Limit: 100})
//
// // The entire operation should not take longer than 30 seconds
// ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
// defer cancel()
// // The entire operation should not take longer than 30 seconds
// ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
// defer cancel()
//
// // For each page of 100 events
// var page []mailgun.Event
// for it.Next(ctx, &page) {
// for _, e := range page {
// // Do something with 'e'
// }
// }
// // For each page of 100 events
// var page []mailgun.Event
// for it.Next(ctx, &page) {
// for _, e := range page {
// // Do something with 'e'
// }
// }
//
//
// License
// # License
//
// Copyright (c) 2013-2019, Michael Banzon.
// All rights reserved.
Expand Down Expand Up @@ -270,12 +269,13 @@ type Mailgun interface {
// MailgunImpl bundles data needed by a large number of methods in order to interact with the Mailgun API.
// Colloquially, we refer to instances of this structure as "clients."
type MailgunImpl struct {
apiBase string
domain string
apiKey string
client *http.Client
baseURL string
overrideHeaders map[string]string
apiBase string
domain string
apiKey string
webhookSigningKey string
client *http.Client
baseURL string
overrideHeaders map[string]string

mu sync.RWMutex
capturedCurlOutput string
Expand All @@ -292,7 +292,7 @@ func NewMailgun(domain, apiKey string) *MailgunImpl {
}

// NewMailgunFromEnv returns a new Mailgun client using the environment variables
// MG_API_KEY, MG_DOMAIN, and MG_URL
// MG_API_KEY, MG_DOMAIN, MG_URL, and MG_WEBHOOK_SIGNING_KEY
func NewMailgunFromEnv() (*MailgunImpl, error) {
apiKey := os.Getenv("MG_API_KEY")
if apiKey == "" {
Expand All @@ -310,6 +310,11 @@ func NewMailgunFromEnv() (*MailgunImpl, error) {
mg.SetAPIBase(url)
}

webhookSigningKey := os.Getenv("MG_WEBHOOK_SIGNING_KEY")
if webhookSigningKey != "" {
mg.SetWebhookSigningKey(webhookSigningKey)
}

return mg, nil
}

Expand Down Expand Up @@ -338,6 +343,20 @@ func (mg *MailgunImpl) SetClient(c *http.Client) {
mg.client = c
}

// WebhookSigningKey returns the webhook signing key configured for this client
func (mg *MailgunImpl) WebhookSigningKey() string {
key := mg.webhookSigningKey
if key == "" {
return mg.APIKey()
}
return key
}

// SetWebhookSigningKey updates the webhook signing key for this client
func (mg *MailgunImpl) SetWebhookSigningKey(webhookSigningKey string) {
mg.webhookSigningKey = webhookSigningKey
}

// SetOnBehalfOfSubaccount sets X-Mailgun-On-Behalf-Of header to SUBACCOUNT_ACCOUNT_ID in order to perform API request
// on behalf of subaccount.
func (mg *MailgunImpl) SetOnBehalfOfSubaccount(subaccountId string) {
Expand All @@ -350,14 +369,15 @@ func (mg *MailgunImpl) RemoveOnBehalfOfSubaccount() {
}

// SetAPIBase updates the API Base URL for this client.
// // For EU Customers
// mg.SetAPIBase(mailgun.APIBaseEU)
//
// // For US Customers
// mg.SetAPIBase(mailgun.APIBaseUS)
// // For EU Customers
// mg.SetAPIBase(mailgun.APIBaseEU)
//
// // For US Customers
// mg.SetAPIBase(mailgun.APIBaseUS)
//
// // Set a custom base API
// mg.SetAPIBase("https://localhost/v3")
// // Set a custom base API
// mg.SetAPIBase("https://localhost/v3")
func (mg *MailgunImpl) SetAPIBase(address string) {
mg.apiBase = address
}
Expand Down
4 changes: 2 additions & 2 deletions webhooks.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ type WebhookPayload struct {

// Use this method to parse the webhook signature given as JSON in the webhook response
func (mg *MailgunImpl) VerifyWebhookSignature(sig Signature) (verified bool, err error) {
h := hmac.New(sha256.New, []byte(mg.APIKey()))
h := hmac.New(sha256.New, []byte(mg.WebhookSigningKey()))

_, err = io.WriteString(h, sig.TimeStamp)
if err != nil {
Expand All @@ -149,7 +149,7 @@ func (mg *MailgunImpl) VerifyWebhookSignature(sig Signature) (verified bool, err
// Deprecated: Please use the VerifyWebhookSignature() to parse the latest
// version of WebHooks from mailgun
func (mg *MailgunImpl) VerifyWebhookRequest(req *http.Request) (verified bool, err error) {
h := hmac.New(sha256.New, []byte(mg.APIKey()))
h := hmac.New(sha256.New, []byte(mg.WebhookSigningKey()))

_, err = io.WriteString(h, req.FormValue("timestamp"))
if err != nil {
Expand Down
9 changes: 6 additions & 3 deletions webhooks_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,10 @@ var signedTests = []bool{

func TestVerifyWebhookSignature(t *testing.T) {
mg := mailgun.NewMailgun(testDomain, testKey)
mg.SetWebhookSigningKey(testWebhookSigningKey)

for _, v := range signedTests {
fields := getSignatureFields(mg.APIKey(), v)
fields := getSignatureFields(mg.WebhookSigningKey(), v)
sig := mailgun.Signature{
TimeStamp: fields["timestamp"],
Token: fields["token"],
Expand All @@ -100,9 +101,10 @@ func TestVerifyWebhookSignature(t *testing.T) {

func TestVerifyWebhookRequest_Form(t *testing.T) {
mg := mailgun.NewMailgun(testDomain, testKey)
mg.SetWebhookSigningKey(testWebhookSigningKey)

for _, v := range signedTests {
fields := getSignatureFields(mg.APIKey(), v)
fields := getSignatureFields(mg.WebhookSigningKey(), v)
req := buildFormRequest(fields)

verified, err := mg.VerifyWebhookRequest(req)
Expand All @@ -116,9 +118,10 @@ func TestVerifyWebhookRequest_Form(t *testing.T) {

func TestVerifyWebhookRequest_MultipartForm(t *testing.T) {
mg := mailgun.NewMailgun(testDomain, testKey)
mg.SetWebhookSigningKey(testWebhookSigningKey)

for _, v := range signedTests {
fields := getSignatureFields(mg.APIKey(), v)
fields := getSignatureFields(mg.WebhookSigningKey(), v)
req := buildMultipartFormRequest(fields)

verified, err := mg.VerifyWebhookRequest(req)
Expand Down

0 comments on commit c8b5533

Please sign in to comment.