snapshot of https://github.com/ory/kratos.git (master, 2026-09-15; blobs > 131072B omitted)
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
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
This commit is contained in:
285
pkg/testhelpers/session.go
Normal file
285
pkg/testhelpers/session.go
Normal file
@@ -0,0 +1,285 @@
|
||||
// Copyright © 2023 Ory Corp
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package testhelpers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/tidwall/gjson"
|
||||
|
||||
"github.com/ory/kratos/driver"
|
||||
"github.com/ory/kratos/identity"
|
||||
"github.com/ory/kratos/session"
|
||||
"github.com/ory/kratos/x"
|
||||
"github.com/ory/nosurf"
|
||||
"github.com/ory/x/contextx"
|
||||
)
|
||||
|
||||
type SessionLifespanProvider struct {
|
||||
e time.Duration
|
||||
}
|
||||
|
||||
func (p *SessionLifespanProvider) SessionLifespan(context.Context) time.Duration {
|
||||
return p.e
|
||||
}
|
||||
|
||||
func NewSessionClient(t *testing.T, u string) *http.Client {
|
||||
c := NewClientWithCookies(t)
|
||||
MockHydrateCookieClient(t, c, u)
|
||||
return c
|
||||
}
|
||||
|
||||
func maybePersistSession(ctx context.Context, t *testing.T, reg *driver.RegistryDefault, sess *session.Session) {
|
||||
id, err := reg.PrivilegedIdentityPool().GetIdentityConfidential(ctx, sess.Identity.ID)
|
||||
if err != nil {
|
||||
require.NoError(t, sess.Identity.SetAvailableAAL(ctx, reg.IdentityManager()))
|
||||
require.NoError(t, reg.IdentityManager().Create(ctx, sess.Identity))
|
||||
id, err = reg.PrivilegedIdentityPool().GetIdentityConfidential(ctx, sess.Identity.ID)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
sess.Identity = id
|
||||
sess.IdentityID = id.ID
|
||||
|
||||
require.NoError(t, err, reg.SessionPersister().UpsertSession(ctx, sess))
|
||||
}
|
||||
|
||||
func NewHTTPClientWithSessionCookie(ctx context.Context, t *testing.T, reg *driver.RegistryDefault, sess *session.Session) *http.Client {
|
||||
maybePersistSession(ctx, t, reg, sess)
|
||||
|
||||
var handler http.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
require.NoError(t, reg.SessionManager().IssueCookie(ctx, w, r, sess))
|
||||
})
|
||||
|
||||
if _, ok := reg.CSRFHandler().(*nosurf.CSRFHandler); ok {
|
||||
handler = nosurf.New(handler)
|
||||
}
|
||||
|
||||
ts := httptest.NewServer(handler)
|
||||
defer ts.Close()
|
||||
|
||||
c := NewClientWithCookies(t)
|
||||
MockHydrateCookieClient(t, c, ts.URL)
|
||||
return c
|
||||
}
|
||||
|
||||
func NewHTTPClientWithSessionCookieLocalhost(ctx context.Context, t *testing.T, reg *driver.RegistryDefault, sess *session.Session) *http.Client {
|
||||
maybePersistSession(ctx, t, reg, sess)
|
||||
|
||||
var handler http.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
require.NoError(t, reg.SessionManager().IssueCookie(ctx, w, r, sess))
|
||||
})
|
||||
|
||||
if _, ok := reg.CSRFHandler().(*nosurf.CSRFHandler); ok {
|
||||
handler = nosurf.New(handler)
|
||||
}
|
||||
|
||||
ts := httptest.NewServer(handler)
|
||||
defer ts.Close()
|
||||
|
||||
c := NewClientWithCookies(t)
|
||||
|
||||
ts.URL = strings.ReplaceAll(ts.URL, "127.0.0.1", "localhost")
|
||||
MockHydrateCookieClient(t, c, ts.URL)
|
||||
return c
|
||||
}
|
||||
|
||||
func NewNoRedirectHTTPClientWithSessionCookie(ctx context.Context, t *testing.T, reg *driver.RegistryDefault, sess *session.Session) *http.Client {
|
||||
maybePersistSession(ctx, t, reg, sess)
|
||||
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
require.NoError(t, reg.SessionManager().IssueCookie(ctx, w, r, sess))
|
||||
}))
|
||||
defer ts.Close()
|
||||
|
||||
c := NewNoRedirectClientWithCookies(t)
|
||||
|
||||
MockHydrateCookieClient(t, c, ts.URL)
|
||||
return c
|
||||
}
|
||||
|
||||
func NewTransportWithLogger(parent http.RoundTripper, t *testing.T) *TransportWithLogger {
|
||||
return &TransportWithLogger{
|
||||
RoundTripper: parent,
|
||||
t: t,
|
||||
}
|
||||
}
|
||||
|
||||
type TransportWithLogger struct {
|
||||
http.RoundTripper
|
||||
t *testing.T
|
||||
}
|
||||
|
||||
func (ct *TransportWithLogger) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
ct.t.Logf("Made request to: %s", req.URL.String())
|
||||
if ct.RoundTripper == nil {
|
||||
return http.DefaultTransport.RoundTrip(req)
|
||||
}
|
||||
return ct.RoundTripper.RoundTrip(req)
|
||||
}
|
||||
|
||||
func NewHTTPClientWithSessionToken(ctx context.Context, t *testing.T, reg *driver.RegistryDefault, sess *session.Session) *http.Client {
|
||||
maybePersistSession(ctx, t, reg, sess)
|
||||
|
||||
return &http.Client{
|
||||
Transport: NewTransportWithHeader(t, http.Header{
|
||||
"Authorization": {"Bearer " + sess.Token},
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
func NewHTTPClientWithArbitrarySessionToken(ctx context.Context, t *testing.T, reg *driver.RegistryDefault) *http.Client {
|
||||
return NewHTTPClientWithArbitrarySessionTokenAndTraits(ctx, t, reg, nil)
|
||||
}
|
||||
|
||||
func NewHTTPClientWithArbitrarySessionTokenAndTraits(ctx context.Context, t *testing.T, reg *driver.RegistryDefault, traits identity.Traits) *http.Client {
|
||||
req := NewTestHTTPRequest(t, "GET", "/sessions/whoami", nil).WithContext(contextx.WithConfigValue(ctx, "session.lifespan", time.Hour))
|
||||
s, err := NewActiveSession(req, reg,
|
||||
&identity.Identity{ID: x.NewUUID(), State: identity.StateActive, Traits: traits, NID: x.NewUUID(), SchemaID: "default"},
|
||||
time.Now(),
|
||||
identity.CredentialsTypePassword,
|
||||
identity.AuthenticatorAssuranceLevel1,
|
||||
)
|
||||
require.NoError(t, err, "Could not initialize session from identity.")
|
||||
|
||||
return NewHTTPClientWithSessionToken(ctx, t, reg, s)
|
||||
}
|
||||
|
||||
func NewHTTPClientWithArbitrarySessionCookie(ctx context.Context, t *testing.T, reg *driver.RegistryDefault) *http.Client {
|
||||
req := NewTestHTTPRequest(t, "GET", "/sessions/whoami", nil)
|
||||
req = req.WithContext(contextx.WithConfigValue(ctx, "session.lifespan", time.Hour))
|
||||
id := x.NewUUID()
|
||||
s, err := NewActiveSession(req, reg,
|
||||
&identity.Identity{ID: id, State: identity.StateActive, Traits: []byte("{}"), Credentials: map[identity.CredentialsType]identity.Credentials{
|
||||
identity.CredentialsTypePassword: {Type: "password", Identifiers: []string{id.String()}, Config: []byte(`{"hashed_password":"$2a$04$zvZz1zV"}`)},
|
||||
}},
|
||||
time.Now(),
|
||||
identity.CredentialsTypePassword,
|
||||
identity.AuthenticatorAssuranceLevel1,
|
||||
)
|
||||
require.NoError(t, err, "Could not initialize session from identity.")
|
||||
|
||||
return NewHTTPClientWithSessionCookie(ctx, t, reg, s)
|
||||
}
|
||||
|
||||
func NewNoRedirectHTTPClientWithArbitrarySessionCookie(ctx context.Context, t *testing.T, reg *driver.RegistryDefault) *http.Client {
|
||||
req := NewTestHTTPRequest(t, "GET", "/sessions/whoami", nil)
|
||||
req = req.WithContext(contextx.WithConfigValue(ctx, "session.lifespan", time.Hour))
|
||||
id := x.NewUUID()
|
||||
s, err := NewActiveSession(req, reg,
|
||||
&identity.Identity{ID: id, State: identity.StateActive,
|
||||
Credentials: map[identity.CredentialsType]identity.Credentials{
|
||||
identity.CredentialsTypePassword: {Type: "password", Identifiers: []string{id.String()}, Config: []byte(`{"hashed_password":"$2a$04$zvZz1zV"}`)},
|
||||
}},
|
||||
time.Now(),
|
||||
identity.CredentialsTypePassword,
|
||||
identity.AuthenticatorAssuranceLevel1,
|
||||
)
|
||||
require.NoError(t, err, "Could not initialize session from identity.")
|
||||
|
||||
return NewNoRedirectHTTPClientWithSessionCookie(ctx, t, reg, s)
|
||||
}
|
||||
|
||||
func NewHTTPClientWithIdentitySessionCookie(ctx context.Context, t *testing.T, reg *driver.RegistryDefault, id *identity.Identity) *http.Client {
|
||||
req := NewTestHTTPRequest(t, "GET", "/sessions/whoami", nil)
|
||||
req = req.WithContext(contextx.WithConfigValue(ctx, "session.lifespan", time.Hour))
|
||||
s, err := NewActiveSession(req, reg,
|
||||
id,
|
||||
time.Now(),
|
||||
identity.CredentialsTypePassword,
|
||||
identity.AuthenticatorAssuranceLevel1,
|
||||
)
|
||||
require.NoError(t, err, "Could not initialize session from identity.")
|
||||
|
||||
return NewHTTPClientWithSessionCookie(ctx, t, reg, s)
|
||||
}
|
||||
|
||||
func NewHTTPClientWithIdentitySessionCookieLocalhost(ctx context.Context, t *testing.T, reg *driver.RegistryDefault, id *identity.Identity) *http.Client {
|
||||
req := NewTestHTTPRequest(t, "GET", "/sessions/whoami", nil)
|
||||
s, err := NewActiveSession(req, reg,
|
||||
id,
|
||||
time.Now(),
|
||||
identity.CredentialsTypePassword,
|
||||
identity.AuthenticatorAssuranceLevel1,
|
||||
)
|
||||
require.NoError(t, err, "Could not initialize session from identity.")
|
||||
|
||||
return NewHTTPClientWithSessionCookieLocalhost(ctx, t, reg, s)
|
||||
}
|
||||
|
||||
func NewHTTPClientWithIdentitySessionToken(ctx context.Context, t *testing.T, reg *driver.RegistryDefault, id *identity.Identity) *http.Client {
|
||||
req := NewTestHTTPRequest(t, "GET", "/sessions/whoami", nil)
|
||||
req = req.WithContext(contextx.WithConfigValue(ctx, "session.lifespan", time.Hour))
|
||||
s, err := NewActiveSession(req, reg,
|
||||
id,
|
||||
time.Now(),
|
||||
identity.CredentialsTypePassword,
|
||||
identity.AuthenticatorAssuranceLevel1,
|
||||
)
|
||||
require.NoError(t, err, "Could not initialize session from identity.")
|
||||
|
||||
return NewHTTPClientWithSessionToken(ctx, t, reg, s)
|
||||
}
|
||||
|
||||
func EnsureAAL(t *testing.T, c *http.Client, ts *httptest.Server, aal string, methods ...string) {
|
||||
res, err := c.Get(ts.URL + session.RouteWhoami)
|
||||
require.NoError(t, err)
|
||||
sess := x.MustReadAll(res.Body)
|
||||
require.NoError(t, res.Body.Close())
|
||||
assert.EqualValues(t, aal, gjson.GetBytes(sess, "authenticator_assurance_level").String())
|
||||
for _, method := range methods {
|
||||
assert.EqualValues(t, method, gjson.GetBytes(sess, "authentication_methods.#(method=="+method+").method").String())
|
||||
}
|
||||
assert.Len(t, gjson.GetBytes(sess, "authentication_methods").Array(), 1+len(methods))
|
||||
}
|
||||
|
||||
func NewAuthorizedTransport(ctx context.Context, t *testing.T, reg *driver.RegistryDefault, sess *session.Session) *TransportWithHeader {
|
||||
maybePersistSession(ctx, t, reg, sess)
|
||||
|
||||
return NewTransportWithHeader(t, http.Header{
|
||||
"Authorization": {"Bearer " + sess.Token},
|
||||
})
|
||||
}
|
||||
|
||||
func NewTransportWithHeader(t *testing.T, h http.Header) *TransportWithHeader {
|
||||
if t == nil {
|
||||
panic("This function is for testing use only.")
|
||||
}
|
||||
return &TransportWithHeader{
|
||||
RoundTripper: NewTestTransport(t),
|
||||
h: h,
|
||||
}
|
||||
}
|
||||
|
||||
type TransportWithHeader struct {
|
||||
http.RoundTripper
|
||||
h http.Header
|
||||
}
|
||||
|
||||
func (ct *TransportWithHeader) GetHeader() http.Header {
|
||||
return ct.h
|
||||
}
|
||||
|
||||
func (ct *TransportWithHeader) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
for k := range ct.h {
|
||||
req.Header.Set(k, ct.h.Get(k))
|
||||
}
|
||||
return ct.RoundTripper.RoundTrip(req)
|
||||
}
|
||||
|
||||
func AssertNoCSRFCookieInResponse(t *testing.T, r *http.Response) {
|
||||
found := false
|
||||
for _, c := range r.Cookies() {
|
||||
if strings.HasPrefix(c.Name, "csrf_token") {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
require.False(t, found)
|
||||
}
|
||||
Reference in New Issue
Block a user