diff --git a/.circleci/config.yml b/.circleci/config.yml index 1920d8183ca..d74243ebe90 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -34,12 +34,17 @@ jobs: image: circleci/golang:1.13 environment: - GO111MODULE=on + - TEST_MAILHOG_SMTP=smtp://test:test@127.0.0.1:1025 + - TEST_MAILHOG_API=smtp://127.0.0.1:8025 - TEST_SELFSERVICE_OIDC_HYDRA_ADMIN=http://127.0.0.1:4445 - TEST_SELFSERVICE_OIDC_HYDRA_PUBLIC=http://127.0.0.1:4444 - TEST_SELFSERVICE_OIDC_HYDRA_INTEGRATION_ADDR=127.0.0.1:4499 - TEST_DATABASE_POSTGRESQL=postgres://test:test@localhost:5432/postgres?sslmode=disable - TEST_DATABASE_MYSQL=mysql://root:test@(localhost:3306)/mysql?parseTime=true # - TEST_DATABASE_COCKROACHDB=cockroach://root@localhost:26257/defaultdb?sslmode=disable + - + image: mailhog/mailhog:v1.0.0 + command: MailHog -invite-jim -jim-linkspeed-affect=0.25 -jim-reject-auth=0.25 -jim-reject-recipient=0.25 -jim-reject-sender=0.25 -jim-disconnect=0.25 -jim-linkspeed-min=1250 -jim-linkspeed-max=12500 - image: postgres:9.6 environment: diff --git a/contrib/sql/migrations/20191120000000_identities.down.fizz b/contrib/sql/migrations/20191100000001_identities.down.fizz similarity index 100% rename from contrib/sql/migrations/20191120000000_identities.down.fizz rename to contrib/sql/migrations/20191100000001_identities.down.fizz diff --git a/contrib/sql/migrations/20191120000000_identities.up.fizz b/contrib/sql/migrations/20191100000001_identities.up.fizz similarity index 95% rename from contrib/sql/migrations/20191120000000_identities.up.fizz rename to contrib/sql/migrations/20191100000001_identities.up.fizz index 3d06612a898..c56f3adec8f 100644 --- a/contrib/sql/migrations/20191120000000_identities.up.fizz +++ b/contrib/sql/migrations/20191100000001_identities.up.fizz @@ -2,8 +2,6 @@ create_table("identities") { t.Column("id", "uuid", {primary: true}) t.Column("traits_schema_url", "string", {"size": 2048}) t.Column("traits", "json") - - t.Timestamps() } create_table("identity_credential_types") { @@ -22,8 +20,6 @@ create_table("identity_credentials") { t.Column("identity_credential_type_id", "uuid", { "size": 32 }) t.Column("identity_id", "uuid") - t.Timestamps() - t.ForeignKey("identity_id", {"identities": ["id"]}, {"on_delete": "cascade"}) t.ForeignKey("identity_credential_type_id", {"identity_credential_types": ["id"]}, {"on_delete": "cascade"}) } @@ -33,8 +29,6 @@ create_table("identity_credential_identifiers") { t.Column("identifier", "string", {"size": 255}) t.Column("identity_credential_id", "uuid") - t.Timestamps() - t.ForeignKey("identity_credential_id", {"identity_credentials": ["id"]}, {"on_delete": "cascade"}) } diff --git a/contrib/sql/migrations/20191121000000_requests.down.fizz b/contrib/sql/migrations/20191100000002_requests.down.fizz similarity index 100% rename from contrib/sql/migrations/20191121000000_requests.down.fizz rename to contrib/sql/migrations/20191100000002_requests.down.fizz diff --git a/contrib/sql/migrations/20191121000000_requests.up.fizz b/contrib/sql/migrations/20191100000002_requests.up.fizz similarity index 95% rename from contrib/sql/migrations/20191121000000_requests.up.fizz rename to contrib/sql/migrations/20191100000002_requests.up.fizz index 05f5105bb46..219feacdf02 100644 --- a/contrib/sql/migrations/20191121000000_requests.up.fizz +++ b/contrib/sql/migrations/20191100000002_requests.up.fizz @@ -4,8 +4,6 @@ create_table("selfservice_login_requests") { t.Column("issued_at", "timestamp", { default_raw: "CURRENT_TIMESTAMP" }) t.Column("expires_at", "timestamp") t.Column("active_method", "string", {"size": 32}) - - t.Timestamps() } create_table("selfservice_login_request_methods") { @@ -14,8 +12,6 @@ create_table("selfservice_login_request_methods") { t.Column("selfservice_login_request_id", "uuid") t.Column("config", "json") - t.Timestamps() - t.ForeignKey("selfservice_login_request_id", {"selfservice_login_requests": ["id"]}, {"on_delete": "cascade"}) } @@ -25,8 +21,6 @@ create_table("selfservice_registration_requests") { t.Column("issued_at", "timestamp", { default_raw: "CURRENT_TIMESTAMP" }) t.Column("expires_at", "timestamp") t.Column("active_method", "string", {"size": 32}) - - t.Timestamps() } create_table("selfservice_registration_request_methods") { @@ -35,8 +29,6 @@ create_table("selfservice_registration_request_methods") { t.Column("selfservice_registration_request_id", "uuid") t.Column("config", "json") - t.Timestamps() - t.ForeignKey("selfservice_registration_request_id", {"selfservice_registration_requests": ["id"]}, {"on_delete": "cascade"}) } @@ -49,7 +41,5 @@ create_table("selfservice_profile_management_requests") { t.Column("update_successful", "bool") t.Column("identity_id", "uuid") - t.Timestamps() - t.ForeignKey("identity_id", {"identities": ["id"]}, {"on_delete": "cascade"}) } diff --git a/contrib/sql/migrations/20191122000000_sessions.down.fizz b/contrib/sql/migrations/20191100000003_sessions.down.fizz similarity index 100% rename from contrib/sql/migrations/20191122000000_sessions.down.fizz rename to contrib/sql/migrations/20191100000003_sessions.down.fizz diff --git a/contrib/sql/migrations/20191122000000_sessions.up.fizz b/contrib/sql/migrations/20191100000003_sessions.up.fizz similarity index 95% rename from contrib/sql/migrations/20191122000000_sessions.up.fizz rename to contrib/sql/migrations/20191100000003_sessions.up.fizz index 94ab9a4e143..ef669cdaf83 100644 --- a/contrib/sql/migrations/20191122000000_sessions.up.fizz +++ b/contrib/sql/migrations/20191100000003_sessions.up.fizz @@ -5,7 +5,5 @@ create_table("sessions") { t.Column("authenticated_at", "timestamp") t.Column("identity_id", "uuid") - t.Timestamps() - t.ForeignKey("identity_id", {"identities": ["id"]}, {"on_delete": "cascade"}) } diff --git a/contrib/sql/migrations/20191123000000_errors.down.fizz b/contrib/sql/migrations/20191100000004_errors.down.fizz similarity index 100% rename from contrib/sql/migrations/20191123000000_errors.down.fizz rename to contrib/sql/migrations/20191100000004_errors.down.fizz diff --git a/contrib/sql/migrations/20191123000000_errors.up.fizz b/contrib/sql/migrations/20191100000004_errors.up.fizz similarity index 90% rename from contrib/sql/migrations/20191123000000_errors.up.fizz rename to contrib/sql/migrations/20191100000004_errors.up.fizz index e17347c2692..55156635e2f 100644 --- a/contrib/sql/migrations/20191123000000_errors.up.fizz +++ b/contrib/sql/migrations/20191100000004_errors.up.fizz @@ -3,6 +3,4 @@ create_table("selfservice_errors") { t.Column("errors", "json") t.Column("seen_at", "timestamp") t.Column("was_seen", "bool") - - t.Timestamps() } diff --git a/contrib/sql/migrations/20191100000005_identities.mysql.down.sql b/contrib/sql/migrations/20191100000005_identities.mysql.down.sql new file mode 100644 index 00000000000..48aed3b442d --- /dev/null +++ b/contrib/sql/migrations/20191100000005_identities.mysql.down.sql @@ -0,0 +1 @@ +ALTER TABLE identity_credential_identifiers MODIFY COLUMN identifier VARCHAR(255); \ No newline at end of file diff --git a/contrib/sql/migrations/20191130170530_identities.mysql.up.sql b/contrib/sql/migrations/20191100000005_identities.mysql.up.sql similarity index 100% rename from contrib/sql/migrations/20191130170530_identities.mysql.up.sql rename to contrib/sql/migrations/20191100000005_identities.mysql.up.sql diff --git a/contrib/sql/migrations/20191100000006_courier.down.fizz b/contrib/sql/migrations/20191100000006_courier.down.fizz new file mode 100644 index 00000000000..2da9c63dfc1 --- /dev/null +++ b/contrib/sql/migrations/20191100000006_courier.down.fizz @@ -0,0 +1 @@ +drop_table("courier_messages") diff --git a/contrib/sql/migrations/20191100000006_courier.up.fizz b/contrib/sql/migrations/20191100000006_courier.up.fizz new file mode 100644 index 00000000000..2c3138d69eb --- /dev/null +++ b/contrib/sql/migrations/20191100000006_courier.up.fizz @@ -0,0 +1,10 @@ +create_table("courier_messages") { + t.Column("id", "uuid", {primary: true}) + + t.Column("type", "int") + t.Column("status", "int") + + t.Column("body", "string") + t.Column("subject", "string") + t.Column("recipient", "string") +} diff --git a/contrib/sql/migrations/20191130170530_identities.mysql.down.sql b/contrib/sql/migrations/20191130170530_identities.mysql.down.sql deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/courier/courier.go b/courier/courier.go new file mode 100644 index 00000000000..db46cc5b4ed --- /dev/null +++ b/courier/courier.go @@ -0,0 +1,145 @@ +package courier + +import ( + "context" + "crypto/tls" + "fmt" + "strconv" + "time" + + "github.com/cenkalti/backoff" + "github.com/gofrs/uuid" + "github.com/pkg/errors" + "gopkg.in/gomail.v2" + + "github.com/ory/kratos/driver/configuration" + "github.com/ory/kratos/x" +) + +type ( + smtpDependencies interface { + PersistenceProvider + x.LoggingProvider + } + Courier struct { + dialer *gomail.Dialer + d smtpDependencies + c configuration.Provider + } + Provider interface { + Courier() *Courier + } +) + +func NewSMTP(d smtpDependencies, c configuration.Provider) *Courier { + uri := c.CourierSMTPURL() + sslSkipVerify, _ := strconv.ParseBool(uri.Query().Get("skip_ssl_verify")) + password, _ := uri.User.Password() + port, _ := strconv.ParseInt(uri.Port(), 10, 64) + return &Courier{ + d: d, + c: c, + dialer: &gomail.Dialer{ + Host: uri.Hostname(), + Port: int(port), + Username: uri.User.Username(), + Password: password, + SSL: uri.Scheme == "smtps", + TLSConfig: &tls.Config{InsecureSkipVerify: sslSkipVerify}, + }, + } +} + +func (m *Courier) SendEmail(ctx context.Context, t EmailTemplate) (uuid.UUID, error) { + body, err := t.EmailBody() + if err != nil { + return uuid.Nil, err + } + + subject, err := t.EmailSubject() + if err != nil { + return uuid.Nil, err + } + + recipient, err := t.EmailRecipient() + if err != nil { + return uuid.Nil, err + } + + message := &Message{ + Status: MessageStatusQueued, + Type: MessageTypeEmail, + Body: body, + Subject: subject, + Recipient: recipient, + } + if err := m.d.CourierPersister().AddMessage(ctx, message); err != nil { + return uuid.Nil, err + } + return message.ID, nil +} + +func (m *Courier) Work(ctx context.Context) error { + errChan := make(chan error) + defer close(errChan) + + go m.watchMessages(ctx, errChan) + + select { + case <-ctx.Done(): + return ctx.Err() + case err := <-errChan: + return err + } +} + +func (m *Courier) watchMessages(ctx context.Context, errChan chan error) { + for { + if err := backoff.Retry(func() error { + messages, err := m.d.CourierPersister().NextMessages(ctx, 10) + if err != nil { + return err + } + + for k := range messages { + var msg Message = messages[k] + + switch msg.Type { + case MessageTypeEmail: + from := m.c.CourierSMTPFrom() + gm := gomail.NewMessage() + gm.SetHeader("From", from) + gm.SetHeader("To", msg.Recipient) + gm.SetHeader("Subject", msg.Subject) + gm.SetBody("text/plain", msg.Body) + gm.AddAlternative("text/html", msg.Body) + + if err := m.dialer.DialAndSend(gm); err != nil { + m.d.Logger(). + WithError(err). + WithField("smtp_server", fmt.Sprintf("smtp(s)://%s:%d", m.dialer.Host, m.dialer.Port)). + WithField("email_to", msg.Recipient).WithField("email_from", from). + Error("Unable to send email using SMTP connection.") + continue + } + + if err := m.d.CourierPersister().SetMessageStatus(ctx, msg.ID, MessageStatusSent); err != nil { + m.d.Logger(). + WithError(err). + WithField("message_id", msg.ID). + Error(`Unable to set the message status to "sent".`) + return err + } + default: + return errors.Errorf("received unexpected message type: %d", msg.Type) + } + } + + return nil + }, backoff.NewExponentialBackOff()); err != nil { + errChan <- err + return + } + time.Sleep(time.Second) + } +} diff --git a/courier/courier_test.go b/courier/courier_test.go new file mode 100644 index 00000000000..13829f8e9e1 --- /dev/null +++ b/courier/courier_test.go @@ -0,0 +1,151 @@ +package courier_test + +import ( + "context" + "fmt" + "io/ioutil" + "net/http" + "os" + "testing" + "time" + + "github.com/cenkalti/backoff" + "github.com/gofrs/uuid" + "github.com/pkg/errors" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/tidwall/gjson" + + "github.com/ory/dockertest" + "github.com/ory/viper" + + templates "github.com/ory/kratos/courier/template" + "github.com/ory/kratos/driver/configuration" + "github.com/ory/kratos/internal" +) + +func runTestSMTP(t *testing.T) (smtp, api string, r *dockertest.Resource) { + if smtp, api := os.Getenv("TEST_MAILHOG_SMTP"), os.Getenv("TEST_MAILHOG_API"); smtp != "" && api != "" { + t.Logf("Skipping Docker setup because environment variables TEST_MAILHOG_SMTP and TEST_MAILHOG_API are both set.") + return smtp, api, nil + } else if len(smtp)+len(api) > 0 { + t.Fatal("Environment variables TEST_MAILHOG_SMTP, TEST_MAILHOG_API must both be set!") + return "", "", nil + } + + pool, err := dockertest.NewPool("") + require.NoError(t, err) + + resource, err := pool. + RunWithOptions(&dockertest.RunOptions{ + Repository: "mailhog/mailhog", + Tag: "v1.0.0", + Cmd: []string{ + "-invite-jim", + "-jim-linkspeed-affect=0.05", + "-jim-reject-auth=0.05", + "-jim-reject-recipient=0.05", + "-jim-reject-sender=0.05", + "-jim-disconnect=0.05", + "-jim-linkspeed-min=1250", + "-jim-linkspeed-max=12500", + }, + }) + require.NoError(t, err) + + smtp = fmt.Sprintf("smtp://test:test@127.0.0.1:%s", resource.GetPort("1025/tcp")) + api = fmt.Sprintf("http://127.0.0.1:%s", resource.GetPort("8025/tcp")) + require.NoError(t, backoff.Retry(func() error { + res, err := http.Get(api + "/api/v2/messages") + if err != nil { + t.Logf("Unable to connect to mailhog: %s", err) + return err + } + defer res.Body.Close() + if res.StatusCode != http.StatusOK { + err := errors.Errorf("expected status code 200 but got: %d", res.StatusCode) + t.Logf("Unable to connect to mailhog: %s", err) + return err + } + return nil + }, backoff.WithMaxRetries(backoff.NewConstantBackOff(time.Second), 15))) + + return smtp, api, resource +} + +func TestSMTP(t *testing.T) { + if testing.Short() { + t.SkipNow() + } + + smtp, api, resource := runTestSMTP(t) + defer resource.Close() + + conf, reg := internal.NewRegistryDefault(t) + viper.Set(configuration.ViperKeyCourierSMTPURL, smtp) + viper.Set(configuration.ViperKeyCourierSMTPFrom, "test-stub@ory.sh") + c := reg.Courier() + + go func() { + require.NoError(t, c.Work(context.Background())) + }() + + t.Run("case=queue messages", func(t *testing.T) { + id, err := c.SendEmail(context.Background(), templates.NewTestStub(conf, &templates.TestStubModel{ + To: "test-recipient-1@example.org", + Subject: "test-subject-1", + Body: "test-body-1", + })) + require.NoError(t, err) + require.NotEqual(t, uuid.Nil, id) + + id, err = c.SendEmail(context.Background(), templates.NewTestStub(conf, &templates.TestStubModel{ + To: "test-recipient-2@example.org", + Subject: "test-subject-2", + Body: "test-body-2", + })) + require.NoError(t, err) + require.NotEqual(t, uuid.Nil, id) + }) + + t.Run("case=check for delivery", func(t *testing.T) { + var err error + var body []byte + for k := 0; k < 30; k++ { + time.Sleep(time.Second) + err = func() error { + res, err := http.Get(api + "/api/v2/messages") + if err != nil { + return err + } + + defer res.Body.Close() + body, err = ioutil.ReadAll(res.Body) + if err != nil { + return err + } + + if http.StatusOK != res.StatusCode { + return errors.Errorf("expected status code 200 but got %d with body: %s", res.StatusCode, body) + } + + if total := gjson.GetBytes(body, "total").Int(); total != 2 { + return errors.Errorf("expected to have delivered at least 2 messages but got count %d with body: %s", total, body) + } + + return nil + }() + if err == nil { + break + } + } + require.NoError(t, err) + + for k := 1; k <= 2; k++ { + assert.Contains(t, string(body), fmt.Sprintf("test-subject-%d", k)) + assert.Contains(t, string(body), fmt.Sprintf("test-body-%d", k)) + assert.Contains(t, string(body), fmt.Sprintf("test-recipient-%d@example.org", k)) + assert.Contains(t, string(body), "test-stub@ory.sh") + } + }) +} diff --git a/courier/message.go b/courier/message.go new file mode 100644 index 00000000000..4d210a805ee --- /dev/null +++ b/courier/message.go @@ -0,0 +1,38 @@ +package courier + +import ( + "time" + + "github.com/gofrs/uuid" +) + +type MessageStatus int + +const ( + MessageStatusQueued MessageStatus = iota + 1 + MessageStatusSent +) + +type MessageType int + +const ( + MessageTypeEmail MessageType = iota + 1 +) + +type Message struct { + ID uuid.UUID `json:"-" faker:"-" db:"id"` + Status MessageStatus `json:"-" db:"status"` + Type MessageType `json:"-" db:"type"` + Recipient string `json:"-" db:"recipient"` + Body string `json:"-" db:"body"` + Subject string `json:"-" db:"subject"` + + // CreatedAt is a helper struct field for gobuffalo.pop. + CreatedAt time.Time `json:"-" faker:"-" db:"created_at"` + // UpdatedAt is a helper struct field for gobuffalo.pop. + UpdatedAt time.Time `json:"-" faker:"-" db:"updated_at"` +} + +func (m Message) TableName() string { + return "courier_messages" +} diff --git a/courier/persistence.go b/courier/persistence.go new file mode 100644 index 00000000000..10528c10314 --- /dev/null +++ b/courier/persistence.go @@ -0,0 +1,90 @@ +package courier + +import ( + "context" + "fmt" + "testing" + + "github.com/bxcodec/faker" + "github.com/pkg/errors" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/gofrs/uuid" + + "github.com/ory/kratos/identity" +) + +var ErrQueueEmpty = errors.New("queue is empty") + +type ( + Persister interface { + AddMessage(context.Context, *Message) error + + NextMessages(context.Context, uint8) ([]Message, error) + + SetMessageStatus(context.Context, uuid.UUID, MessageStatus) error + } + PersistenceProvider interface { + CourierPersister() Persister + } +) + +func TestPersister(p interface { + Persister + identity.Pool +}) func(t *testing.T) { + return func(t *testing.T) { + t.Run("case=no messages in queue", func(t *testing.T) { + m, err := p.NextMessages(context.Background(), 10) + require.EqualError(t, err, ErrQueueEmpty.Error()) + assert.Len(t, m, 0) + }) + + messages := make([]Message, 5) + t.Run("case=add messages to the queue", func(t *testing.T) { + for k, m := range messages { + require.NoError(t, faker.FakeData(&m)) + require.NoError(t, p.AddMessage(context.Background(), &m)) + messages[k] = m + } + }) + + t.Run("case=pull messages from the queue", func(t *testing.T) { + for k, expected := range messages { + t.Run(fmt.Sprintf("message=%d", k), func(t *testing.T) { + messages, err := p.NextMessages(context.Background(), 1) + require.NoError(t, err) + require.Len(t, messages, 1) + + actual := messages[0] + assert.Equal(t, expected.ID, actual.ID) + assert.Equal(t, expected.Subject, actual.Subject) + assert.Equal(t, expected.Body, actual.Body) + assert.Equal(t, expected.Status, actual.Status) + assert.Equal(t, expected.Type, actual.Type) + assert.Equal(t, expected.Recipient, actual.Recipient) + + require.NoError(t, p.SetMessageStatus(context.Background(), actual.ID, MessageStatusSent)) + }) + } + + ms, err := p.NextMessages(context.Background(), 10) + require.NoError(t, err) + assert.Len(t, ms, 0) + }) + + t.Run("case=setting message status", func(t *testing.T) { + require.NoError(t, p.SetMessageStatus(context.Background(), messages[0].ID, MessageStatusQueued)) + ms, err := p.NextMessages(context.Background(), 1) + require.NoError(t, err) + require.Len(t, ms, 1) + assert.Equal(t, messages[0].ID, ms[1].ID) + + require.NoError(t, p.SetMessageStatus(context.Background(), messages[0].ID, MessageStatusSent)) + ms, err = p.NextMessages(context.Background(), 1) + require.EqualError(t, err, ErrQueueEmpty.Error()) + assert.Len(t, ms, 0) + }) + } +} diff --git a/courier/template/load_template.go b/courier/template/load_template.go new file mode 100644 index 00000000000..12b4aafc779 --- /dev/null +++ b/courier/template/load_template.go @@ -0,0 +1,56 @@ +package templates + +import ( + "bytes" + "io" + "os" + "text/template" + + "github.com/Masterminds/sprig/v3" + lru "github.com/hashicorp/golang-lru" + "github.com/markbates/pkger" + "github.com/pkg/errors" +) + +var cache, _ = lru.New(16) + +func loadTextTemplate(path string, model interface{}) (string, error) { + var b bytes.Buffer + + if t, found := cache.Get(path); found { + var tb bytes.Buffer + if err := t.(*template.Template).ExecuteTemplate(&tb, path, model); err != nil { + return "", errors.WithStack(err) + } + return tb.String(), nil + } + + if file, err := pkger.Open(path); err == nil { + defer file.Close() + if _, err := io.Copy(&b, file); err != nil { + return "", errors.WithStack(err) + } + } else { + file, err := os.Open(path) + if err != nil { + return "", errors.WithStack(err) + } + defer file.Close() + if _, err := io.Copy(&b, file); err != nil { + return "", errors.WithStack(err) + } + } + + t, err := template.New(path).Funcs(sprig.TxtFuncMap()).Parse(b.String()) + if err != nil { + return "", errors.WithStack(err) + } + + _ = cache.Add(path, t) + var tb bytes.Buffer + if err := t.ExecuteTemplate(&tb, path, model); err != nil { + return "", errors.WithStack(err) + } + + return tb.String(), nil +} diff --git a/courier/template/load_template_test.go b/courier/template/load_template_test.go new file mode 100644 index 00000000000..6c81307f45a --- /dev/null +++ b/courier/template/load_template_test.go @@ -0,0 +1,36 @@ +package templates + +import ( + "bytes" + "os" + "path/filepath" + "testing" + + "github.com/shurcooL/go/ioutil" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/ory/kratos/x" +) + +func TestLoadTextTemplate(t *testing.T) { + var executeTemplate = func(t *testing.T, path string) string { + tp, err := loadTextTemplate(path, nil) + require.NoError(t, err) + return tp + } + + t.Run("method=from bundled", func(t *testing.T) { + actual := executeTemplate(t, "/courier/template/templates/test_stub/email.body.gotmpl") + assert.Contains(t, actual, "stub email") + }) + + t.Run("method=cache works", func(t *testing.T) { + fp := filepath.Join(os.TempDir(), x.NewUUID().String()) + ".body.gotmpl" + require.NoError(t, ioutil.WriteFile(fp, bytes.NewBufferString("cached stub body"))) + assert.Contains(t, executeTemplate(t, fp), "cached stub body") + + require.NoError(t, os.RemoveAll(fp)) + assert.Contains(t, executeTemplate(t, fp), "cached stub body") + }) +} diff --git a/courier/template/stub.go b/courier/template/stub.go new file mode 100644 index 00000000000..c492e466095 --- /dev/null +++ b/courier/template/stub.go @@ -0,0 +1,34 @@ +package templates + +import ( + "path/filepath" + + "github.com/ory/kratos/driver/configuration" +) + +type TestStub struct { + c configuration.Provider + m *TestStubModel +} + +type TestStubModel struct { + To string + Subject string + Body string +} + +func NewTestStub(c configuration.Provider, m *TestStubModel) *TestStub { + return &TestStub{c: c, m: m} +} + +func (t *TestStub) EmailRecipient() (string, error) { + return t.m.To, nil +} + +func (t *TestStub) EmailSubject() (string, error) { + return loadTextTemplate(filepath.Join(t.c.CourierTemplatesRoot(), "test_stub/email.subject.gotmpl"), t.m) +} + +func (t *TestStub) EmailBody() (string, error) { + return loadTextTemplate(filepath.Join(t.c.CourierTemplatesRoot(), "test_stub/email.body.gotmpl"), t.m) +} diff --git a/courier/template/templates/test_stub/email.body.gotmpl b/courier/template/templates/test_stub/email.body.gotmpl new file mode 100644 index 00000000000..f22928ff192 --- /dev/null +++ b/courier/template/templates/test_stub/email.body.gotmpl @@ -0,0 +1 @@ +stub email body {{ .Body }} \ No newline at end of file diff --git a/courier/template/templates/test_stub/email.subject.gotmpl b/courier/template/templates/test_stub/email.subject.gotmpl new file mode 100644 index 00000000000..e6562fffe30 --- /dev/null +++ b/courier/template/templates/test_stub/email.subject.gotmpl @@ -0,0 +1 @@ +stub email subject {{ .Subject }} \ No newline at end of file diff --git a/courier/templates.go b/courier/templates.go new file mode 100644 index 00000000000..0aa2a405236 --- /dev/null +++ b/courier/templates.go @@ -0,0 +1,7 @@ +package courier + +type EmailTemplate interface { + EmailSubject() (string, error) + EmailBody() (string, error) + EmailRecipient() (string, error) +} diff --git a/docs/.kratos.yaml b/docs/.kratos.yaml index b85a30a3ba5..2a7dae93ca0 100644 --- a/docs/.kratos.yaml +++ b/docs/.kratos.yaml @@ -99,8 +99,6 @@ selfservice: default_redirect_url: http://test.kratos.ory.sh:4000/ allow_user_defined_redirect: true - - # - run: account_activation # config: # redirect: diff --git a/docs/config.schema.json b/docs/config.schema.json index 2d0be929afc..d95a6660ba5 100644 --- a/docs/config.schema.json +++ b/docs/config.schema.json @@ -235,6 +235,43 @@ "dsn": { "type": "string" }, + "courier": { + "type": "object", + "title": "Courier configuration", + "description": "The courier is responsible for sending and delivering messages over email, sms, and other means.", + "properties": { + "template_override_path": { + "type": "string", + "title": "Override message templates", + "description": "You can override certain or all message templates by pointing this key to the path where the templates are located." + }, + "smtp": { + "title": "SMTP Configuration", + "description": "Configures outgoing emails using the SMTP protocol.", + "type": "object", + "properties": { + "connection_uri": { + "title": "SMTP connection string", + "description": "This URI will be used to connect to the SMTP server.", + "examples": ["smtps://foo:bar@my-mailserver:1234/"], + "type": "string", + "format": "uri" + }, + "from_address": { + "title": "SMTP Sender Address", + "description": "The recipient of an email will see this as the sender address.", + "type": "string", + "format": "email", + "default": "no-reply@ory.kratos.sh" + } + }, + "required": ["connection_uri"], + "additionalProperties": false + } + }, + "required": ["smtp"], + "additionalProperties": false + }, "serve": { "type": "object", "properties": { @@ -425,7 +462,8 @@ "required": [ "dsn", "identity", - "selfservice" + "selfservice", + "courier" ], "additionalProperties": false } \ No newline at end of file diff --git a/driver/configuration/provider.go b/driver/configuration/provider.go index a2394d0279b..5fd5593950e 100644 --- a/driver/configuration/provider.go +++ b/driver/configuration/provider.go @@ -55,6 +55,10 @@ type Provider interface { SelfServiceRegistrationAfterHooks(strategy string) []SelfServiceHook SelfServiceLogoutRedirectURL() *url.URL + CourierSMTPFrom() string + CourierSMTPURL() *url.URL + CourierTemplatesRoot() string + DefaultIdentityTraitsSchemaURL() *url.URL WhitelistedReturnToDomains() []url.URL diff --git a/driver/configuration/provider_viper.go b/driver/configuration/provider_viper.go index 9193f335d7a..4c63a49ca05 100644 --- a/driver/configuration/provider_viper.go +++ b/driver/configuration/provider_viper.go @@ -33,6 +33,10 @@ var _ Provider = new(ViperProvider) const ( ViperKeyDSN = "dsn" + ViperKeyCourierSMTPURL = "courier.smtp.connection_uri" + ViperKeyCourierTemplatesPath = "courier.template_override_path" + ViperKeyCourierSMTPFrom = "courier.stmp.from_address" + ViperKeySecretsSession = "secrets.session" ViperKeyURLsDefaultReturnTo = "urls.default_return_to" @@ -211,6 +215,10 @@ func (p *ViperProvider) SelfAdminURL() *url.URL { return mustParseURLFromViper(p.l, ViperKeyURLsSelfAdmin) } +func (p *ViperProvider) CourierSMTPURL() *url.URL { + return mustParseURLFromViper(p.l, ViperKeyCourierSMTPURL) +} + func (p *ViperProvider) LoginURL() *url.URL { return mustParseURLFromViper(p.l, ViperKeyURLsLogin) } @@ -262,6 +270,14 @@ func (p *ViperProvider) SelfServiceLogoutRedirectURL() *url.URL { return mustParseURLFromViper(p.l, ViperKeySelfServiceLogoutRedirectURL) } +func (p *ViperProvider) CourierSMTPFrom() string { + return viperx.GetString(p.l, ViperKeyCourierSMTPFrom, "noreply@kratos.ory.sh") +} + +func (p *ViperProvider) CourierTemplatesRoot() string { + return viperx.GetString(p.l, ViperKeyCourierTemplatesPath, "/courier/template/templates/") +} + func mustParseURLFromViper(l logrus.FieldLogger, key string) *url.URL { u, err := url.ParseRequestURI(viper.GetString(key)) if err != nil { diff --git a/driver/registry_default.go b/driver/registry_default.go index cbcb704c811..2803f135fd7 100644 --- a/driver/registry_default.go +++ b/driver/registry_default.go @@ -20,6 +20,7 @@ import ( "github.com/ory/x/logrusx" + "github.com/ory/kratos/courier" "github.com/ory/kratos/persistence" "github.com/ory/kratos/persistence/sql" "github.com/ory/kratos/selfservice/flow/login" @@ -51,6 +52,7 @@ type RegistryDefault struct { writer herodot.Writer healthxHandler *healthx.Handler + courier *courier.Courier persister persistence.Persister identityHandler *identity.Handler @@ -339,6 +341,13 @@ func (m *RegistryDefault) Init() error { ) } +func (m *RegistryDefault) Courier() *courier.Courier { + if m.courier == nil { + m.courier = courier.NewSMTP(m, m.c) + } + return m.courier +} + func (m *RegistryDefault) IdentityPool() identity.Pool { return m.persister } @@ -363,6 +372,10 @@ func (m *RegistryDefault) SessionPersister() session.Persister { return m.persister } +func (m *RegistryDefault) CourierPersister() courier.Persister { + return m.persister +} + func (m *RegistryDefault) Persister() persistence.Persister { return m.persister } diff --git a/go.mod b/go.mod index c3be82d2567..fba0e474388 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,8 @@ go 1.13 replace github.com/santhosh-tekuri/jsonschema/v2 => github.com/ory/jsonschema/v2 v2.1.1-0.20191123130340-1c20114d2c04 require ( + github.com/Masterminds/sprig/v3 v3.0.0 + github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 github.com/bxcodec/faker v2.0.1+incompatible github.com/cenkalti/backoff v2.1.1+incompatible github.com/coreos/go-oidc v2.0.0+incompatible @@ -20,11 +22,15 @@ require ( github.com/go-playground/universal-translator v0.16.0 // indirect github.com/go-swagger/go-swagger v0.19.0 github.com/go-swagger/scan-repo-boundary v0.0.0-20180623220736-973b3573c013 // indirect + github.com/gobuffalo/envy v1.8.1 // indirect + github.com/gobuffalo/fizz v1.9.5 // indirect + github.com/gobuffalo/flect v0.1.7 // indirect + github.com/gobuffalo/helpers v0.4.0 // indirect github.com/gobuffalo/httptest v1.0.2 github.com/gobuffalo/nulls v0.1.0 // indirect github.com/gobuffalo/packr v1.22.0 github.com/gobuffalo/packr/v2 v2.7.1 - github.com/gobuffalo/pop v4.12.2+incompatible + github.com/gobuffalo/pop v4.13.0+incompatible github.com/gobuffalo/tags v2.1.7+incompatible // indirect github.com/gobuffalo/uuid v2.0.5+incompatible github.com/gofrs/uuid v3.2.0+incompatible @@ -36,6 +42,7 @@ require ( github.com/gorilla/handlers v1.4.1 // indirect github.com/gorilla/securecookie v1.1.1 github.com/gorilla/sessions v1.1.3 + github.com/hashicorp/golang-lru v0.5.1 github.com/imdario/mergo v0.3.7 github.com/jessevdk/go-flags v1.4.0 // indirect github.com/jteeuwen/go-bindata v3.0.7+incompatible @@ -43,6 +50,7 @@ require ( github.com/justinas/nosurf v0.0.0-20190118163749-6453469bdcc9 github.com/leodido/go-urn v1.1.0 // indirect github.com/luna-duclos/instrumentedsql v1.1.1 // indirect + github.com/markbates/pkger v0.12.8 github.com/mattn/go-sqlite3 v1.13.0 // indirect github.com/mattn/goveralls v0.0.4 github.com/mitchellh/go-homedir v1.1.0 @@ -59,6 +67,7 @@ require ( github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 // indirect github.com/rogpeppe/go-internal v1.5.0 // indirect github.com/santhosh-tekuri/jsonschema/v2 v2.1.0 + github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e github.com/sirupsen/logrus v1.4.2 github.com/spf13/cobra v0.0.5 github.com/spf13/pflag v1.0.5 // indirect @@ -70,13 +79,15 @@ require ( github.com/toqueteos/webbrowser v1.1.0 // indirect github.com/urfave/negroni v1.0.0 github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect + go.uber.org/zap v1.10.0 golang.org/x/crypto v0.0.0-20191122220453-ac88ee75c92c - golang.org/x/net v0.0.0-20191125084936-ffdde1057850 // indirect + golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553 // indirect golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9 // indirect golang.org/x/tools v0.0.0-20191105231337-689d0f08e67a - golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 // indirect google.golang.org/appengine v1.6.5 // indirect gopkg.in/go-playground/assert.v1 v1.2.1 // indirect gopkg.in/go-playground/validator.v9 v9.28.0 + gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df + gotest.tools v2.2.0+incompatible ) diff --git a/go.sum b/go.sum index e4f887a1fd9..55ebd0b360f 100644 --- a/go.sum +++ b/go.sum @@ -15,7 +15,14 @@ github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= +github.com/Masterminds/goutils v1.1.0 h1:zukEsf/1JZwCMgHiK3GZftabmxiCw4apj3a28RPBiVg= +github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= +github.com/Masterminds/semver v1.4.2 h1:WBLTQ37jOCzSLtXNdoo8bNM8876KhNqOKvrlGITgsTc= github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= +github.com/Masterminds/semver/v3 v3.0.1 h1:2kKm5lb7dKVrt5TYUiAavE6oFc1cFT0057UVGT+JqLk= +github.com/Masterminds/semver/v3 v3.0.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= +github.com/Masterminds/sprig/v3 v3.0.0 h1:KSQz7Nb08/3VU9E4ns29dDxcczhOD1q7O1UfM4G3t3g= +github.com/Masterminds/sprig/v3 v3.0.0/go.mod h1:NEUY/Qq8Gdm2xgYA+NwJM6wmfdRV9xkh8h/Rld20R0U= github.com/Microsoft/go-winio v0.4.11 h1:zoIOcVf0xPN1tnMVbTtEdI+P8OofVk3NObnwOQ6nK2Q= github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw= @@ -51,8 +58,6 @@ github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= github.com/cockroachdb/cockroach-go v0.0.0-20181001143604-e0a95dfd547c h1:2zRrJWIt/f9c9HhNHAgrRgq0San5gRRUJTBXLkchal0= github.com/cockroachdb/cockroach-go v0.0.0-20181001143604-e0a95dfd547c/go.mod h1:XGLbWH/ujMcbPbhZq52Nv6UrCghb1yGn//133kEsvDk= -github.com/cockroachdb/cockroach-go v0.0.0-20190925194419-606b3d062051 h1:eApuUG8W2EtBVwxqLlY2wgoqDYOg3WvIHGvW4fUbbow= -github.com/cockroachdb/cockroach-go v0.0.0-20190925194419-606b3d062051/go.mod h1:XGLbWH/ujMcbPbhZq52Nv6UrCghb1yGn//133kEsvDk= github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd h1:qMd81Ts1T2OTKmB4acZcyKaMtRnY5Y44NuXGX2GFJ1w= github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= github.com/codegangsta/negroni v1.0.0/go.mod h1:v0y3T5G7Y1UlFfyxFn/QLRU4a2EuNau2iZY63YTKWo0= @@ -65,10 +70,8 @@ github.com/coreos/go-oidc v2.0.0+incompatible h1:+RStIopZ8wooMx+Vs5Bt8zMXxV1ABl5 github.com/coreos/go-oidc v2.0.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= -github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -207,8 +210,8 @@ github.com/gobuffalo/flect v0.0.0-20181210151238-24a2b68e0316/go.mod h1:en58vff7 github.com/gobuffalo/flect v0.0.0-20190104192022-4af577e09bf2/go.mod h1:en58vff74S9b99Eg42Dr+/9yPu437QjlNsO/hBYPuOk= github.com/gobuffalo/flect v0.0.0-20190117212819-a62e61d96794 h1:HZOs07hF3AmoaUj4HJQHV5RqfOuGnPZI7aFcireIrww= github.com/gobuffalo/flect v0.0.0-20190117212819-a62e61d96794/go.mod h1:397QT6v05LkZkn07oJXXT6y9FCfwC8Pug0WA2/2mE9k= +github.com/gobuffalo/flect v0.1.5 h1:xpKq9ap8MbYfhuPCF0dBH854Gp9CxZjr/IocxELFflo= github.com/gobuffalo/flect v0.1.5/go.mod h1:W3K3X9ksuZfir8f/LrfVtWmCDQFfayuylOJ7sz/Fj80= -github.com/gobuffalo/flect v0.1.6 h1:D7KWNRFiCknJKA495/e1BO7oxqf8tbieaLv/ehoZ/+g= github.com/gobuffalo/flect v0.1.6/go.mod h1:W3K3X9ksuZfir8f/LrfVtWmCDQFfayuylOJ7sz/Fj80= github.com/gobuffalo/flect v0.1.7 h1:qQqM2eGdM6tJX8yHKYBM0wVHBLjUT7Qs6uk5jnAhOwI= github.com/gobuffalo/flect v0.1.7/go.mod h1:W3K3X9ksuZfir8f/LrfVtWmCDQFfayuylOJ7sz/Fj80= @@ -236,11 +239,9 @@ github.com/gobuffalo/genny v0.0.0-20181211165820-e26c8466f14d/go.mod h1:sHnK+ZSU github.com/gobuffalo/genny v0.0.0-20190104222617-a71664fc38e7/go.mod h1:QPsQ1FnhEsiU8f+O0qKWXz2RE4TiDqLVChWkBuh1WaY= github.com/gobuffalo/genny v0.0.0-20190112155932-f31a84fcacf5 h1:boQS3dA9PxhyufJEWIILrG6pJQbDnpwP2rFyvWacdoY= github.com/gobuffalo/genny v0.0.0-20190112155932-f31a84fcacf5/go.mod h1:CIaHCrSIuJ4il6ka3Hub4DR4adDrGoXGEEt2FbBxoIo= -github.com/gobuffalo/genny v0.2.0 h1:4srOvuxvdxKsc6izHdHkS8KpQCtUHp6QZm5EUc5vksU= github.com/gobuffalo/genny v0.2.0/go.mod h1:rWs4Z12d1Zbf19rlsn0nurr75KqhYp52EAGGxTbBhNk= +github.com/gobuffalo/genny v0.3.0 h1:eHQpmPmH3ozcAIZ/krOTqQuDy3FxPMmI7pahFrDP3As= github.com/gobuffalo/genny v0.3.0/go.mod h1:ywJ2CoXrTZj7rbS8HTbzv7uybnLKlsNSBhEQ+yFI3E8= -github.com/gobuffalo/genny v0.4.1 h1:ylgRyFoVGtfq92Ziq0kyi0Sdwh//pqWEwg+vD3eK1ZA= -github.com/gobuffalo/genny v0.4.1/go.mod h1:dpded+KBgICFciAb+6R5Lo+1VxzofjqHgKqFYIL8M7U= github.com/gobuffalo/github_flavored_markdown v1.0.4/go.mod h1:uRowCdK+q8d/RF0Kt3/DSalaIXbb0De/dmTqMQdkQ4I= github.com/gobuffalo/github_flavored_markdown v1.0.5/go.mod h1:U0643QShPF+OF2tJvYNiYDLDGDuQmJZXsf/bHOJPsMY= github.com/gobuffalo/github_flavored_markdown v1.0.7 h1:Vjvz4wqOnviiLEfTh5bh270b3lhpJiwwQEWOWmHMwY8= @@ -248,8 +249,8 @@ github.com/gobuffalo/github_flavored_markdown v1.0.7/go.mod h1:w93Pd9Lz6LvyQXEG6 github.com/gobuffalo/github_flavored_markdown v1.1.0 h1:8Zzj4fTRl/OP2R7sGerzSf6g2nEJnaBEJe7UAOiEvbQ= github.com/gobuffalo/github_flavored_markdown v1.1.0/go.mod h1:TSpTKWcRTI0+v7W3x8dkSKMLJSUpuVitlptCkpeY8ic= github.com/gobuffalo/gogen v0.2.0/go.mod h1:V9QVDIxsgKNZs6L2IYiGR8datgMhB577vzTDqypH360= -github.com/gobuffalo/helpers v0.2.2 h1:AAu+rUINhzIii77xzuih26Q0ZJgOcLkf37FaBcv2diU= github.com/gobuffalo/helpers v0.2.2/go.mod h1:xYbzUdCUpVzLwLnqV8HIjT6hmG0Cs7YIBCJkNM597jw= +github.com/gobuffalo/helpers v0.2.4 h1:K5pXrMiSn8qJpMuv/q853i+WI2o47wS8ehD6NGF0hkE= github.com/gobuffalo/helpers v0.2.4/go.mod h1:NX7v27yxPDOPTgUFYmJ5ow37EbxdoLraucOGvMNawyk= github.com/gobuffalo/helpers v0.4.0 h1:DR/iYihrVCXv1cYeIGSK3EZz2CljO+DqDLQPWZAod9c= github.com/gobuffalo/helpers v0.4.0/go.mod h1:2q/ZnVxCehM4/y1bNz3+wXsvWvWUY+iTUr7mPC6QqGQ= @@ -273,14 +274,13 @@ github.com/gobuffalo/logger v0.0.0-20190315122211-86e12af44bc2/go.mod h1:QdxcLw5 github.com/gobuffalo/logger v1.0.0/go.mod h1:2zbswyIUa45I+c+FLXuWl9zSWEiVuthsk8ze5s8JvPs= github.com/gobuffalo/logger v1.0.1 h1:ZEgyRGgAm4ZAhAO45YXMs5Fp+bzGLESFewzAVBMKuTg= github.com/gobuffalo/logger v1.0.1/go.mod h1:2zbswyIUa45I+c+FLXuWl9zSWEiVuthsk8ze5s8JvPs= -github.com/gobuffalo/logger v1.0.3 h1:YaXOTHNPCvkqqA7w05A4v0k2tCdpr+sgFlgINbQ6gqc= -github.com/gobuffalo/logger v1.0.3/go.mod h1:SoeejUwldiS7ZsyCBphOGURmWdwUFXs0J7TCjEhjKxM= github.com/gobuffalo/makr v1.1.5 h1:lOlpv2iz0dNa4qse0ZYQgbtT+ybwVxWEAcOZbcPmeYc= github.com/gobuffalo/makr v1.1.5/go.mod h1:Y+o0btAH1kYAMDJW/TX3+oAXEu0bmSLLoC9mIFxtzOw= github.com/gobuffalo/mapi v1.0.0/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= github.com/gobuffalo/mapi v1.0.1 h1:JRuTiZzDEZhBHkFiHTxJkYRT6CbYuL0K/rn+1byJoEA= github.com/gobuffalo/mapi v1.0.1/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= github.com/gobuffalo/mapi v1.0.2/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= +github.com/gobuffalo/mapi v1.1.0 h1:VEhxtd2aoPXFqVmliLXGSmqPh541OprxYYZFwgNcjn4= github.com/gobuffalo/mapi v1.1.0/go.mod h1:pqQ1XAqvpy/JYtRwoieNps2yU8MFiMxBUpAm2FBtQ50= github.com/gobuffalo/meta v0.0.0-20181018155829-df62557efcd3/go.mod h1:XTTOhwMNryif3x9LkTTBO/Llrveezd71u3quLd0u7CM= github.com/gobuffalo/meta v0.0.0-20181018192820-8c6cef77dab3/go.mod h1:E94EPzx9NERGCY69UWlcj6Hipf2uK/vnfrF4QD0plVE= @@ -333,7 +333,6 @@ github.com/gobuffalo/packr/v2 v2.0.0-rc.15/go.mod h1:IMe7H2nJvcKXSF90y4X1rjYIRlN github.com/gobuffalo/packr/v2 v2.4.0/go.mod h1:ra341gygw9/61nSjAbfwcwh8IrYL4WmR4IsPkPBhQiY= github.com/gobuffalo/packr/v2 v2.5.2/go.mod h1:sgEE1xNZ6G0FNN5xn9pevVu4nywaxHvgup67xisti08= github.com/gobuffalo/packr/v2 v2.5.3/go.mod h1:sgEE1xNZ6G0FNN5xn9pevVu4nywaxHvgup67xisti08= -github.com/gobuffalo/packr/v2 v2.6.0/go.mod h1:sgEE1xNZ6G0FNN5xn9pevVu4nywaxHvgup67xisti08= github.com/gobuffalo/packr/v2 v2.7.1 h1:n3CIW5T17T8v4GGK5sWXLVWJhCz7b5aNLSxW6gYim4o= github.com/gobuffalo/packr/v2 v2.7.1/go.mod h1:qYEvAazPaVxy7Y7KR0W8qYEE+RymX74kETFqjFoFlOc= github.com/gobuffalo/plush v3.7.16+incompatible/go.mod h1:rQ4zdtUUyZNqULlc6bqd5scsPfLKfT0+TGMChgduDvI= @@ -345,7 +344,6 @@ github.com/gobuffalo/plush v3.7.30+incompatible/go.mod h1:rQ4zdtUUyZNqULlc6bqd5s github.com/gobuffalo/plush v3.7.31+incompatible/go.mod h1:rQ4zdtUUyZNqULlc6bqd5scsPfLKfT0+TGMChgduDvI= github.com/gobuffalo/plush v3.7.32+incompatible h1:sxY0WMa6J1pMuomTUQ8n2TlR3otiCVZaq21gSrHLScU= github.com/gobuffalo/plush v3.7.32+incompatible/go.mod h1:rQ4zdtUUyZNqULlc6bqd5scsPfLKfT0+TGMChgduDvI= -github.com/gobuffalo/plush v3.8.2+incompatible h1:EXtDf5L7TTwX8tEddtdHT+PT2lFerIKQm8Ye/M7O+54= github.com/gobuffalo/plush v3.8.2+incompatible/go.mod h1:rQ4zdtUUyZNqULlc6bqd5scsPfLKfT0+TGMChgduDvI= github.com/gobuffalo/plush v3.8.3+incompatible h1:kzvUTnFPhwyfPEsx7U7LI05/IIslZVGnAlMA1heWub8= github.com/gobuffalo/plush v3.8.3+incompatible/go.mod h1:rQ4zdtUUyZNqULlc6bqd5scsPfLKfT0+TGMChgduDvI= @@ -359,6 +357,8 @@ github.com/gobuffalo/pop v4.8.3+incompatible/go.mod h1:DwBz3SD5SsHpTZiTubcsFWcVD github.com/gobuffalo/pop v4.8.4+incompatible/go.mod h1:DwBz3SD5SsHpTZiTubcsFWcVDpJWGsxjVjMPnkiThWg= github.com/gobuffalo/pop v4.12.2+incompatible h1:WFHMzzHbVLulZnEium1VlYRnWkzHz39FzVLov6rZdDI= github.com/gobuffalo/pop v4.12.2+incompatible/go.mod h1:DwBz3SD5SsHpTZiTubcsFWcVDpJWGsxjVjMPnkiThWg= +github.com/gobuffalo/pop v4.13.0+incompatible h1:sE2C/dmCENwLhSfXyFXk3re4dzBbSkH/Ut62g6N1zDw= +github.com/gobuffalo/pop v4.13.0+incompatible/go.mod h1:DwBz3SD5SsHpTZiTubcsFWcVDpJWGsxjVjMPnkiThWg= github.com/gobuffalo/release v1.0.35/go.mod h1:VtHFAKs61vO3wboCec5xr9JPTjYyWYcvaM3lclkc4x4= github.com/gobuffalo/release v1.0.38/go.mod h1:VtHFAKs61vO3wboCec5xr9JPTjYyWYcvaM3lclkc4x4= github.com/gobuffalo/release v1.0.42/go.mod h1:RPs7EtafH4oylgetOJpGP0yCZZUiO4vqHfTHJjSdpug= @@ -416,6 +416,7 @@ github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-github/v27 v27.0.1 h1:sSMFSShNn4VnqCqs+qhab6TS3uQc+uVR6TD1bW6MavM= github.com/google/go-github/v27 v27.0.1/go.mod h1:/0Gr8pJ55COkmv+S/yPKCczSkUPIM/LnFyubufRNIS0= @@ -460,45 +461,16 @@ github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/huandu/xstrings v1.2.0 h1:yPeWdRnmynF7p+lLYz0H2tthW9lqhMJrQV/U7yy4wX0= +github.com/huandu/xstrings v1.2.0/go.mod h1:DvyZB1rfVYsBIigL8HwpZgxHwXozlTgGqn63UyNX5k4= github.com/imdario/mergo v0.3.7 h1:Y+UAYTZ7gDEuOfhxKWy+dvb5dRQ6rJjFSdX2HZY1/gI= github.com/imdario/mergo v0.3.7/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/jackc/chunkreader v1.0.0 h1:4s39bBR8ByfqH+DKm8rQA3E1LHZWB9XWcrz8fqaZbe0= -github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= -github.com/jackc/chunkreader/v2 v2.0.0 h1:DUwgMQuuPnS0rhMXenUtZpqZqrR/30NWY+qQvTpSvEs= -github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= github.com/jackc/fake v0.0.0-20150926172116-812a484cc733 h1:vr3AYkKovP8uR8AvSGGUK1IDqRa5lAAvEkZG1LKaCRc= github.com/jackc/fake v0.0.0-20150926172116-812a484cc733/go.mod h1:WrMFNQdiFJ80sQsxDoMokWK1W5TQtxBFNpzWTD84ibQ= -github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA= -github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE= -github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s= -github.com/jackc/pgconn v1.1.0 h1:10i6DMVJOSko/sD3FLpFKBHONzDGKkX8pbLyHC8B92o= -github.com/jackc/pgconn v1.1.0/go.mod h1:GgY/Lbj1VonNaVdNUHs9AwWom3yP2eymFQ1C8z9r/Lk= -github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE= -github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8= -github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2 h1:JVX6jT/XfzNqIjye4717ITLaNwV9mWbJx0dLCpcRzdA= -github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE= -github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= -github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= -github.com/jackc/pgproto3 v1.1.0 h1:FYYE4yRw+AgI8wXIinMlNjBbp/UitDJwfj5LqqewP1A= -github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78= -github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA= -github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg= -github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= -github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= -github.com/jackc/pgproto3/v2 v2.0.0 h1:FApgMJ/GtaXfI0s8Lvd0kaLaRwMOhs4VH92pwkwQQvU= -github.com/jackc/pgproto3/v2 v2.0.0/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= -github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg= -github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc= -github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw= github.com/jackc/pgx v3.2.0+incompatible h1:0Vihzu20St42/UDsvZGdNE6jak7oi/UOeMzwMPHkgFY= github.com/jackc/pgx v3.2.0+incompatible/go.mod h1:0ZGrqGqkRlliWnWB4zKnWtjbSWbGkVEFm4TeybAXq+I= -github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y= -github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM= -github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc= -github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= -github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= @@ -536,16 +508,12 @@ github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/leodido/go-urn v1.1.0 h1:Sm1gr51B1kKyfD2BlRcLSiEkffoG96g6TPv6eRoEiB8= github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw= github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0= -github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/luna-duclos/instrumentedsql v0.0.0-20181127104832-b7d587d28109 h1:SSbnT1UH/TdSedRIy8XVB1dsVUOFP8iHaa/+QE0/q2k= github.com/luna-duclos/instrumentedsql v0.0.0-20181127104832-b7d587d28109/go.mod h1:PWUIzhtavmOR965zfawVsHXbEuU1G29BPZ/CB3C7jXk= github.com/luna-duclos/instrumentedsql v1.1.1 h1:lfhaSOlvuJUV+UDE7Rwsb1yPLRZOJrbV9CfB2uWiXhc= @@ -572,6 +540,8 @@ github.com/markbates/oncer v0.0.0-20180924034138-723ad0170a46/go.mod h1:Ld9puTsI github.com/markbates/oncer v0.0.0-20181014194634-05fccaae8fc4/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2 h1:JgVTCPf0uBVcUSWpyXmGpgOc62nK5HWUBKAGc3Qqa5k= github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= +github.com/markbates/pkger v0.12.8 h1:4mEUzWb1HzRnxPwUevBX8g8ntsQ4rWw2R8CRB2QdZVI= +github.com/markbates/pkger v0.12.8/go.mod h1:C7e5A6bnWZT+nXkUwkvysGW7sxl/IGd63HEa6N/JY8s= github.com/markbates/refresh v1.4.10/go.mod h1:NDPHvotuZmTmesXxr95C9bjlw1/0frJwtME2dzcVKhc= github.com/markbates/safe v1.0.0/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= github.com/markbates/safe v1.0.1 h1:yjZkbvRM6IzKj9tlu/zMJLS0n/V351OZWRnF3QfaUxI= @@ -580,16 +550,8 @@ github.com/markbates/sigtx v1.0.0/go.mod h1:QF1Hv6Ic6Ca6W+T+DL0Y/ypborFKyvUY9Hmu github.com/markbates/willie v1.0.9/go.mod h1:fsrFVWl91+gXpx/6dv715j7i11fYPfZ9ZGfH0DQzY7w= github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= -github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= -github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA= -github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.10 h1:qxFzApOv4WsAL965uUPIsXzAKCZxN2p9UqdhFS4ZW10= -github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-sqlite3 v1.10.0 h1:jbhqpg7tQe4SupckyijYiy0mJJ/pRyHvXf7JdWK860o= github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= @@ -605,12 +567,16 @@ github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5 github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= github.com/microcosm-cc/bluemonday v1.0.2 h1:5lPfLTTAvAbtS0VqT+94yOtFnGfUWYyx0+iToC3Os3s= github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc= +github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ= +github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v1.0.0/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY= +github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= github.com/monoculum/formam v0.0.0-20180901015400-4e68be1d79ba/go.mod h1:RKgILGEJq24YyJ2ban8EO0RUVSJlF1pGsEvoLEACr/Q= github.com/moul/http2curl v0.0.0-20170919181001-9ac6cf4d929b/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ= @@ -623,7 +589,6 @@ github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.9.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.10.1 h1:q/mM8GF/n0shIN8SaAZ0V+jnLPzen6WIVZdiwrRlMlo= github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.4.2/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= @@ -631,7 +596,6 @@ github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.6.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.7.0 h1:XPnZz8VVBHjVsy1vzJmRwIcSwiUO+JFfrv/xGiigmME= github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ= github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= @@ -704,9 +668,6 @@ github.com/rogpeppe/go-internal v1.4.0/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTE github.com/rogpeppe/go-internal v1.5.0 h1:Usqs0/lDK/NqTkvrmKSwA/3XkZAs7ZAW/eLeQ2MVBTw= github.com/rogpeppe/go-internal v1.5.0/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rs/cors v1.6.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= -github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= -github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= -github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= github.com/rubenv/sql-migrate v0.0.0-20190212093014-1007f53448d7 h1:ID2fzWzRFJcF/xf/8eLN9GW5CXb6NQnKfC+ksTwMNpY= github.com/rubenv/sql-migrate v0.0.0-20190212093014-1007f53448d7/go.mod h1:WS0rl9eEliYI8DPnr3TOwz4439pay+qNgzJoVya/DmY= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= @@ -724,6 +685,7 @@ github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24 h1:pntxY8Ary0t43dCZ5dqY4YTJCObLY1kIXl0uzMv+7DE= github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= +github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e h1:MZM7FHLqUHYI0Y/mQAt3d2aYa0SiNms/hFqC9qJYolM= github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ= github.com/shurcooL/highlight_diff v0.0.0-20170515013008-09bb4053de1b/go.mod h1:ZpfEhSmds4ytuByIcDnOLkTHGUI6KNqRNPDLHDk+mUU= @@ -735,7 +697,6 @@ github.com/sirupsen/logrus v1.1.0/go.mod h1:zrgwTnHtNr00buQ1vSptGe8m1f/BbgsPukg8 github.com/sirupsen/logrus v1.1.1/go.mod h1:zrgwTnHtNr00buQ1vSptGe8m1f/BbgsPukg8qsT7A+A= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.3.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= @@ -774,7 +735,6 @@ github.com/sqs/goreturns v0.0.0-20181028201513-538ac6014518 h1:iD+PFTQwKEmbwSdwf github.com/sqs/goreturns v0.0.0-20181028201513-538ac6014518/go.mod h1:CKI4AZ4XmGV240rTHfO0hfE83S6/a3/Q1siZJ/vXf7A= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= @@ -815,7 +775,6 @@ github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c h1:3lbZUMbMiGUW/LMkfsEABsc5zNT9+b1CvsJx47JzJ8g= github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c/go.mod h1:UrdRz5enIKZ63MEE3IF9l2/ebyx59GyGgPi+tICQdmM= -github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs= github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= @@ -823,11 +782,10 @@ go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0 h1:C9hSCOW830chIVkdja34wa6Ky+IzWllkUinR+BtRZd4= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.1/go.mod h1:Ap50jQcDJrx6rB6VgeeFPtuPIf3wMRvRfrfYDO6+BmA= -go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= -go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= golang.org/x/crypto v0.0.0-20180830192347-182538f80094/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -845,13 +803,12 @@ golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnf golang.org/x/crypto v0.0.0-20190102171810-8d7daa0c54b3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190103213133-ff983b9c42bc/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 h1:HuIa8hRrWRSrqYzx1qI49NNxhdi2PrY7gxVSq1JjLDc= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191122220453-ac88ee75c92c h1:/nJuwDLoL/zrqY6gf57vxC+Pi+pZ8bfhpPkicO5H7W4= golang.org/x/crypto v0.0.0-20191122220453-ac88ee75c92c/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -891,9 +848,10 @@ golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190628185345-da137c7871d7 h1:rTIdg5QFRR7XCaK4LCjBiPbx8j4DQRpdYMnGn/bJUEU= golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191125084936-ffdde1057850 h1:Vq85/r8R9IdcUHmZ0/nQlUg1v15rzvQ2sHdnZAj/x7s= golang.org/x/net v0.0.0-20191125084936-ffdde1057850/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553 h1:efeOvDhwQ29Dj3SdAV/MJf8oukgn+8D8WgaCaRMchF8= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181003184128-c57b0facaced/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -930,7 +888,6 @@ golang.org/x/sys v0.0.0-20181206074257-70b957f3b65e/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190102155601-82a175fd1598/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190116161447-11f53e031339/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -940,12 +897,8 @@ golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190515120540-06a5c4944438/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191105231009-c1f44814a5cd h1:3x5uuvBgE6oaXJjCOvpCC1IpgJogqQ+PqGGU3ZxAgII= golang.org/x/sys v0.0.0-20191105231009-c1f44814a5cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e h1:N7DeIrjYszNmSW409R3frPPwglRwMkXSBzwVbkOjLLA= -golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9 h1:ZBzSG/7F4eNKz2L3GE9o300RX0Az1Bw5HF7PDraD+qU= golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -985,7 +938,6 @@ golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3 golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= @@ -995,17 +947,11 @@ golang.org/x/tools v0.0.0-20190624190245-7f2218787638/go.mod h1:/rFqwRUd4F7ZHNgw golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190711191110-9a621aea19f8 h1:VZick+NwcqlXXVsD1iFr4Wo6F1FgBbnM4AOMzhwKQ7w= golang.org/x/tools v0.0.0-20190711191110-9a621aea19f8/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI= -golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191004055002-72853e10c5a3/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191105231337-689d0f08e67a h1:RzzIfXstYPS78k0QViPGpDcTlV+QuYrbxVmsxDHdxTs= golang.org/x/tools v0.0.0-20191105231337-689d0f08e67a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 h1:9zdDQZ7Thm29KFXgAX/+yaf3eVbP7djjWp/dXAppNCc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 h1:/atklqdjdhuosWIl6AIbOeHJjicWYPqR9bpxqxYG2pA= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0 h1:9sdfJOzWlkqPltHAuzT2Cp+yrBeY1KRVYgms8soxMwM= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= @@ -1037,6 +983,7 @@ google.golang.org/grpc v1.22.0 h1:J0UbZOIrCAl+fpTOf8YLs4dJo8L/owV4LYVtAXQoPkw= google.golang.org/grpc v1.22.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk= gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= @@ -1049,10 +996,10 @@ gopkg.in/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXa gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= gopkg.in/go-playground/validator.v9 v9.28.0 h1:6pzvnzx1RWaaQiAmv6e1DvCFULRaz5cKoP5j1VcrLsc= gopkg.in/go-playground/validator.v9 v9.28.0/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ= +gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE= gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw= gopkg.in/gorp.v1 v1.7.2 h1:j3DWlAyGVv8whO7AcIWznQ2Yj7yJkn34B8s63GViAAw= gopkg.in/gorp.v1 v1.7.2/go.mod h1:Wo3h+DBQZIxATwftsglhdD/62zRFPhGhTiu5jUJmCaw= -gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= gopkg.in/mail.v2 v2.0.0-20180731213649-a0242b2233b4/go.mod h1:htwXN1Qh09vZJ1NVKxQqHPBaCBbzKhp5GzuJEA4VJWw= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/square/go-jose.v2 v2.1.9/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= @@ -1066,8 +1013,8 @@ gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo= -gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5 h1:ymVxjfMaHvXD8RqPRmzHHsB3VvucivSkIAvJFDI5O3c= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/main.go b/main.go index 62a2dfecdc7..871782eb4fe 100644 --- a/main.go +++ b/main.go @@ -15,8 +15,13 @@ // package main is the entry point for kratos. package main -import "github.com/ory/kratos/cmd" +import ( + "github.com/markbates/pkger" + + "github.com/ory/kratos/cmd" +) func main() { + pkger.Include("/courier/template/templates") cmd.Execute() } diff --git a/notify/mailman.go b/notify/mailman.go deleted file mode 100644 index edf23930534..00000000000 --- a/notify/mailman.go +++ /dev/null @@ -1,5 +0,0 @@ -package notify - -type Mailman interface { - Enqueue(to string, template Template, args ...interface{}) error -} diff --git a/notify/templates.go b/notify/templates.go deleted file mode 100644 index 5b6c1dc271d..00000000000 --- a/notify/templates.go +++ /dev/null @@ -1,7 +0,0 @@ -package notify - -type Template int - -const ( - TemplateEmailPasswordlessAuthentication Template = iota -) diff --git a/persistence/reference.go b/persistence/reference.go index e433fe097e7..0a58c0900c8 100644 --- a/persistence/reference.go +++ b/persistence/reference.go @@ -3,6 +3,7 @@ package persistence import ( "context" + "github.com/ory/kratos/courier" "github.com/ory/kratos/identity" "github.com/ory/kratos/selfservice/errorx" "github.com/ory/kratos/selfservice/flow/login" @@ -20,6 +21,7 @@ type Persister interface { registration.RequestPersister login.RequestPersister profile.RequestPersister + courier.Persister session.Persister errorx.Persister diff --git a/persistence/sql/persister.go b/persistence/sql/persister.go index cc2c8ab623b..e95eaa76523 100644 --- a/persistence/sql/persister.go +++ b/persistence/sql/persister.go @@ -2,9 +2,7 @@ package sql import ( "context" - "time" - "github.com/cenkalti/backoff" "github.com/gobuffalo/packr" "github.com/gobuffalo/pop" "github.com/pkg/errors" @@ -31,17 +29,6 @@ type ( } ) -func RetryConnect(dsn string) (c *pop.Connection, err error) { - bc := backoff.NewExponentialBackOff() - bc.MaxElapsedTime = time.Minute * 5 - bc.Reset() - - return c, backoff.Retry(func() (err error) { - c, err = pop.Connect(dsn) - return errors.WithStack(err) - }, bc) -} - func NewPersister(r persisterDependencies, conf configuration.Provider, c *pop.Connection) (*Persister, error) { m, err := pop.NewMigrationBox(migrations, c) if err != nil { diff --git a/persistence/sql/persister_courier.go b/persistence/sql/persister_courier.go new file mode 100644 index 00000000000..74106c7fa66 --- /dev/null +++ b/persistence/sql/persister_courier.go @@ -0,0 +1,52 @@ +package sql + +import ( + "context" + "database/sql" + + "github.com/gofrs/uuid" + "github.com/pkg/errors" + + "github.com/ory/x/sqlcon" + + "github.com/ory/kratos/courier" +) + +var _ courier.Persister = new(Persister) + +func (p *Persister) AddMessage(ctx context.Context, m *courier.Message) error { + m.Status = courier.MessageStatusQueued + return sqlcon.HandleError(p.c.Create(m)) // do not create eager to avoid identity injection. +} + +func (p *Persister) NextMessages(ctx context.Context, limit uint8) ([]courier.Message, error) { + var m []courier.Message + if err := p.c. + Eager(). + Where("status != ?", courier.MessageStatusSent). + Order("created_at ASC").Limit(int(limit)).All(&m); err != nil { + if errors.Cause(err) == sql.ErrNoRows { + return nil, courier.ErrQueueEmpty + } + return nil, sqlcon.HandleError(err) + } + + if len(m) == 0 { + return nil, courier.ErrQueueEmpty + } + + return m, nil +} + +func (p *Persister) SetMessageStatus(ctx context.Context, id uuid.UUID, ms courier.MessageStatus) error { + count, err := p.c.RawQuery("UPDATE courier_messages SET status = ? WHERE id = ?", ms, id).ExecWithCount() + if err != nil { + return sqlcon.HandleError(err) + } + + if count == 0 { + return errors.WithStack(sqlcon.ErrNoRows) + } + + return nil +} diff --git a/persistence/sql/persister_test.go b/persistence/sql/persister_test.go index fd57b6068e1..3be72c4fc2b 100644 --- a/persistence/sql/persister_test.go +++ b/persistence/sql/persister_test.go @@ -18,6 +18,7 @@ import ( // "github.com/ory/x/sqlcon/dockertest" "github.com/stretchr/testify/require" + "github.com/ory/kratos/courier" "github.com/ory/kratos/identity" "github.com/ory/kratos/internal" "github.com/ory/kratos/selfservice/flow/login" @@ -31,7 +32,7 @@ var sqlite = fmt.Sprintf("sqlite3://%s.sqlite?_fk=true&mode=rwc", filepath.Join( func init() { internal.RegisterFakes() - // pop.Debug = true + // op.Debug = true } // nolint:staticcheck @@ -107,7 +108,7 @@ func TestPersister(t *testing.T) { _, reg := internal.NewRegistryDefaultWithDSN(t, dsn) p := reg.Persister() - // pop.SetLogger(pl(t)) + pop.SetLogger(pl(t)) require.NoError(t, p.MigrationStatus(context.Background())) require.NoError(t, p.MigrateUp(context.Background())) @@ -131,8 +132,12 @@ func TestPersister(t *testing.T) { pop.SetLogger(pl(t)) session.TestPersister(p)(t) }) + t.Run("contract=session.TestRequestPersister", func(t *testing.T) { + pop.SetLogger(pl(t)) + courier.TestPersister(p)(t) + }) }) + t.Logf("DSN: %s", dsn) } - } diff --git a/selfservice/flow/login/error.go b/selfservice/flow/login/error.go index 8ccd3d0aa09..4aeeba2f897 100644 --- a/selfservice/flow/login/error.go +++ b/selfservice/flow/login/error.go @@ -36,8 +36,8 @@ type ( ErrorHandlerProvider interface{ LoginRequestErrorHandler() *ErrorHandler } ErrorHandler struct { - d errorHandlerDependencies - c configuration.Provider + d errorHandlerDependencies + c configuration.Provider } ) diff --git a/selfservice/flow/profile/error.go b/selfservice/flow/profile/error.go index 68b4e0d4385..df4b13f22b6 100644 --- a/selfservice/flow/profile/error.go +++ b/selfservice/flow/profile/error.go @@ -32,8 +32,8 @@ type ( ErrorHandlerProvider interface{ ProfileRequestRequestErrorHandler() *ErrorHandler } ErrorHandler struct { - d errorHandlerDependencies - c configuration.Provider + d errorHandlerDependencies + c configuration.Provider } ) diff --git a/selfservice/flow/registration/error.go b/selfservice/flow/registration/error.go index e2c0c736b0b..c1b9cb06a92 100644 --- a/selfservice/flow/registration/error.go +++ b/selfservice/flow/registration/error.go @@ -36,8 +36,8 @@ type ( ErrorHandlerProvider interface{ RegistrationRequestErrorHandler() *ErrorHandler } ErrorHandler struct { - d errorHandlerDependencies - c configuration.Provider + d errorHandlerDependencies + c configuration.Provider } ) diff --git a/selfservice/strategy/oidc/strategy.go b/selfservice/strategy/oidc/strategy.go index 8323bb06780..1e3756c6349 100644 --- a/selfservice/strategy/oidc/strategy.go +++ b/selfservice/strategy/oidc/strategy.go @@ -83,8 +83,8 @@ type dependencies interface { // Strategy implements selfservice.LoginStrategy, selfservice.RegistrationStrategy. It supports both login // and registration via OpenID Providers. type Strategy struct { - c configuration.Provider - d dependencies + c configuration.Provider + d dependencies validator *schema.Validator cg form.CSRFGenerator }