Skip to content

Commit b162897

Browse files
authored
feat: enable JSONNet templating for password migration hook (#4390)
This enables JSONNet body templating for the password migration hook. There is also a significant refactoring of some internals around webhook config handling.
1 parent ea4da51 commit b162897

File tree

113 files changed

+1515
-1307
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

113 files changed

+1515
-1307
lines changed

cmd/daemon/serve.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import (
99
"net/http"
1010
"time"
1111

12+
"github.com/ory/kratos/x/nosurfx"
13+
1214
"github.com/pkg/errors"
1315
"github.com/rs/cors"
1416
"github.com/spf13/cobra"
@@ -97,7 +99,7 @@ func servePublic(ctx context.Context, r driver.Registry, cmd *cobra.Command, slO
9799
n.Use(r.PrometheusManager())
98100

99101
router := x.NewRouterPublic()
100-
csrf := x.NewCSRFHandler(router, r)
102+
csrf := nosurfx.NewCSRFHandler(router, r)
101103

102104
// we need to always load the CORS middleware even if it is disabled, to allow hot-enabling CORS
103105
n.UseFunc(func(w http.ResponseWriter, req *http.Request, next http.HandlerFunc) {

courier/courier_dispatcher.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ func (c *courier) channels(ctx context.Context, id string) (Channel, error) {
3131
}
3232
return courierChannel, nil
3333
case "http":
34-
return newHttpChannel(channel.ID, channel.RequestConfig, c.deps), nil
34+
return newHttpChannel(channel.ID, &channel.RequestConfig, c.deps), nil
3535
default:
3636
return nil, errors.Errorf("unknown courier channel type: %s", channel.Type)
3737
}

courier/handler.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ import (
77
"fmt"
88
"net/http"
99

10+
"github.com/ory/kratos/x/nosurfx"
11+
"github.com/ory/kratos/x/redir"
12+
1013
"github.com/gofrs/uuid"
1114

1215
"github.com/ory/herodot"
@@ -29,7 +32,7 @@ type (
2932
handlerDependencies interface {
3033
x.WriterProvider
3134
x.LoggingProvider
32-
x.CSRFProvider
35+
nosurfx.CSRFProvider
3336
PersistenceProvider
3437
config.Provider
3538
}
@@ -47,8 +50,8 @@ func NewHandler(r handlerDependencies) *Handler {
4750

4851
func (h *Handler) RegisterPublicRoutes(public *x.RouterPublic) {
4952
h.r.CSRFHandler().IgnoreGlobs(x.AdminPrefix+AdminRouteListMessages, AdminRouteListMessages)
50-
public.GET(x.AdminPrefix+AdminRouteListMessages, x.RedirectToAdminRoute(h.r))
51-
public.GET(x.AdminPrefix+AdminRouteGetMessage, x.RedirectToAdminRoute(h.r))
53+
public.GET(x.AdminPrefix+AdminRouteListMessages, redir.RedirectToAdminRoute(h.r))
54+
public.GET(x.AdminPrefix+AdminRouteGetMessage, redir.RedirectToAdminRoute(h.r))
5255
}
5356

5457
func (h *Handler) RegisterAdminRoutes(admin *x.RouterAdmin) {

courier/http_channel.go

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,8 @@ package courier
55

66
import (
77
"context"
8-
"encoding/json"
98
"fmt"
109

11-
"github.com/tidwall/gjson"
12-
1310
"github.com/pkg/errors"
1411

1512
"github.com/ory/kratos/courier/template"
@@ -22,7 +19,7 @@ import (
2219
type (
2320
httpChannel struct {
2421
id string
25-
requestConfig json.RawMessage
22+
requestConfig *request.Config
2623
d channelDependencies
2724
}
2825
channelDependencies interface {
@@ -36,7 +33,7 @@ type (
3633

3734
var _ Channel = new(httpChannel)
3835

39-
func newHttpChannel(id string, requestConfig json.RawMessage, d channelDependencies) *httpChannel {
36+
func newHttpChannel(id string, requestConfig *request.Config, d channelDependencies) *httpChannel {
4037
return &httpChannel{
4138
id: id,
4239
requestConfig: requestConfig,
@@ -96,7 +93,7 @@ func (c *httpChannel) Dispatch(ctx context.Context, msg Message) (err error) {
9693
}
9794

9895
logger := c.d.Logger().
99-
WithField("http_server", gjson.GetBytes(c.requestConfig, "url").String()).
96+
WithField("http_server", c.requestConfig.URL).
10097
WithField("message_id", msg.ID).
10198
WithField("message_nid", msg.NID).
10299
WithField("message_type", msg.Type).

driver/config/config.go

Lines changed: 12 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -18,30 +18,29 @@ import (
1818
"testing"
1919
"time"
2020

21-
"go.opentelemetry.io/otel/trace/noop"
22-
23-
"github.com/ory/x/crdbx"
24-
"github.com/ory/x/pointerx"
25-
2621
"github.com/go-webauthn/webauthn/protocol"
2722
"github.com/go-webauthn/webauthn/webauthn"
2823
"github.com/gofrs/uuid"
2924
"github.com/inhies/go-bytesize"
3025
"github.com/pkg/errors"
3126
"github.com/rs/cors"
3227
"github.com/stretchr/testify/require"
28+
"go.opentelemetry.io/otel/trace/noop"
3329
"golang.org/x/net/publicsuffix"
3430

3531
"github.com/ory/herodot"
3632
"github.com/ory/jsonschema/v3"
3733
"github.com/ory/jsonschema/v3/httploader"
3834
"github.com/ory/kratos/embedx"
35+
"github.com/ory/kratos/request"
3936
"github.com/ory/x/configx"
4037
"github.com/ory/x/contextx"
38+
"github.com/ory/x/crdbx"
4139
"github.com/ory/x/httpx"
4240
"github.com/ory/x/jsonschemax"
4341
"github.com/ory/x/logrusx"
4442
"github.com/ory/x/otelx"
43+
"github.com/ory/x/pointerx"
4544
"github.com/ory/x/stringsx"
4645
"github.com/ory/x/tlsx"
4746
"github.com/ory/x/watcherx"
@@ -286,11 +285,10 @@ type (
286285
PlainText string `json:"plaintext"`
287286
}
288287
CourierChannel struct {
289-
ID string `json:"id" koanf:"id"`
290-
Type string `json:"type" koanf:"type"`
291-
SMTPConfig *SMTPConfig `json:"smtp_config" koanf:"smtp_config"`
292-
RequestConfig json.RawMessage `json:"request_config" koanf:"-"`
293-
RequestConfigRaw map[string]any `json:"-" koanf:"request_config"`
288+
ID string `json:"id" koanf:"id"`
289+
Type string `json:"type" koanf:"type"`
290+
SMTPConfig *SMTPConfig `json:"smtp_config" koanf:"smtp_config"`
291+
RequestConfig request.Config `json:"request_config" koanf:"request_config"`
294292
}
295293
SMTPConfig struct {
296294
ConnectionURI string `json:"connection_uri" koanf:"connection_uri"`
@@ -302,8 +300,8 @@ type (
302300
LocalName string `json:"local_name" koanf:"local_name"`
303301
}
304302
PasswordMigrationHook struct {
305-
Enabled bool `json:"enabled" koanf:"enabled"`
306-
Config json.RawMessage `json:"config" koanf:"config"`
303+
Enabled bool `json:"enabled" koanf:"enabled"`
304+
Config request.Config `json:"config" koanf:"config"`
307305
}
308306
Config struct {
309307
l *logrusx.Logger
@@ -1238,17 +1236,6 @@ func (p *Config) CourierChannels(ctx context.Context) (ccs []*CourierChannel, _
12381236
if err := p.GetProvider(ctx).Koanf.Unmarshal(ViperKeyCourierChannels, &ccs); err != nil {
12391237
return nil, errors.WithStack(err)
12401238
}
1241-
if len(ccs) != 0 {
1242-
for _, c := range ccs {
1243-
if c.RequestConfigRaw != nil {
1244-
var err error
1245-
c.RequestConfig, err = json.Marshal(c.RequestConfigRaw)
1246-
if err != nil {
1247-
return nil, errors.WithStack(err)
1248-
}
1249-
}
1250-
}
1251-
}
12521239

12531240
// load legacy configs
12541241
channel := CourierChannel{
@@ -1260,9 +1247,7 @@ func (p *Config) CourierChannels(ctx context.Context) (ccs []*CourierChannel, _
12601247
return nil, errors.WithStack(err)
12611248
}
12621249
} else {
1263-
var err error
1264-
channel.RequestConfig, err = json.Marshal(p.GetProvider(ctx).Get(ViperKeyCourierHTTPRequestConfig))
1265-
if err != nil {
1250+
if err := p.GetProvider(ctx).Koanf.Unmarshal(ViperKeyCourierHTTPRequestConfig, &channel.RequestConfig); err != nil {
12661251
return nil, errors.WithStack(err)
12671252
}
12681253
}
@@ -1687,7 +1672,7 @@ func (p *Config) PasswordMigrationHook(ctx context.Context) *PasswordMigrationHo
16871672
return hook
16881673
}
16891674

1690-
hook.Config, _ = json.Marshal(p.GetProvider(ctx).Get(ViperKeyPasswordMigrationHook + ".config"))
1675+
_ = p.GetProvider(ctx).Unmarshal(ViperKeyPasswordMigrationHook+".config", &hook.Config)
16911676

16921677
return hook
16931678
}

driver/registry.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import (
3131
password2 "github.com/ory/kratos/selfservice/strategy/password"
3232
"github.com/ory/kratos/session"
3333
"github.com/ory/kratos/x"
34+
"github.com/ory/kratos/x/nosurfx"
3435
"github.com/ory/nosurf"
3536
"github.com/ory/x/contextx"
3637
"github.com/ory/x/dbal"
@@ -51,7 +52,7 @@ type Registry interface {
5152
WithJsonnetVMProvider(jsonnetsecure.VMProvider) Registry
5253

5354
WithCSRFHandler(c nosurf.Handler)
54-
WithCSRFTokenGenerator(cg x.CSRFToken)
55+
WithCSRFTokenGenerator(cg nosurfx.CSRFToken)
5556

5657
MetricsHandler() *prometheus.Handler
5758
HealthHandler(ctx context.Context) *healthx.Handler
@@ -70,7 +71,7 @@ type Registry interface {
7071
WithConfig(c *config.Config) Registry
7172
WithContextualizer(ctxer contextx.Contextualizer) Registry
7273

73-
x.CSRFProvider
74+
nosurfx.CSRFProvider
7475
x.WriterProvider
7576
x.LoggingProvider
7677
x.HTTPClientProvider
@@ -151,7 +152,7 @@ type Registry interface {
151152
recovery.HandlerProvider
152153
recovery.StrategyProvider
153154

154-
x.CSRFTokenGeneratorProvider
155+
nosurfx.CSRFTokenGeneratorProvider
155156
}
156157

157158
func NewRegistryFromDSN(ctx context.Context, c *config.Config, l *logrusx.Logger) (Registry, error) {

driver/registry_default.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import (
1212
"testing"
1313
"time"
1414

15+
"github.com/ory/kratos/x/nosurfx"
16+
1517
"github.com/lestrrat-go/jwx/jwk"
1618

1719
"github.com/ory/kratos/selfservice/strategy/idfirst"
@@ -157,7 +159,7 @@ type RegistryDefault struct {
157159
buildHash string
158160
buildDate string
159161

160-
csrfTokenGenerator x.CSRFToken
162+
csrfTokenGenerator nosurfx.CSRFToken
161163

162164
jsonnetVMProvider jsonnetsecure.VMProvider
163165
jsonnetPool jsonnetsecure.Pool
@@ -830,13 +832,13 @@ func (m *RegistryDefault) Ping() error {
830832
return m.persister.Ping(context.Background())
831833
}
832834

833-
func (m *RegistryDefault) WithCSRFTokenGenerator(cg x.CSRFToken) {
835+
func (m *RegistryDefault) WithCSRFTokenGenerator(cg nosurfx.CSRFToken) {
834836
m.csrfTokenGenerator = cg
835837
}
836838

837839
func (m *RegistryDefault) GenerateCSRFToken(r *http.Request) string {
838840
if m.csrfTokenGenerator == nil {
839-
m.csrfTokenGenerator = x.DefaultCSRFToken
841+
m.csrfTokenGenerator = nosurfx.DefaultCSRFToken
840842
}
841843
return m.csrfTokenGenerator(r)
842844
}

driver/registry_default_hooks.go

Lines changed: 38 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,13 @@
44
package driver
55

66
import (
7+
"encoding/json"
8+
"fmt"
9+
10+
"github.com/pkg/errors"
11+
712
"github.com/ory/kratos/driver/config"
13+
"github.com/ory/kratos/request"
814
"github.com/ory/kratos/selfservice/hook"
915
)
1016

@@ -57,44 +63,61 @@ func (m *RegistryDefault) WithExtraHandlers(handlers []NewHandlerRegistrar) {
5763
m.extraHandlerFactories = handlers
5864
}
5965

60-
func (m *RegistryDefault) getHooks(credentialsType string, configs []config.SelfServiceHook) (i []interface{}) {
66+
func getHooks[T any](m *RegistryDefault, credentialsType string, configs []config.SelfServiceHook) ([]T, error) {
67+
hooks := make([]T, 0, len(configs))
68+
6169
var addSessionIssuer bool
70+
allHooksLoop:
6271
for _, h := range configs {
6372
switch h.Name {
6473
case hook.KeySessionIssuer:
6574
// The session issuer hook always needs to come last.
6675
addSessionIssuer = true
6776
case hook.KeySessionDestroyer:
68-
i = append(i, m.HookSessionDestroyer())
77+
if h, ok := any(m.HookSessionDestroyer()).(T); ok {
78+
hooks = append(hooks, h)
79+
}
6980
case hook.KeyWebHook:
70-
i = append(i, hook.NewWebHook(m, h.Config))
81+
cfg := request.Config{}
82+
if err := json.Unmarshal(h.Config, &cfg); err != nil {
83+
m.l.WithError(err).WithField("raw_config", string(h.Config)).Error("failed to unmarshal hook configuration, ignoring hook")
84+
return nil, errors.WithStack(fmt.Errorf("failed to unmarshal webhook configuration for %s: %w", credentialsType, err))
85+
}
86+
if h, ok := any(hook.NewWebHook(m, &cfg)).(T); ok {
87+
hooks = append(hooks, h)
88+
}
7189
case hook.KeyAddressVerifier:
72-
i = append(i, m.HookAddressVerifier())
90+
if h, ok := any(m.HookAddressVerifier()).(T); ok {
91+
hooks = append(hooks, h)
92+
}
7393
case hook.KeyVerificationUI:
74-
i = append(i, m.HookShowVerificationUI())
94+
if h, ok := any(m.HookShowVerificationUI()).(T); ok {
95+
hooks = append(hooks, h)
96+
}
7597
case hook.KeyVerifier:
76-
i = append(i, m.HookVerifier())
98+
if h, ok := any(m.HookVerifier()).(T); ok {
99+
hooks = append(hooks, h)
100+
}
77101
default:
78-
var found bool
79102
for name, m := range m.injectedSelfserviceHooks {
80103
if name == h.Name {
81-
i = append(i, m(h))
82-
found = true
83-
break
104+
if h, ok := m(h).(T); ok {
105+
hooks = append(hooks, h)
106+
}
107+
continue allHooksLoop
84108
}
85109
}
86-
if found {
87-
continue
88-
}
89110
m.l.
90111
WithField("for", credentialsType).
91112
WithField("hook", h.Name).
92113
Warn("A configuration for a non-existing hook was found and will be ignored.")
93114
}
94115
}
95116
if addSessionIssuer {
96-
i = append(i, m.HookSessionIssuer())
117+
if h, ok := any(m.HookSessionIssuer()).(T); ok {
118+
hooks = append(hooks, h)
119+
}
97120
}
98121

99-
return i
122+
return hooks, nil
100123
}

0 commit comments

Comments
 (0)