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

This commit is contained in:
upstream-mirror
2026-09-15 21:19:12 +05:00
commit c60cc7ca75
8092 changed files with 394160 additions and 0 deletions

View File

@@ -0,0 +1,62 @@
// Copyright © 2024 Ory Corp
// SPDX-License-Identifier: Apache-2.0
import { execSync } from "child_process"
/**
* Extracts the SQLite file path from the TEST_DATABASE_SQLITE env var.
*
* Expected format: sqlite:///path/to/db.sqlite?_fk=true
*/
export function getSqliteDbPath(): string {
const dsn = process.env["TEST_DATABASE_SQLITE"]
if (!dsn) {
throw new Error(
"TEST_DATABASE_SQLITE is not set — cannot manipulate legacy data",
)
}
// Strip the sqlite:// prefix and any query parameters
const withoutScheme = dsn.replace(/^sqlite:\/\//, "")
const pathOnly = withoutScheme.split("?")[0]
return pathOnly
}
/**
* Downgrades an identity created via admin API to simulate a legacy (pre-normalization)
* record in the database. This reverts the E.164 normalization that the admin API applies
* on creation, making the DB state match what an old Kratos version would have stored.
*
* Updates:
* - identity_credential_identifiers: replaces normalized identifier with legacy format
* - identity_credentials: sets version to 0 (pre-normalization)
* - identity_verifiable_addresses: replaces normalized value with legacy format
* - identity_recovery_addresses: replaces normalized value with legacy format
*/
export function downgradeLegacyIdentity(
identityId: string,
legacyPhone: string,
normalizedPhone: string,
) {
const dbPath = getSqliteDbPath()
const escapedLegacy = legacyPhone.replace(/'/g, "''")
const escapedNormalized = normalizedPhone.replace(/'/g, "''")
const queries = [
// Revert credential identifiers to legacy format
`UPDATE identity_credential_identifiers SET identifier = '${escapedLegacy}' WHERE identifier = '${escapedNormalized}' AND identity_credential_id IN (SELECT id FROM identity_credentials WHERE identity_id = '${identityId}');`,
// Set credential version to 0 (pre-normalization)
`UPDATE identity_credentials SET version = 0 WHERE identity_id = '${identityId}';`,
// Revert verifiable addresses
`UPDATE identity_verifiable_addresses SET value = '${escapedLegacy}' WHERE value = '${escapedNormalized}' AND identity_id = '${identityId}';`,
// Revert recovery addresses
`UPDATE identity_recovery_addresses SET value = '${escapedLegacy}' WHERE value = '${escapedNormalized}' AND identity_id = '${identityId}';`,
]
for (const query of queries) {
execSync(`sqlite3 "${dbPath}" "${query}"`, {
encoding: "utf-8",
timeout: 5000,
})
}
}

View File

@@ -0,0 +1,100 @@
// Copyright © 2024 Ory Corp
// SPDX-License-Identifier: Apache-2.0
import { faker } from "@faker-js/faker"
import { APIRequestContext } from "@playwright/test"
import { CreateIdentityBody } from "@ory/kratos-client"
import { generatePhoneNumber, CountryNames } from "phone-number-generator-js"
import { expect } from "../fixtures"
export async function createIdentity(
request: APIRequestContext,
data: Partial<CreateIdentityBody>,
) {
const resp = await request.post("http://localhost:4434/admin/identities", {
data,
})
expect(resp.status()).toBe(201)
return await resp.json()
}
export async function createIdentityWithPhoneNumber(
request: APIRequestContext,
) {
const phone = generatePhoneNumber({
countryName: CountryNames.Germany,
withoutCountryCode: false,
})
return {
identity: await createIdentity(request, {
schema_id: "sms",
traits: {
phone,
},
}),
phone,
}
}
export async function createIdentityWithEmail(request: APIRequestContext) {
const email = faker.internet.email({ provider: "ory.sh" })
return {
identity: await createIdentity(request, {
schema_id: "email",
traits: {
email,
website: faker.internet.url(),
},
}),
email,
}
}
export async function createIdentityWithPhoneAndPassword(
request: APIRequestContext,
phone: string,
password: string,
schemaId = "sms-password",
) {
return {
identity: await createIdentity(request, {
schema_id: schemaId,
traits: {
phone,
},
credentials: {
password: {
config: {
password,
},
},
},
}),
phone,
password,
}
}
export async function createIdentityWithPassword(request: APIRequestContext) {
const email = faker.internet.email({ provider: "ory.sh" })
const password = faker.internet.password()
return {
identity: await createIdentity(request, {
schema_id: "email",
traits: {
email,
website: faker.internet.url(),
},
credentials: {
password: {
config: {
password,
},
},
},
}),
email,
password,
}
}

View File

@@ -0,0 +1,61 @@
// Copyright © 2024 Ory Corp
// SPDX-License-Identifier: Apache-2.0
import { APIRequestContext } from "@playwright/test"
import { findCsrfToken } from "../lib/helper"
import { LoginFlow, LogoutFlow, Session } from "@ory/kratos-client"
import { expectJSONResponse } from "../lib/request"
import { expect } from "../fixtures"
export async function loginWithPassword(
user: { password: string; traits: { email: string } },
r: APIRequestContext,
baseUrl: string,
): Promise<void> {
const { ui } = await expectJSONResponse<LoginFlow>(
await r.get(baseUrl + "/self-service/login/browser", {
headers: {
Accept: "application/json",
},
}),
{
message: "Initializing login flow failed",
},
)
const res = await r.post(ui.action, {
headers: {
Accept: "application/json",
},
data: {
identifier: user.traits.email,
password: user.password,
method: "password",
csrf_token: findCsrfToken(ui),
},
})
const { session } = await expectJSONResponse<{ session: Session }>(res)
expect(session?.identity?.traits.email).toEqual(user.traits.email)
expect(
res.headersArray().find(
({ name, value }) =>
name.toLowerCase() === "set-cookie" &&
(value.indexOf("ory_session_") > -1 || // Ory Network
value.indexOf("ory_kratos_session") > -1), // Locally hosted
),
).toBeDefined()
}
export async function logoutUrl(r: APIRequestContext, baseUrl: string) {
const { logout_url } = await expectJSONResponse<LogoutFlow>(
await r.get(baseUrl + "/self-service/logout/browser", {
headers: {
Accept: "application/json",
},
}),
{
message: "Logout failed",
},
)
return logout_url
}

View File

@@ -0,0 +1,39 @@
// Copyright © 2023 Ory Corp
// SPDX-License-Identifier: Apache-2.0
import mailhog from "mailhog"
import retry from "async-retry"
const mh = mailhog({
basePath: "http://localhost:8025/api",
})
type searchProps = {
query: string
kind: "to" | "from" | "containing"
/**
*
* @param message an email message
* @returns decide whether to include the message in the result
*/
filter?: (message: mailhog.Message) => boolean
}
export function search({ query, kind, filter }: searchProps) {
return retry(
async () => {
const res = await mh.search(query, kind)
if (res.total === 0) {
throw new Error("no emails found")
}
const result = filter ? res.items.filter(filter) : res.items
if (result.length === 0) {
throw new Error("no emails found")
}
return result
},
{
retries: 3,
},
)
}

View File

@@ -0,0 +1,38 @@
// Copyright © 2024 Ory Corp
// SPDX-License-Identifier: Apache-2.0
import { APIRequestContext, expect } from "@playwright/test"
import { Session } from "@ory/kratos-client"
export async function hasSession(
r: APIRequestContext,
kratosPublicURL: string,
): Promise<void> {
const resp = await r.get(kratosPublicURL + "/sessions/whoami", {
failOnStatusCode: true,
})
const session = await resp.json()
expect(session).toBeDefined()
expect(session.active).toBe(true)
}
export async function getSession(
r: APIRequestContext,
kratosPublicURL: string,
): Promise<Session> {
const resp = await r.get(kratosPublicURL + "/sessions/whoami", {
failOnStatusCode: true,
})
return resp.json()
}
export async function hasNoSession(
r: APIRequestContext,
kratosPublicURL: string,
): Promise<void> {
const resp = await r.get(kratosPublicURL + "/sessions/whoami", {
failOnStatusCode: false,
})
expect(resp.status()).toBe(401)
return resp.json()
}

View File

@@ -0,0 +1,57 @@
// Copyright © 2024 Ory Corp
// SPDX-License-Identifier: Apache-2.0
import { request } from "@playwright/test"
import retry from "promise-retry"
import { retryOptions } from "../lib/config"
export const WEBHOOK_TARGET = "http://127.0.0.1:4471"
const baseUrl = WEBHOOK_TARGET
/**
* Fetches a documented (hopefully) created by web hook
*
* @param key
*/
export async function fetchDocument(key: string) {
const r = await request.newContext()
return retry(async (retry) => {
const res = await r.get(documentUrl(key))
if (res.status() !== 200) {
const body = await res.text()
const message = `Expected response code 200 but received ${res.status()}: ${body}`
return retry(message)
}
return await res.json()
}, retryOptions)
}
/**
* Fetches a documented (hopefully) created by web hook
*
* @param key
*/
export async function deleteDocument(key: string) {
const r = await request.newContext()
return retry(async (retry) => {
const res = await r.delete(documentUrl(key))
if (res.status() !== 204) {
const body = await res.text()
const message = `Expected response code 204 but received ${res.status()}: ${body}`
return retry(message)
}
return
}, retryOptions)
}
/**
* Returns the URL for a specific document
*
* @param key
*/
export function documentUrl(key: string) {
return `${baseUrl}/documents/${key}`
}