Skip to content

Commit

Permalink
feat: cancelable web hooks
Browse files Browse the repository at this point in the history
Introduces the ability to cancel web hooks by calling `error "cancel"` in JsonNet.
  • Loading branch information
aeneasr committed Feb 27, 2022
1 parent 4918ec4 commit 44a5323
Show file tree
Hide file tree
Showing 7 changed files with 58 additions and 10 deletions.
9 changes: 5 additions & 4 deletions persistence/sql/persister_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,16 @@ package sql_test
import (
"context"
"fmt"
"github.com/ory/kratos/driver/config"
"github.com/ory/kratos/schema"
"github.com/ory/x/sqlxx"
"github.com/ory/x/urlx"
"os"
"path/filepath"
"sync"
"testing"

"github.com/ory/kratos/driver/config"
"github.com/ory/kratos/schema"
"github.com/ory/x/sqlxx"
"github.com/ory/x/urlx"

"github.com/ory/kratos/x/xsql"

"github.com/go-errors/errors"
Expand Down
15 changes: 12 additions & 3 deletions request/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,23 @@ package request
import (
"bytes"
"encoding/json"
"errors"
"io"
"net/http"
"net/url"
"reflect"
"strings"

"github.com/pkg/errors"

"github.com/google/go-jsonnet"
"github.com/hashicorp/go-retryablehttp"

"github.com/ory/x/fetcher"
"github.com/ory/x/logrusx"
)

var ErrCancel = errors.New("request cancel by JsonNet")

const (
ContentTypeForm = "application/x-www-form-urlencoded"
ContentTypeJSON = "application/json"
Expand Down Expand Up @@ -100,15 +103,21 @@ func (b *Builder) addJSONBody(template *bytes.Buffer, body interface{}) error {
enc.SetIndent("", "")

if err := enc.Encode(body); err != nil {
return err
return errors.WithStack(err)
}

vm := jsonnet.MakeVM()
vm.TLACode("ctx", buf.String())

res, err := vm.EvaluateAnonymousSnippet(b.conf.TemplateURI, template.String())
if err != nil {
return err
// Unfortunately we can not use errors.As / errors.Is, see:
// https://github.com/google/go-jsonnet/issues/592
if strings.Contains(err.Error(), (&jsonnet.RuntimeError{Msg: "cancel"}).Error()) {
return errors.WithStack(ErrCancel)
}

return errors.WithStack(err)
}

rb := strings.NewReader(res)
Expand Down
14 changes: 14 additions & 0 deletions request/builder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,20 @@ func TestBuildRequest(t *testing.T) {
}
})
}

t.Run("cancel request", func(t *testing.T) {
l := logrusx.New("kratos", "test")

rb, err := NewBuilder(json.RawMessage(`{
"url": "https://test.kratos.ory.sh/my_endpoint6",
"method": "POST",
"body": "file://./stub/cancel_body.jsonnet"
}`), nil, l)
require.NoError(t, err)

_, err = rb.BuildRequest(json.RawMessage(`{}`))
require.ErrorIs(t, err, ErrCancel)
})
}

func mustContainHeader(t *testing.T, expected http.Header, actual http.Header) {
Expand Down
1 change: 1 addition & 0 deletions request/stub/cancel_body.jsonnet
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
error "cancel"
1 change: 1 addition & 0 deletions selfservice/hook/stub/cancel_template.jsonnet
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
error "cancel"
6 changes: 5 additions & 1 deletion selfservice/hook/web_hook.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import (
"fmt"
"net/http"

"github.com/pkg/errors"

"github.com/ory/kratos/identity"
"github.com/ory/kratos/request"
"github.com/ory/kratos/selfservice/flow"
Expand Down Expand Up @@ -121,7 +123,9 @@ func (e *WebHook) execute(ctx context.Context, data *templateContext) error {
}

req, err := builder.BuildRequest(data)
if err != nil {
if errors.Is(err, request.ErrCancel) {
return nil
} else if err != nil {
return err
}

Expand Down
22 changes: 20 additions & 2 deletions selfservice/hook/web_hook_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,7 @@ func TestWebHooks(t *testing.T) {
})
}

t.Run("Must error when config is erroneous", func(t *testing.T) {
t.Run("must error when config is erroneous", func(t *testing.T) {
req := &http.Request{
Header: map[string][]string{"Some-Header": {"Some-Value"}},
RequestURI: "https://www.ory.sh/some_end_point",
Expand All @@ -307,7 +307,7 @@ func TestWebHooks(t *testing.T) {
assert.Error(t, err)
})

t.Run("Must error when template is erroneous", func(t *testing.T) {
t.Run("must error when template is erroneous", func(t *testing.T) {
ts := newServer(webHookHttpCodeEndPoint(200))
req := &http.Request{
Header: map[string][]string{"Some-Header": {"Some-Value"}},
Expand All @@ -326,6 +326,24 @@ func TestWebHooks(t *testing.T) {
assert.Error(t, err)
})

t.Run("must not make request", func(t *testing.T) {
req := &http.Request{
Header: map[string][]string{"Some-Header": {"Some-Value"}},
RequestURI: "https://www.ory.sh/some_end_point",
Method: http.MethodPost,
}
f := &login.Flow{ID: x.NewUUID()}
conf := json.RawMessage(`{
"url": "https://i-do-not-exist/",
"method": "POST",
"body": "./stub/cancel_template.jsonnet"
}`)
wh := hook.NewWebHook(reg, conf)

err := wh.ExecuteLoginPreHook(nil, req, f)
assert.NoError(t, err)
})

boolToString := func(f bool) string {
if f {
return " not"
Expand Down

0 comments on commit 44a5323

Please sign in to comment.