@ingram-tech/nk-auth 0.19.4 → 0.19.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chain.d.ts.map +1 -1
- package/dist/chain.js +41 -13
- package/dist/chain.js.map +1 -1
- package/dist/shipped-chain.d.ts +16 -0
- package/dist/shipped-chain.d.ts.map +1 -0
- package/dist/shipped-chain.js +26 -0
- package/dist/shipped-chain.js.map +1 -0
- package/package.json +1 -1
- package/src/chain.ts +56 -16
- package/src/shipped-chain.ts +25 -0
package/dist/chain.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chain.d.ts","sourceRoot":"","sources":["../src/chain.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,KAAK,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAGrF,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"chain.d.ts","sourceRoot":"","sources":["../src/chain.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,KAAK,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAGrF,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,IAAI,CAAC;AAG/B;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,QAAO,MAOtC,CAAC;AAEH,wEAAwE;AACxE,eAAO,MAAM,qBAAqB,wBAAwB,CAAC;AAS3D;;;;;;GAMG;AACH,qBAAa,wBAAyB,SAAQ,KAAK;IAClD,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,YAAY,OAAO,EAAE,SAAS,MAAM,EAAE,EAMrC;CACD;AAED,MAAM,WAAW,qBAAqB;IACrC,sFAAsF;IACtF,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,qDAAqD;IACrD,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC1B;AAsDD;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,sBAAsB,SAC5B,IAAI,YACD,qBAAqB,KAC5B,OAAO,CAAC,eAAe,GAAG,IAAI,CAahC,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,cAAc,aAChB,OAAO,YACR,qBAAqB,KAC5B,CAAC,MAAM,OAAO,CAAC,IAAI,CAAC,CAOtB,CAAC"}
|
package/dist/chain.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { inspectMigrations } from "@ingram-tech/nk-db/migrate";
|
|
2
2
|
import { createRequire } from "node:module";
|
|
3
3
|
import { dirname } from "node:path";
|
|
4
|
+
import { SHIPPED_AUTH_CHAIN } from "./shipped-chain.js";
|
|
4
5
|
/**
|
|
5
6
|
* The migration chain this package ships: `migrations/` in the installed
|
|
6
7
|
* package. Resolved through the package manifest, at call time, so a bundler
|
|
@@ -30,6 +31,43 @@ export class AuthChainNotAppliedError extends Error {
|
|
|
30
31
|
this.pending = pending;
|
|
31
32
|
}
|
|
32
33
|
}
|
|
34
|
+
const journalOf = (options) => ({
|
|
35
|
+
schema: options.migrationsSchema ?? "drizzle",
|
|
36
|
+
table: options.migrationsTable ?? AUTH_MIGRATIONS_TABLE,
|
|
37
|
+
});
|
|
38
|
+
/**
|
|
39
|
+
* Whether this role can read the journal table. Looked up in pg_catalog, which
|
|
40
|
+
* every role can read: `to_regclass` raises "permission denied" instead of
|
|
41
|
+
* returning null when the role lacks USAGE on the schema, and that broke every
|
|
42
|
+
* session read on a site whose auth role has no business in `drizzle`.
|
|
43
|
+
*/
|
|
44
|
+
const journalReadable = async (pool, schema, table) => {
|
|
45
|
+
const { rows } = await pool.query(`select has_schema_privilege(n.oid, 'USAGE')
|
|
46
|
+
and has_table_privilege(c.oid, 'SELECT') as readable
|
|
47
|
+
from pg_catalog.pg_class c
|
|
48
|
+
join pg_catalog.pg_namespace n on n.oid = c.relnamespace
|
|
49
|
+
where n.nspname = $1 and c.relname = $2`, [schema, table]);
|
|
50
|
+
return rows[0]?.readable === true;
|
|
51
|
+
};
|
|
52
|
+
const quoteIdent = (name) => `"${name.replaceAll('"', '""')}"`;
|
|
53
|
+
/**
|
|
54
|
+
* The same verdict as {@link assertAuthChainApplied}, decided against
|
|
55
|
+
* {@link SHIPPED_AUTH_CHAIN} instead of the `migrations/` folder: a shipped
|
|
56
|
+
* file is pending when its hash is not recorded (the set difference nk-db's
|
|
57
|
+
* runner uses). It never touches the filesystem, so it is safe inside a
|
|
58
|
+
* site's server bundle, where it runs on the first session read.
|
|
59
|
+
*/
|
|
60
|
+
const assertShippedChainRecorded = async (pool, options) => {
|
|
61
|
+
const { schema, table } = journalOf(options);
|
|
62
|
+
if (!(await journalReadable(pool, schema, table)))
|
|
63
|
+
return;
|
|
64
|
+
const { rows } = await pool.query(`select hash from ${quoteIdent(schema)}.${quoteIdent(table)}`);
|
|
65
|
+
const recorded = new Set(rows.map((row) => row.hash));
|
|
66
|
+
const pending = SHIPPED_AUTH_CHAIN.filter((file) => !recorded.has(file.hash));
|
|
67
|
+
if (pending.length > 0) {
|
|
68
|
+
throw new AuthChainNotAppliedError(pending.map((file) => file.tag));
|
|
69
|
+
}
|
|
70
|
+
};
|
|
33
71
|
/**
|
|
34
72
|
* Verify the shipped auth chain is fully applied to the database behind `pool`.
|
|
35
73
|
*
|
|
@@ -47,18 +85,8 @@ export class AuthChainNotAppliedError extends Error {
|
|
|
47
85
|
* `instrumentation.ts` or a health check to fail a deploy earlier.
|
|
48
86
|
*/
|
|
49
87
|
export const assertAuthChainApplied = async (pool, options = {}) => {
|
|
50
|
-
const schema = options
|
|
51
|
-
|
|
52
|
-
// Look the journal up in pg_catalog, which every role can read:
|
|
53
|
-
// `to_regclass` raises "permission denied" instead of returning null when the
|
|
54
|
-
// role lacks USAGE on the schema, and that broke every session read on a
|
|
55
|
-
// site whose auth role has no business in `drizzle`.
|
|
56
|
-
const { rows } = await pool.query(`select has_schema_privilege(n.oid, 'USAGE')
|
|
57
|
-
and has_table_privilege(c.oid, 'SELECT') as readable
|
|
58
|
-
from pg_catalog.pg_class c
|
|
59
|
-
join pg_catalog.pg_namespace n on n.oid = c.relnamespace
|
|
60
|
-
where n.nspname = $1 and c.relname = $2`, [schema, table]);
|
|
61
|
-
if (rows[0]?.readable !== true)
|
|
88
|
+
const { schema, table } = journalOf(options);
|
|
89
|
+
if (!(await journalReadable(pool, schema, table)))
|
|
62
90
|
return null;
|
|
63
91
|
const status = await inspectMigrations({
|
|
64
92
|
pool,
|
|
@@ -83,7 +111,7 @@ export const authChainCheck = (database, options = {}) => {
|
|
|
83
111
|
return async () => { };
|
|
84
112
|
let checked;
|
|
85
113
|
return () => {
|
|
86
|
-
checked ??=
|
|
114
|
+
checked ??= assertShippedChainRecorded(database, options);
|
|
87
115
|
return checked;
|
|
88
116
|
};
|
|
89
117
|
};
|
package/dist/chain.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chain.js","sourceRoot":"","sources":["../src/chain.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAwB,MAAM,4BAA4B,CAAC;AACrF,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"chain.js","sourceRoot":"","sources":["../src/chain.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The migration chain this package ships, as data: each `migrations/*.sql`
|
|
3
|
+
* file's tag and the sha256 nk-db's runner records for it.
|
|
4
|
+
*
|
|
5
|
+
* The per-request chain check runs inside the site's server bundle, where the
|
|
6
|
+
* package's files are not reliably on disk: Next rewrites `require.resolve`
|
|
7
|
+
* to a numeric module id, so resolving `migrations/` there made every session
|
|
8
|
+
* read throw. Carrying the chain in code keeps the check off the filesystem.
|
|
9
|
+
* `chain.test.ts` fails if this list drifts from `migrations/`, so a new
|
|
10
|
+
* migration must be added here in the same change.
|
|
11
|
+
*/
|
|
12
|
+
export declare const SHIPPED_AUTH_CHAIN: ReadonlyArray<{
|
|
13
|
+
tag: string;
|
|
14
|
+
hash: string;
|
|
15
|
+
}>;
|
|
16
|
+
//# sourceMappingURL=shipped-chain.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shipped-chain.d.ts","sourceRoot":"","sources":["../src/shipped-chain.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,eAAO,MAAM,kBAAkB,EAAE,aAAa,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAa3E,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The migration chain this package ships, as data: each `migrations/*.sql`
|
|
3
|
+
* file's tag and the sha256 nk-db's runner records for it.
|
|
4
|
+
*
|
|
5
|
+
* The per-request chain check runs inside the site's server bundle, where the
|
|
6
|
+
* package's files are not reliably on disk: Next rewrites `require.resolve`
|
|
7
|
+
* to a numeric module id, so resolving `migrations/` there made every session
|
|
8
|
+
* read throw. Carrying the chain in code keeps the check off the filesystem.
|
|
9
|
+
* `chain.test.ts` fails if this list drifts from `migrations/`, so a new
|
|
10
|
+
* migration must be added here in the same change.
|
|
11
|
+
*/
|
|
12
|
+
export const SHIPPED_AUTH_CHAIN = [
|
|
13
|
+
{
|
|
14
|
+
tag: "0001_better_auth",
|
|
15
|
+
hash: "5f44f3294805908dc7a946ac4fb2032d868da5f04e98f496ddfe76e8c0f8cddc",
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
tag: "0002_better_auth_1_7",
|
|
19
|
+
hash: "fa64a63e86ea862aff393fef6ecc705fb478d10c7bfc9e390ed475fa990aedea",
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
tag: "0003_better_auth_1_7_3",
|
|
23
|
+
hash: "45152bfe9401ace12ac860ccfc4ae49b6a3ab69cf2460947a964c5f231aa4919",
|
|
24
|
+
},
|
|
25
|
+
];
|
|
26
|
+
//# sourceMappingURL=shipped-chain.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shipped-chain.js","sourceRoot":"","sources":["../src/shipped-chain.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAiD;IAC/E;QACC,GAAG,EAAE,kBAAkB;QACvB,IAAI,EAAE,kEAAkE;KACxE;IACD;QACC,GAAG,EAAE,sBAAsB;QAC3B,IAAI,EAAE,kEAAkE;KACxE;IACD;QACC,GAAG,EAAE,wBAAwB;QAC7B,IAAI,EAAE,kEAAkE;KACxE;CACD,CAAC"}
|
package/package.json
CHANGED
package/src/chain.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { inspectMigrations, type MigrationStatus } from "@ingram-tech/nk-db/migr
|
|
|
2
2
|
import { createRequire } from "node:module";
|
|
3
3
|
import { dirname } from "node:path";
|
|
4
4
|
import type { Pool } from "pg";
|
|
5
|
+
import { SHIPPED_AUTH_CHAIN } from "./shipped-chain.js";
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* The migration chain this package ships: `migrations/` in the installed
|
|
@@ -53,6 +54,58 @@ export interface AuthChainCheckOptions {
|
|
|
53
54
|
migrationsSchema?: string;
|
|
54
55
|
}
|
|
55
56
|
|
|
57
|
+
const journalOf = (options: AuthChainCheckOptions) => ({
|
|
58
|
+
schema: options.migrationsSchema ?? "drizzle",
|
|
59
|
+
table: options.migrationsTable ?? AUTH_MIGRATIONS_TABLE,
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Whether this role can read the journal table. Looked up in pg_catalog, which
|
|
64
|
+
* every role can read: `to_regclass` raises "permission denied" instead of
|
|
65
|
+
* returning null when the role lacks USAGE on the schema, and that broke every
|
|
66
|
+
* session read on a site whose auth role has no business in `drizzle`.
|
|
67
|
+
*/
|
|
68
|
+
const journalReadable = async (
|
|
69
|
+
pool: Pool,
|
|
70
|
+
schema: string,
|
|
71
|
+
table: string,
|
|
72
|
+
): Promise<boolean> => {
|
|
73
|
+
const { rows } = await pool.query<{ readable: boolean }>(
|
|
74
|
+
`select has_schema_privilege(n.oid, 'USAGE')
|
|
75
|
+
and has_table_privilege(c.oid, 'SELECT') as readable
|
|
76
|
+
from pg_catalog.pg_class c
|
|
77
|
+
join pg_catalog.pg_namespace n on n.oid = c.relnamespace
|
|
78
|
+
where n.nspname = $1 and c.relname = $2`,
|
|
79
|
+
[schema, table],
|
|
80
|
+
);
|
|
81
|
+
return rows[0]?.readable === true;
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const quoteIdent = (name: string): string => `"${name.replaceAll('"', '""')}"`;
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* The same verdict as {@link assertAuthChainApplied}, decided against
|
|
88
|
+
* {@link SHIPPED_AUTH_CHAIN} instead of the `migrations/` folder: a shipped
|
|
89
|
+
* file is pending when its hash is not recorded (the set difference nk-db's
|
|
90
|
+
* runner uses). It never touches the filesystem, so it is safe inside a
|
|
91
|
+
* site's server bundle, where it runs on the first session read.
|
|
92
|
+
*/
|
|
93
|
+
const assertShippedChainRecorded = async (
|
|
94
|
+
pool: Pool,
|
|
95
|
+
options: AuthChainCheckOptions,
|
|
96
|
+
): Promise<void> => {
|
|
97
|
+
const { schema, table } = journalOf(options);
|
|
98
|
+
if (!(await journalReadable(pool, schema, table))) return;
|
|
99
|
+
const { rows } = await pool.query<{ hash: string }>(
|
|
100
|
+
`select hash from ${quoteIdent(schema)}.${quoteIdent(table)}`,
|
|
101
|
+
);
|
|
102
|
+
const recorded = new Set(rows.map((row) => row.hash));
|
|
103
|
+
const pending = SHIPPED_AUTH_CHAIN.filter((file) => !recorded.has(file.hash));
|
|
104
|
+
if (pending.length > 0) {
|
|
105
|
+
throw new AuthChainNotAppliedError(pending.map((file) => file.tag));
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
|
|
56
109
|
/**
|
|
57
110
|
* Verify the shipped auth chain is fully applied to the database behind `pool`.
|
|
58
111
|
*
|
|
@@ -73,21 +126,8 @@ export const assertAuthChainApplied = async (
|
|
|
73
126
|
pool: Pool,
|
|
74
127
|
options: AuthChainCheckOptions = {},
|
|
75
128
|
): Promise<MigrationStatus | null> => {
|
|
76
|
-
const schema = options
|
|
77
|
-
|
|
78
|
-
// Look the journal up in pg_catalog, which every role can read:
|
|
79
|
-
// `to_regclass` raises "permission denied" instead of returning null when the
|
|
80
|
-
// role lacks USAGE on the schema, and that broke every session read on a
|
|
81
|
-
// site whose auth role has no business in `drizzle`.
|
|
82
|
-
const { rows } = await pool.query<{ readable: boolean }>(
|
|
83
|
-
`select has_schema_privilege(n.oid, 'USAGE')
|
|
84
|
-
and has_table_privilege(c.oid, 'SELECT') as readable
|
|
85
|
-
from pg_catalog.pg_class c
|
|
86
|
-
join pg_catalog.pg_namespace n on n.oid = c.relnamespace
|
|
87
|
-
where n.nspname = $1 and c.relname = $2`,
|
|
88
|
-
[schema, table],
|
|
89
|
-
);
|
|
90
|
-
if (rows[0]?.readable !== true) return null;
|
|
129
|
+
const { schema, table } = journalOf(options);
|
|
130
|
+
if (!(await journalReadable(pool, schema, table))) return null;
|
|
91
131
|
const status = await inspectMigrations({
|
|
92
132
|
pool,
|
|
93
133
|
migrationsFolder: authMigrationsFolder(),
|
|
@@ -114,7 +154,7 @@ export const authChainCheck = (
|
|
|
114
154
|
if (!isPool(database)) return async () => {};
|
|
115
155
|
let checked: Promise<void> | undefined;
|
|
116
156
|
return () => {
|
|
117
|
-
checked ??=
|
|
157
|
+
checked ??= assertShippedChainRecorded(database, options);
|
|
118
158
|
return checked;
|
|
119
159
|
};
|
|
120
160
|
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The migration chain this package ships, as data: each `migrations/*.sql`
|
|
3
|
+
* file's tag and the sha256 nk-db's runner records for it.
|
|
4
|
+
*
|
|
5
|
+
* The per-request chain check runs inside the site's server bundle, where the
|
|
6
|
+
* package's files are not reliably on disk: Next rewrites `require.resolve`
|
|
7
|
+
* to a numeric module id, so resolving `migrations/` there made every session
|
|
8
|
+
* read throw. Carrying the chain in code keeps the check off the filesystem.
|
|
9
|
+
* `chain.test.ts` fails if this list drifts from `migrations/`, so a new
|
|
10
|
+
* migration must be added here in the same change.
|
|
11
|
+
*/
|
|
12
|
+
export const SHIPPED_AUTH_CHAIN: ReadonlyArray<{ tag: string; hash: string }> = [
|
|
13
|
+
{
|
|
14
|
+
tag: "0001_better_auth",
|
|
15
|
+
hash: "5f44f3294805908dc7a946ac4fb2032d868da5f04e98f496ddfe76e8c0f8cddc",
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
tag: "0002_better_auth_1_7",
|
|
19
|
+
hash: "fa64a63e86ea862aff393fef6ecc705fb478d10c7bfc9e390ed475fa990aedea",
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
tag: "0003_better_auth_1_7_3",
|
|
23
|
+
hash: "45152bfe9401ace12ac860ccfc4ae49b6a3ab69cf2460947a964c5f231aa4919",
|
|
24
|
+
},
|
|
25
|
+
];
|