Some checks failed
CI / Run tests and lints (push) Failing after 29s
CI / Build CLI docs (push) Has been skipped
CI / Run Playwright end-to-end tests (mysql) (push) Failing after 3s
CI / Run Playwright end-to-end tests (postgres) (push) Failing after 2s
CI / Run Playwright end-to-end tests (sqlite) (push) Failing after 3s
CodeQL / Analyze (go) (push) Failing after 4s
CodeQL / Analyze (javascript) (push) Failing after 3s
Docker Image Scanners / scanners (push) Failing after 4s
Synchronize Issue Labels / Synchronize Issue Labels (push) Has been skipped
CI / Run end-to-end tests (postgres) (push) Failing after 19s
CI / Run end-to-end tests (sqlite) (push) Failing after 2s
CI / Run Playwright end-to-end tests (cockroach) (push) Failing after 2s
CI / Draft newsletter (push) Has been skipped
CI / Generate release (push) Has been skipped
CI / Pending approval Slack notification (push) Has been skipped
CI / Send newsletter (push) Has been skipped
198 lines
8.3 KiB
Go
198 lines
8.3 KiB
Go
// Copyright © 2023 Ory Corp
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package settings_test
|
|
|
|
import (
|
|
"context"
|
|
"net/http"
|
|
"net/url"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/tidwall/gjson"
|
|
|
|
"github.com/gobuffalo/httptest"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/ory/kratos/driver"
|
|
"github.com/ory/kratos/driver/config"
|
|
"github.com/ory/kratos/identity"
|
|
"github.com/ory/kratos/pkg"
|
|
"github.com/ory/kratos/pkg/testhelpers"
|
|
"github.com/ory/kratos/selfservice/flow"
|
|
"github.com/ory/kratos/selfservice/flow/settings"
|
|
"github.com/ory/kratos/selfservice/hook"
|
|
"github.com/ory/kratos/x"
|
|
)
|
|
|
|
func TestSettingsExecutor(t *testing.T) {
|
|
ctx := context.Background()
|
|
for _, strategy := range []string{
|
|
identity.CredentialsTypePassword.String(),
|
|
settings.StrategyProfile,
|
|
} {
|
|
t.Run("strategy="+strategy, func(t *testing.T) {
|
|
conf, reg := pkg.NewFastRegistryWithMocks(t)
|
|
testhelpers.SetDefaultIdentitySchema(conf, "file://./stub/identity.schema.json")
|
|
conf.MustSet(ctx, config.ViperKeySelfServiceBrowserDefaultReturnTo, returnToServer.URL)
|
|
|
|
reg.WithHooks(map[string]driver.NewHookFn{
|
|
"err": func(c config.SelfServiceHook, _ driver.Registry) interface{} {
|
|
return &hook.Error{Config: c.Config}
|
|
},
|
|
})
|
|
|
|
newServer := func(t *testing.T, i *identity.Identity, ft flow.Type) *httptest.Server {
|
|
t.Helper()
|
|
router := http.NewServeMux()
|
|
handleErr := testhelpers.SelfServiceHookSettingsErrorHandler
|
|
router.HandleFunc("GET /settings/pre", func(w http.ResponseWriter, r *http.Request) {
|
|
if i == nil {
|
|
i = testhelpers.SelfServiceHookCreateFakeIdentity(t, reg)
|
|
}
|
|
sess, _ := testhelpers.NewActiveSession(r, reg, i, time.Now().UTC(), identity.CredentialsTypePassword, identity.AuthenticatorAssuranceLevel1)
|
|
|
|
f, err := settings.NewFlow(reg, r, sess.Identity, ft)
|
|
require.NoError(t, err)
|
|
if handleErr(t, w, r, reg.SettingsHookExecutor().PreSettingsHook(r.Context(), w, r, f, sess)) {
|
|
_, _ = w.Write([]byte("ok"))
|
|
}
|
|
})
|
|
|
|
router.HandleFunc("GET /settings/post", func(w http.ResponseWriter, r *http.Request) {
|
|
if i == nil {
|
|
i = testhelpers.SelfServiceHookCreateFakeIdentity(t, reg)
|
|
}
|
|
sess, _ := testhelpers.NewActiveSession(r, reg, i, time.Now().UTC(), identity.CredentialsTypePassword, identity.AuthenticatorAssuranceLevel1)
|
|
|
|
a, err := settings.NewFlow(reg, r, sess.Identity, ft)
|
|
require.NoError(t, err)
|
|
a.RequestURL = x.RequestURL(r).String()
|
|
require.NoError(t, reg.SettingsFlowPersister().CreateSettingsFlow(r.Context(), a))
|
|
_ = handleErr(t, w, r, reg.SettingsHookExecutor().
|
|
PostSettingsHook(ctx, w, r, strategy, &settings.UpdateContext{Flow: a, Session: sess}, i))
|
|
})
|
|
ts := httptest.NewServer(router)
|
|
t.Cleanup(ts.Close)
|
|
conf.MustSet(ctx, config.ViperKeyPublicBaseURL, ts.URL)
|
|
return ts
|
|
}
|
|
|
|
makeRequestPost := testhelpers.SelfServiceMakeSettingsPostHookRequest
|
|
viperSetPost := func(strategy string, c []config.SelfServiceHook) {
|
|
conf.MustSet(ctx, config.HookStrategyKey(config.ViperKeySelfServiceSettingsAfter, strategy), c)
|
|
}
|
|
|
|
uiTS := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}))
|
|
t.Cleanup(uiTS.Close)
|
|
uiURL := uiTS.URL + "/user/settings"
|
|
conf.MustSet(ctx, config.ViperKeySelfServiceSettingsURL, uiURL)
|
|
|
|
t.Run("method=PostSettingsHook", func(t *testing.T) {
|
|
t.Run("case=pass without hooks", func(t *testing.T) {
|
|
t.Cleanup(testhelpers.SelfServiceHookConfigReset(t, conf))
|
|
|
|
res, body := makeRequestPost(t, newServer(t, nil, flow.TypeBrowser), false, url.Values{})
|
|
require.Equalf(t, http.StatusOK, res.StatusCode, "%s", body)
|
|
assert.Contains(t, res.Request.URL.String(), uiURL)
|
|
})
|
|
|
|
t.Run("case=pass without hooks if ajax client", func(t *testing.T) {
|
|
t.Cleanup(testhelpers.SelfServiceHookConfigReset(t, conf))
|
|
|
|
ts := newServer(t, nil, flow.TypeBrowser)
|
|
res, body := makeRequestPost(t, ts, true, url.Values{})
|
|
require.Equalf(t, http.StatusOK, res.StatusCode, "%s", body)
|
|
assert.Contains(t, res.Request.URL.String(), ts.URL)
|
|
assert.EqualValues(t, gjson.Get(body, "continue_with.0.action").String(), "redirect_browser_to")
|
|
})
|
|
|
|
t.Run("case=pass if hooks pass", func(t *testing.T) {
|
|
t.Cleanup(testhelpers.SelfServiceHookConfigReset(t, conf))
|
|
|
|
viperSetPost(strategy, []config.SelfServiceHook{{Name: "err", Config: []byte(`{}`)}})
|
|
res, body := makeRequestPost(t, newServer(t, nil, flow.TypeBrowser), false, url.Values{})
|
|
require.Equalf(t, http.StatusOK, res.StatusCode, "%s", body)
|
|
assert.Contains(t, res.Request.URL.String(), uiURL)
|
|
})
|
|
|
|
t.Run("case=fail if hooks fail", func(t *testing.T) {
|
|
t.Cleanup(testhelpers.SelfServiceHookConfigReset(t, conf))
|
|
|
|
viperSetPost(strategy, []config.SelfServiceHook{{Name: "err", Config: []byte(`{"ExecuteSettingsPrePersistHook": "abort"}`)}})
|
|
res, body := makeRequestPost(t, newServer(t, nil, flow.TypeBrowser), false, url.Values{})
|
|
require.Equalf(t, http.StatusOK, res.StatusCode, "%s", body)
|
|
assert.Equal(t, "", body)
|
|
})
|
|
|
|
t.Run("case=use return_to value", func(t *testing.T) {
|
|
t.Cleanup(testhelpers.SelfServiceHookConfigReset(t, conf))
|
|
conf.MustSet(ctx, config.ViperKeyURLsAllowedReturnToDomains, []string{returnToServer.URL})
|
|
testhelpers.SelfServiceHookSettingsSetDefaultRedirectTo(t, conf, "https://www.ory.com")
|
|
|
|
res, body := makeRequestPost(t, newServer(t, nil, flow.TypeBrowser), false, url.Values{"return_to": {returnToServer.URL + "/kratos"}})
|
|
require.Equalf(t, http.StatusOK, res.StatusCode, "%s", body)
|
|
assert.EqualValues(t, returnToServer.URL+"/kratos", res.Request.URL.String())
|
|
})
|
|
|
|
t.Run("case=use nested config value", func(t *testing.T) {
|
|
t.Cleanup(testhelpers.SelfServiceHookConfigReset(t, conf))
|
|
testhelpers.SelfServiceHookSettingsSetDefaultRedirectTo(t, conf, returnToServer.URL+"/kratos")
|
|
|
|
res, body := makeRequestPost(t, newServer(t, nil, flow.TypeBrowser), false, url.Values{})
|
|
require.Equalf(t, http.StatusOK, res.StatusCode, "%s", body)
|
|
assert.EqualValues(t, returnToServer.URL+"/kratos", res.Request.URL.String())
|
|
})
|
|
|
|
t.Run("case=use nested config value", func(t *testing.T) {
|
|
t.Cleanup(testhelpers.SelfServiceHookConfigReset(t, conf))
|
|
testhelpers.SelfServiceHookSettingsSetDefaultRedirectTo(t, conf, "https://www.ory.com/not-kratos")
|
|
testhelpers.SelfServiceHookSettingsSetDefaultRedirectToStrategy(t, conf, strategy, returnToServer.URL+"/kratos")
|
|
|
|
res, body := makeRequestPost(t, newServer(t, nil, flow.TypeBrowser), false, url.Values{})
|
|
require.Equalf(t, http.StatusOK, res.StatusCode, "%s", body)
|
|
assert.EqualValues(t, returnToServer.URL+"/kratos", res.Request.URL.String())
|
|
})
|
|
|
|
t.Run("case=pass if hooks pass", func(t *testing.T) {
|
|
t.Cleanup(testhelpers.SelfServiceHookConfigReset(t, conf))
|
|
viperSetPost(strategy, []config.SelfServiceHook{{Name: "err", Config: []byte(`{}`)}})
|
|
res, body := makeRequestPost(t, newServer(t, nil, flow.TypeBrowser), false, url.Values{})
|
|
require.Equalf(t, http.StatusOK, res.StatusCode, "%s", body)
|
|
assert.Contains(t, res.Request.URL.String(), uiURL)
|
|
})
|
|
|
|
t.Run("case=send a json response for API clients", func(t *testing.T) {
|
|
t.Cleanup(testhelpers.SelfServiceHookConfigReset(t, conf))
|
|
viperSetPost(strategy, nil)
|
|
res, body := makeRequestPost(t, newServer(t, nil, flow.TypeAPI), true, url.Values{})
|
|
require.Equalf(t, http.StatusOK, res.StatusCode, "%s", body)
|
|
assert.NotEmpty(t, gjson.Get(body, "identity.id"))
|
|
})
|
|
|
|
t.Run("case=pass without hooks for browser flow with application/json", func(t *testing.T) {
|
|
t.Cleanup(testhelpers.SelfServiceHookConfigReset(t, conf))
|
|
|
|
res, body := makeRequestPost(t, newServer(t, nil, flow.TypeBrowser), true, url.Values{})
|
|
require.Equalf(t, http.StatusOK, res.StatusCode, "%s", body)
|
|
assert.NotEmpty(t, gjson.Get(body, "identity.id"))
|
|
})
|
|
})
|
|
|
|
for _, kind := range []flow.Type{flow.TypeBrowser, flow.TypeAPI} {
|
|
t.Run("type="+string(kind)+"/method=PreSettingsHook", testhelpers.TestSelfServicePreHook(
|
|
config.ViperKeySelfServiceSettingsBeforeHooks,
|
|
testhelpers.SelfServiceMakeSettingsPreHookRequest,
|
|
func(t *testing.T) *httptest.Server {
|
|
return newServer(t, nil, kind)
|
|
},
|
|
conf,
|
|
))
|
|
}
|
|
})
|
|
}
|
|
}
|