@kyro-cms/core 0.8.0 → 0.9.1
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/README.md +57 -589
- package/dist/{WebhookService-118ZTFis.d.ts → WebhookService-CUTb9XOy.d.ts} +1 -1
- package/dist/{WebhookService-AefJfqX0.d.cts → WebhookService-Yg2UEOB4.d.cts} +1 -1
- package/dist/api-handler-graphql.cjs +44 -0
- package/dist/api-handler-graphql.cjs.map +1 -0
- package/dist/api-handler-graphql.d.cts +6 -0
- package/dist/api-handler-graphql.d.ts +6 -0
- package/dist/api-handler-graphql.js +41 -0
- package/dist/api-handler-graphql.js.map +1 -0
- package/dist/api-handler-trpc.cjs +38 -0
- package/dist/api-handler-trpc.cjs.map +1 -0
- package/dist/api-handler-trpc.d.cts +5 -0
- package/dist/api-handler-trpc.d.ts +5 -0
- package/dist/api-handler-trpc.js +36 -0
- package/dist/api-handler-trpc.js.map +1 -0
- package/dist/api-handler.cjs +31 -97
- package/dist/api-handler.cjs.map +1 -1
- package/dist/api-handler.d.cts +2 -1
- package/dist/api-handler.d.ts +2 -1
- package/dist/api-handler.js +19 -95
- package/dist/api-handler.js.map +1 -1
- package/dist/{tenant-B1YB0Jy8.d.ts → base-B71y_EAF.d.cts} +6 -12
- package/dist/{tenant-Cpeveji6.d.cts → base-DaqY2GhA.d.ts} +6 -12
- package/dist/bootstrap-5NLASFOG.cjs +32 -0
- package/dist/{bootstrap-AKAUP6F6.cjs.map → bootstrap-5NLASFOG.cjs.map} +1 -1
- package/dist/bootstrap-T5BK77LD.js +7 -0
- package/dist/{bootstrap-JCML6NFO.js.map → bootstrap-T5BK77LD.js.map} +1 -1
- package/dist/{chunk-35U3FROB.js → chunk-22M4O4ZJ.js} +607 -63
- package/dist/chunk-22M4O4ZJ.js.map +1 -0
- package/dist/chunk-2HZRBATX.cjs +253 -0
- package/dist/chunk-2HZRBATX.cjs.map +1 -0
- package/dist/{chunk-VJT6P4N6.cjs → chunk-3HR772HI.cjs} +199 -32
- package/dist/chunk-3HR772HI.cjs.map +1 -0
- package/dist/chunk-3KTWGODI.cjs +178 -0
- package/dist/chunk-3KTWGODI.cjs.map +1 -0
- package/dist/{chunk-QXIQWPAP.js → chunk-3UK5XBVJ.js} +4 -134
- package/dist/chunk-3UK5XBVJ.js.map +1 -0
- package/dist/{chunk-FXYP2HA6.js → chunk-4AO3A3JM.js} +48 -4
- package/dist/chunk-4AO3A3JM.js.map +1 -0
- package/dist/chunk-4M7X5HAB.cjs +173 -0
- package/dist/chunk-4M7X5HAB.cjs.map +1 -0
- package/dist/chunk-5EPFQUQD.js +3243 -0
- package/dist/chunk-5EPFQUQD.js.map +1 -0
- package/dist/{chunk-Y3N7UUDO.js → chunk-7OGPN7MP.js} +5 -2
- package/dist/chunk-7OGPN7MP.js.map +1 -0
- package/dist/{chunk-WOWUL7ZY.js → chunk-AL5KX63J.js} +4 -3
- package/dist/chunk-AL5KX63J.js.map +1 -0
- package/dist/{chunk-2OL4O2TH.cjs → chunk-C36TMDTY.cjs} +66 -61
- package/dist/chunk-C36TMDTY.cjs.map +1 -0
- package/dist/{chunk-ES5HNFFT.js → chunk-CF7OL6HR.js} +4 -2
- package/dist/chunk-CF7OL6HR.js.map +1 -0
- package/dist/chunk-CJONKRHJ.js +162 -0
- package/dist/chunk-CJONKRHJ.js.map +1 -0
- package/dist/{chunk-2KVHZE6O.cjs → chunk-COIASRDK.cjs} +202 -46
- package/dist/chunk-COIASRDK.cjs.map +1 -0
- package/dist/chunk-DEVFAKCQ.cjs +3291 -0
- package/dist/chunk-DEVFAKCQ.cjs.map +1 -0
- package/dist/{chunk-3ZFYL34R.js → chunk-DYTZ6FQ7.js} +12 -185
- package/dist/chunk-DYTZ6FQ7.js.map +1 -0
- package/dist/{chunk-QPPDLRNR.js → chunk-EJN2PAOE.js} +197 -41
- package/dist/chunk-EJN2PAOE.js.map +1 -0
- package/dist/chunk-FAXU7BMP.js +220 -0
- package/dist/chunk-FAXU7BMP.js.map +1 -0
- package/dist/{chunk-OHVB4AJ7.js → chunk-FOPGUM27.js} +22 -17
- package/dist/chunk-FOPGUM27.js.map +1 -0
- package/dist/chunk-GAOXD3XT.js +175 -0
- package/dist/chunk-GAOXD3XT.js.map +1 -0
- package/dist/{chunk-4DA7QPLA.cjs → chunk-GXFOGU7N.cjs} +5 -2
- package/dist/chunk-GXFOGU7N.cjs.map +1 -0
- package/dist/{chunk-I7HHI6QV.cjs → chunk-IDVRRRAK.cjs} +17 -9
- package/dist/chunk-IDVRRRAK.cjs.map +1 -0
- package/dist/{chunk-WQBRWOQT.cjs → chunk-JOPVMWTM.cjs} +3 -2
- package/dist/chunk-JOPVMWTM.cjs.map +1 -0
- package/dist/chunk-KC2GDBLS.cjs +84 -0
- package/dist/chunk-KC2GDBLS.cjs.map +1 -0
- package/dist/{chunk-K7JPTH3G.cjs → chunk-KNRSROWB.cjs} +132 -74
- package/dist/chunk-KNRSROWB.cjs.map +1 -0
- package/dist/{chunk-3AJE4SEG.js → chunk-KPA4AN4R.js} +125 -67
- package/dist/chunk-KPA4AN4R.js.map +1 -0
- package/dist/{chunk-QUW2RZTM.cjs → chunk-L46ROHUS.cjs} +51 -7
- package/dist/chunk-L46ROHUS.cjs.map +1 -0
- package/dist/chunk-L4EZKIEX.js +185 -0
- package/dist/chunk-L4EZKIEX.js.map +1 -0
- package/dist/{chunk-REK7AYOC.js → chunk-L5UKKZQN.js} +199 -32
- package/dist/chunk-L5UKKZQN.js.map +1 -0
- package/dist/chunk-NKPKR5BW.cjs +188 -0
- package/dist/chunk-NKPKR5BW.cjs.map +1 -0
- package/dist/{chunk-Y3QQN7PN.js → chunk-P2HKJ7P5.js} +13 -4
- package/dist/chunk-P2HKJ7P5.js.map +1 -0
- package/dist/{chunk-SA7NSSIQ.cjs → chunk-PI73NNOK.cjs} +13 -187
- package/dist/chunk-PI73NNOK.cjs.map +1 -0
- package/dist/{chunk-HXRD4B37.js → chunk-PU2Z5VWF.js} +1279 -556
- package/dist/chunk-PU2Z5VWF.js.map +1 -0
- package/dist/{chunk-H727JIG7.js → chunk-Q72BOAPK.js} +16 -8
- package/dist/chunk-Q72BOAPK.js.map +1 -0
- package/dist/{chunk-IBG6V56E.cjs → chunk-QFLB4EIJ.cjs} +2 -139
- package/dist/chunk-QFLB4EIJ.cjs.map +1 -0
- package/dist/{chunk-YVUJBEXE.cjs → chunk-RAMGUDJN.cjs} +16 -7
- package/dist/chunk-RAMGUDJN.cjs.map +1 -0
- package/dist/{chunk-LINKCEG4.cjs → chunk-ROJHKAQ4.cjs} +617 -73
- package/dist/chunk-ROJHKAQ4.cjs.map +1 -0
- package/dist/{chunk-5KVM3WEY.cjs → chunk-RSF3UU7H.cjs} +1330 -602
- package/dist/chunk-RSF3UU7H.cjs.map +1 -0
- package/dist/{chunk-V3LKPM3O.cjs → chunk-SHTTJMLT.cjs} +4 -2
- package/dist/chunk-SHTTJMLT.cjs.map +1 -0
- package/dist/chunk-SPBTLUN6.js +92 -0
- package/dist/chunk-SPBTLUN6.js.map +1 -0
- package/dist/{chunk-57P6MJKC.js → chunk-TXSZFA4G.js} +3 -3
- package/dist/chunk-TXSZFA4G.js.map +1 -0
- package/dist/chunk-UERVXYVK.cjs +99 -0
- package/dist/chunk-UERVXYVK.cjs.map +1 -0
- package/dist/{chunk-PDYFVNUX.cjs → chunk-V2TVSCV5.cjs} +16 -23
- package/dist/chunk-V2TVSCV5.cjs.map +1 -0
- package/dist/{chunk-DXHRBMGB.js → chunk-VO35MNPH.js} +12 -19
- package/dist/chunk-VO35MNPH.js.map +1 -0
- package/dist/{chunk-IA6AU5PI.cjs → chunk-WNCYAKF3.cjs} +3 -3
- package/dist/chunk-WNCYAKF3.cjs.map +1 -0
- package/dist/chunk-XEB7PH2E.js +81 -0
- package/dist/chunk-XEB7PH2E.js.map +1 -0
- package/dist/cli/index.cjs +5 -5
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.js +5 -5
- package/dist/cli/index.js.map +1 -1
- package/dist/client.cjs +3 -3
- package/dist/client.d.cts +3 -3
- package/dist/client.d.ts +3 -3
- package/dist/client.js +1 -1
- package/dist/drizzle/index.cjs +14 -13
- package/dist/drizzle/index.d.cts +9 -7
- package/dist/drizzle/index.d.ts +9 -7
- package/dist/drizzle/index.js +5 -4
- package/dist/fields/index.cjs +21 -37
- package/dist/fields/index.d.cts +2 -22
- package/dist/fields/index.d.ts +2 -22
- package/dist/fields/index.js +1 -1
- package/dist/graphql/index.cjs +5 -4
- package/dist/graphql/index.d.cts +5 -3
- package/dist/graphql/index.d.ts +5 -3
- package/dist/graphql/index.js +3 -2
- package/dist/index-CJXPB_ot.d.ts +276 -0
- package/dist/index-CaTNnLGd.d.cts +276 -0
- package/dist/index.cjs +304 -162
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +129 -205
- package/dist/index.d.ts +129 -205
- package/dist/index.js +172 -33
- package/dist/index.js.map +1 -1
- package/dist/integration.cjs +2 -2
- package/dist/integration.js +1 -1
- package/dist/mongo-auth-adapter-ISOM7FSS.cjs +17 -0
- package/dist/{mongo-auth-adapter-NHHUJHVH.cjs.map → mongo-auth-adapter-ISOM7FSS.cjs.map} +1 -1
- package/dist/mongo-auth-adapter-MO6STCV3.js +4 -0
- package/dist/{mongo-auth-adapter-NJQUUCTP.js.map → mongo-auth-adapter-MO6STCV3.js.map} +1 -1
- package/dist/mongodb/index.cjs +8 -7
- package/dist/mongodb/index.d.cts +5 -7
- package/dist/mongodb/index.d.ts +5 -7
- package/dist/mongodb/index.js +4 -3
- package/dist/postgres-auth-adapter-DWDR7P5G.js +5 -0
- package/dist/{postgres-auth-adapter-3T2NKTSE.js.map → postgres-auth-adapter-DWDR7P5G.js.map} +1 -1
- package/dist/postgres-auth-adapter-WRWSJD4E.cjs +14 -0
- package/dist/{postgres-auth-adapter-7IEENCKQ.cjs.map → postgres-auth-adapter-WRWSJD4E.cjs.map} +1 -1
- package/dist/redis-adapter-HGTPWIGV.js +4 -0
- package/dist/{redis-adapter-VQXD7ESY.js.map → redis-adapter-HGTPWIGV.js.map} +1 -1
- package/dist/redis-adapter-KJ3YOOT6.cjs +13 -0
- package/dist/{redis-adapter-D2E2S3GB.cjs.map → redis-adapter-KJ3YOOT6.cjs.map} +1 -1
- package/dist/rest/index.cjs +15 -14
- package/dist/rest/index.d.cts +4 -4
- package/dist/rest/index.d.ts +4 -4
- package/dist/rest/index.js +13 -12
- package/dist/{schema-5PHL5IVB.js → schema-6I5OFR4Z.js} +3 -3
- package/dist/{schema-5PHL5IVB.js.map → schema-6I5OFR4Z.js.map} +1 -1
- package/dist/{schema-37SE2F4B.cjs → schema-TTFE4467.cjs} +14 -14
- package/dist/{schema-37SE2F4B.cjs.map → schema-TTFE4467.cjs.map} +1 -1
- package/dist/sqlite-adapter-6GEUSVXQ.js +4 -0
- package/dist/{sqlite-adapter-TR3U3W6Q.js.map → sqlite-adapter-6GEUSVXQ.js.map} +1 -1
- package/dist/sqlite-adapter-CSIZE5SX.cjs +13 -0
- package/dist/{sqlite-adapter-LVK5PS4T.cjs.map → sqlite-adapter-CSIZE5SX.cjs.map} +1 -1
- package/dist/templates/index.cjs +133 -31
- package/dist/templates/index.d.cts +52 -9
- package/dist/templates/index.d.ts +52 -9
- package/dist/templates/index.js +3 -1
- package/dist/trpc/index.cjs +13 -12
- package/dist/trpc/index.d.cts +55 -49
- package/dist/trpc/index.d.ts +55 -49
- package/dist/trpc/index.js +4 -3
- package/dist/{types-D6ZLRGbH.d.cts → types-CpjuXbe7.d.cts} +2 -0
- package/dist/{types-D6ZLRGbH.d.ts → types-CpjuXbe7.d.ts} +2 -0
- package/dist/{types-Bs1up4yP.d.ts → types-CyCQ6SAI.d.ts} +28 -2
- package/dist/{types-J3R9nVsZ.d.cts → types-DJxD9394.d.cts} +28 -2
- package/dist/{types-VtjUxIMp.d.cts → types-Z6FBiqa2.d.cts} +35 -14
- package/dist/{types-VtjUxIMp.d.ts → types-Z6FBiqa2.d.ts} +35 -14
- package/package.json +22 -4
- package/dist/bootstrap-AKAUP6F6.cjs +0 -32
- package/dist/bootstrap-JCML6NFO.js +0 -7
- package/dist/chunk-2KVHZE6O.cjs.map +0 -1
- package/dist/chunk-2OL4O2TH.cjs.map +0 -1
- package/dist/chunk-35U3FROB.js.map +0 -1
- package/dist/chunk-3AJE4SEG.js.map +0 -1
- package/dist/chunk-3J4MFTI3.js +0 -3872
- package/dist/chunk-3J4MFTI3.js.map +0 -1
- package/dist/chunk-3ZFYL34R.js.map +0 -1
- package/dist/chunk-4DA7QPLA.cjs.map +0 -1
- package/dist/chunk-57P6MJKC.js.map +0 -1
- package/dist/chunk-5KVM3WEY.cjs.map +0 -1
- package/dist/chunk-6IMPH6WV.cjs +0 -3897
- package/dist/chunk-6IMPH6WV.cjs.map +0 -1
- package/dist/chunk-ATBOUGQP.cjs +0 -513
- package/dist/chunk-ATBOUGQP.cjs.map +0 -1
- package/dist/chunk-DXHRBMGB.js.map +0 -1
- package/dist/chunk-ES5HNFFT.js.map +0 -1
- package/dist/chunk-FXYP2HA6.js.map +0 -1
- package/dist/chunk-H727JIG7.js.map +0 -1
- package/dist/chunk-HXRD4B37.js.map +0 -1
- package/dist/chunk-I7HHI6QV.cjs.map +0 -1
- package/dist/chunk-IA6AU5PI.cjs.map +0 -1
- package/dist/chunk-IBG6V56E.cjs.map +0 -1
- package/dist/chunk-K7JPTH3G.cjs.map +0 -1
- package/dist/chunk-LINKCEG4.cjs.map +0 -1
- package/dist/chunk-OHVB4AJ7.js.map +0 -1
- package/dist/chunk-PDYFVNUX.cjs.map +0 -1
- package/dist/chunk-Q23JB3KL.js +0 -488
- package/dist/chunk-Q23JB3KL.js.map +0 -1
- package/dist/chunk-QPPDLRNR.js.map +0 -1
- package/dist/chunk-QUW2RZTM.cjs.map +0 -1
- package/dist/chunk-QXIQWPAP.js.map +0 -1
- package/dist/chunk-R3XIBBAW.cjs +0 -34
- package/dist/chunk-R3XIBBAW.cjs.map +0 -1
- package/dist/chunk-REK7AYOC.js.map +0 -1
- package/dist/chunk-SA7NSSIQ.cjs.map +0 -1
- package/dist/chunk-SDMNUYVU.js +0 -30
- package/dist/chunk-SDMNUYVU.js.map +0 -1
- package/dist/chunk-V3LKPM3O.cjs.map +0 -1
- package/dist/chunk-VJT6P4N6.cjs.map +0 -1
- package/dist/chunk-WOWUL7ZY.js.map +0 -1
- package/dist/chunk-WQBRWOQT.cjs.map +0 -1
- package/dist/chunk-Y3N7UUDO.js.map +0 -1
- package/dist/chunk-Y3QQN7PN.js.map +0 -1
- package/dist/chunk-YVUJBEXE.cjs.map +0 -1
- package/dist/index-CLp-DRKA.d.ts +0 -64
- package/dist/index-DfO7G4kN.d.cts +0 -64
- package/dist/mongo-auth-adapter-NHHUJHVH.cjs +0 -17
- package/dist/mongo-auth-adapter-NJQUUCTP.js +0 -4
- package/dist/postgres-auth-adapter-3T2NKTSE.js +0 -5
- package/dist/postgres-auth-adapter-7IEENCKQ.cjs +0 -14
- package/dist/redis-adapter-D2E2S3GB.cjs +0 -13
- package/dist/redis-adapter-VQXD7ESY.js +0 -4
- package/dist/sqlite-adapter-LVK5PS4T.cjs +0 -13
- package/dist/sqlite-adapter-TR3U3W6Q.js +0 -4
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var crypto = require('crypto');
|
|
4
|
+
|
|
5
|
+
// src/access/types.ts
|
|
6
|
+
async function evaluateAccess(access, args) {
|
|
7
|
+
if (typeof access === "boolean") {
|
|
8
|
+
return access;
|
|
9
|
+
}
|
|
10
|
+
if (typeof access === "function") {
|
|
11
|
+
return await access(args);
|
|
12
|
+
}
|
|
13
|
+
return true;
|
|
14
|
+
}
|
|
15
|
+
function mergeWhereClauses(...whereClauses) {
|
|
16
|
+
const result = {};
|
|
17
|
+
for (const clause of whereClauses) {
|
|
18
|
+
if (clause && typeof clause === "object") {
|
|
19
|
+
Object.assign(result, clause);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return result;
|
|
23
|
+
}
|
|
24
|
+
function getWhereClause(access, args) {
|
|
25
|
+
return evaluateAccess(access, args).then((result) => {
|
|
26
|
+
if (result === true) return void 0;
|
|
27
|
+
if (result === false) return { _id: { $eq: null } };
|
|
28
|
+
return result;
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
var API_KEY_COLLECTION = "_api_keys";
|
|
32
|
+
function generateKeyPrefix(key) {
|
|
33
|
+
return key.substring(0, 8);
|
|
34
|
+
}
|
|
35
|
+
function constantTimeCompare(a, b) {
|
|
36
|
+
if (a.length !== b.length) {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
try {
|
|
40
|
+
return crypto.timingSafeEqual(Buffer.from(a), Buffer.from(b));
|
|
41
|
+
} catch {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
async function validateApiKey(rawKey, db, userLookup) {
|
|
46
|
+
if (!rawKey || typeof rawKey !== "string") {
|
|
47
|
+
return { valid: false, error: "No API key provided" };
|
|
48
|
+
}
|
|
49
|
+
if (!rawKey.startsWith("kyro_")) {
|
|
50
|
+
return { valid: false, error: "Invalid API key format" };
|
|
51
|
+
}
|
|
52
|
+
const keyPrefix = generateKeyPrefix(rawKey);
|
|
53
|
+
try {
|
|
54
|
+
const result = await db.find({
|
|
55
|
+
collection: API_KEY_COLLECTION,
|
|
56
|
+
where: { keyPrefix: { equals: keyPrefix } },
|
|
57
|
+
limit: 100,
|
|
58
|
+
page: 1
|
|
59
|
+
});
|
|
60
|
+
if (!result.docs || result.docs.length === 0) {
|
|
61
|
+
return { valid: false, error: "Invalid API key" };
|
|
62
|
+
}
|
|
63
|
+
let matchedKey = null;
|
|
64
|
+
for (const doc of result.docs) {
|
|
65
|
+
const record = doc;
|
|
66
|
+
if (constantTimeCompare(record.key, rawKey)) {
|
|
67
|
+
matchedKey = record;
|
|
68
|
+
break;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
if (!matchedKey) {
|
|
72
|
+
return { valid: false, error: "Invalid API key" };
|
|
73
|
+
}
|
|
74
|
+
if (matchedKey.expiresAt) {
|
|
75
|
+
const expiresAt = new Date(matchedKey.expiresAt);
|
|
76
|
+
if (expiresAt < /* @__PURE__ */ new Date()) {
|
|
77
|
+
return { valid: false, error: "API key has expired" };
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
try {
|
|
81
|
+
await db.update({
|
|
82
|
+
collection: API_KEY_COLLECTION,
|
|
83
|
+
id: matchedKey.id,
|
|
84
|
+
data: { lastUsedAt: (/* @__PURE__ */ new Date()).toISOString() }
|
|
85
|
+
});
|
|
86
|
+
} catch {
|
|
87
|
+
}
|
|
88
|
+
const user = {
|
|
89
|
+
id: matchedKey.userId,
|
|
90
|
+
role: matchedKey.role || "author",
|
|
91
|
+
tenantId: matchedKey.tenantId
|
|
92
|
+
};
|
|
93
|
+
if (userLookup) {
|
|
94
|
+
const dbUser = await userLookup(matchedKey.userId);
|
|
95
|
+
if (dbUser) {
|
|
96
|
+
Object.assign(user, dbUser);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return {
|
|
100
|
+
valid: true,
|
|
101
|
+
userId: matchedKey.userId,
|
|
102
|
+
user,
|
|
103
|
+
permissions: matchedKey.permissions || [],
|
|
104
|
+
apiKeyId: matchedKey.id,
|
|
105
|
+
tenantId: user.tenantId,
|
|
106
|
+
role: user.role
|
|
107
|
+
};
|
|
108
|
+
} catch (error) {
|
|
109
|
+
console.error("[ApiKey] Validation error:", error);
|
|
110
|
+
return { valid: false, error: "Failed to validate API key" };
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
function extractApiKeyFromRequest(request) {
|
|
114
|
+
const authHeader = request.headers.get("Authorization");
|
|
115
|
+
if (authHeader) {
|
|
116
|
+
if (authHeader.startsWith("ApiKey ")) {
|
|
117
|
+
return authHeader.slice(7).trim();
|
|
118
|
+
}
|
|
119
|
+
if (authHeader.startsWith("Bearer ")) {
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
const xApiKey = request.headers.get("X-API-Key");
|
|
124
|
+
if (xApiKey) {
|
|
125
|
+
return xApiKey.trim();
|
|
126
|
+
}
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
function createApiKeyContext(result) {
|
|
130
|
+
if (!result.valid || !result.userId) {
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
return {
|
|
134
|
+
userId: result.userId,
|
|
135
|
+
user: result.user || {},
|
|
136
|
+
permissions: result.permissions || [],
|
|
137
|
+
apiKeyId: result.apiKeyId || "",
|
|
138
|
+
tenantId: result.tenantId,
|
|
139
|
+
role: result.role
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
function hasApiKeyPermission(permissions, required) {
|
|
143
|
+
if (permissions.length === 0) return false;
|
|
144
|
+
if (permissions.includes("*")) return true;
|
|
145
|
+
if (permissions.includes(required)) return true;
|
|
146
|
+
const [resource, action] = required.split(":");
|
|
147
|
+
if (permissions.includes(`${resource}:*`)) return true;
|
|
148
|
+
return false;
|
|
149
|
+
}
|
|
150
|
+
function generateApiKey() {
|
|
151
|
+
const chars = "abcdefghijklmnopqrstuvwxyz0123456789";
|
|
152
|
+
let suffix = "";
|
|
153
|
+
for (let i = 0; i < 32; i++) {
|
|
154
|
+
suffix += chars[Math.floor(Math.random() * chars.length)];
|
|
155
|
+
}
|
|
156
|
+
return `kyro_${suffix}`;
|
|
157
|
+
}
|
|
158
|
+
function generateApiKeyPrefix(key) {
|
|
159
|
+
return key.substring(0, 8);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
exports.API_KEY_COLLECTION = API_KEY_COLLECTION;
|
|
163
|
+
exports.createApiKeyContext = createApiKeyContext;
|
|
164
|
+
exports.evaluateAccess = evaluateAccess;
|
|
165
|
+
exports.extractApiKeyFromRequest = extractApiKeyFromRequest;
|
|
166
|
+
exports.generateApiKey = generateApiKey;
|
|
167
|
+
exports.generateApiKeyPrefix = generateApiKeyPrefix;
|
|
168
|
+
exports.getWhereClause = getWhereClause;
|
|
169
|
+
exports.hasApiKeyPermission = hasApiKeyPermission;
|
|
170
|
+
exports.mergeWhereClauses = mergeWhereClauses;
|
|
171
|
+
exports.validateApiKey = validateApiKey;
|
|
172
|
+
//# sourceMappingURL=chunk-4M7X5HAB.cjs.map
|
|
173
|
+
//# sourceMappingURL=chunk-4M7X5HAB.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/access/types.ts","../src/auth/api-key.ts"],"names":["timingSafeEqual"],"mappings":"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","file":"chunk-4M7X5HAB.cjs","sourcesContent":["import type { User, Request } from '../hooks/types.js';\n\n// ============================================================================\n// Access Control Types\n// ============================================================================\n\nexport interface WhereClause {\n [field: string]: any;\n}\n\nexport interface AccessArgs {\n req: Request;\n user?: User;\n data?: any;\n doc?: any;\n id?: string;\n tenantID?: string;\n context?: Record<string, any>;\n}\n\nexport type AccessControl = boolean | ((args: AccessArgs) => Promise<boolean | WhereClause> | boolean | WhereClause);\n\nexport interface CollectionAccess {\n create?: AccessControl;\n read?: AccessControl;\n update?: AccessControl;\n delete?: AccessControl;\n admin?: AccessControl;\n unlock?: AccessControl;\n readVersions?: AccessControl;\n}\n\nexport interface GlobalAccess {\n read?: AccessControl;\n update?: AccessControl;\n}\n\nexport interface FieldAccess {\n create?: AccessControl;\n read?: AccessControl;\n update?: AccessControl;\n}\n\n// ============================================================================\n// Access Control Evaluation\n// ============================================================================\n\nexport async function evaluateAccess(\n access: AccessControl,\n args: AccessArgs\n): Promise<boolean | WhereClause> {\n if (typeof access === 'boolean') {\n return access;\n }\n if (typeof access === 'function') {\n return await access(args);\n }\n return true;\n}\n\nexport function mergeWhereClauses(\n ...whereClauses: (WhereClause | boolean | undefined)[]\n): WhereClause {\n const result: WhereClause = {};\n for (const clause of whereClauses) {\n if (clause && typeof clause === 'object') {\n Object.assign(result, clause);\n }\n }\n return result;\n}\n\nexport function getWhereClause(\n access: AccessControl,\n args: AccessArgs\n): Promise<WhereClause | undefined> {\n return evaluateAccess(access, args).then(result => {\n if (result === true) return undefined;\n if (result === false) return { _id: { $eq: null } };\n return result;\n });\n}\n","import { timingSafeEqual } from \"crypto\";\nimport type { BaseAdapter } from \"../registry/types.js\";\nimport type { AuthUser, UserRole } from \"./types.js\";\n\nexport interface ApiKeyRecord {\n id: string;\n userId: string;\n name: string;\n key: string;\n keyPrefix: string;\n permissions: string[];\n lastUsedAt?: string;\n expiresAt?: string;\n createdAt: string;\n}\n\nexport interface ApiKeyValidationResult {\n valid: boolean;\n userId?: string;\n user?: Partial<AuthUser>;\n permissions?: string[];\n apiKeyId?: string;\n error?: string;\n tenantId?: string;\n role?: UserRole;\n}\n\nexport interface ApiKeyContext {\n userId: string;\n user: Partial<AuthUser>;\n permissions: string[];\n apiKeyId: string;\n tenantId?: string;\n role?: UserRole;\n}\n\nexport const API_KEY_COLLECTION = \"_api_keys\";\n\nfunction generateKeyPrefix(key: string): string {\n return key.substring(0, 8);\n}\n\nfunction constantTimeCompare(a: string, b: string): boolean {\n if (a.length !== b.length) {\n return false;\n }\n try {\n return timingSafeEqual(Buffer.from(a), Buffer.from(b));\n } catch {\n return false;\n }\n}\n\nexport async function validateApiKey(\n rawKey: string,\n db: BaseAdapter,\n userLookup?: (userId: string) => Promise<Partial<AuthUser> | null>,\n): Promise<ApiKeyValidationResult> {\n if (!rawKey || typeof rawKey !== \"string\") {\n return { valid: false, error: \"No API key provided\" };\n }\n\n if (!rawKey.startsWith(\"kyro_\")) {\n return { valid: false, error: \"Invalid API key format\" };\n }\n\n const keyPrefix = generateKeyPrefix(rawKey);\n\n try {\n const result = await db.find({\n collection: API_KEY_COLLECTION,\n where: { keyPrefix: { equals: keyPrefix } },\n limit: 100,\n page: 1,\n });\n\n if (!result.docs || result.docs.length === 0) {\n return { valid: false, error: \"Invalid API key\" };\n }\n\n let matchedKey: ApiKeyRecord | null = null;\n for (const doc of result.docs) {\n const record = doc as unknown as ApiKeyRecord;\n if (constantTimeCompare(record.key, rawKey)) {\n matchedKey = record;\n break;\n }\n }\n\n if (!matchedKey) {\n return { valid: false, error: \"Invalid API key\" };\n }\n\n if (matchedKey.expiresAt) {\n const expiresAt = new Date(matchedKey.expiresAt);\n if (expiresAt < new Date()) {\n return { valid: false, error: \"API key has expired\" };\n }\n }\n\n try {\n await db.update({\n collection: API_KEY_COLLECTION,\n id: matchedKey.id,\n data: { lastUsedAt: new Date().toISOString() },\n });\n } catch {\n // Non-critical: don't fail if lastUsedAt update fails\n }\n\n const user: Partial<AuthUser> = {\n id: matchedKey.userId,\n role: (matchedKey as any).role || \"author\",\n tenantId: (matchedKey as any).tenantId,\n };\n\n if (userLookup) {\n const dbUser = await userLookup(matchedKey.userId);\n if (dbUser) {\n Object.assign(user, dbUser);\n }\n }\n\n return {\n valid: true,\n userId: matchedKey.userId,\n user,\n permissions: matchedKey.permissions || [],\n apiKeyId: matchedKey.id,\n tenantId: user.tenantId,\n role: user.role,\n };\n } catch (error) {\n console.error(\"[ApiKey] Validation error:\", error);\n return { valid: false, error: \"Failed to validate API key\" };\n }\n}\n\nexport function extractApiKeyFromRequest(request: Request): string | null {\n const authHeader = request.headers.get(\"Authorization\");\n if (authHeader) {\n if (authHeader.startsWith(\"ApiKey \")) {\n return authHeader.slice(7).trim();\n }\n if (authHeader.startsWith(\"Bearer \")) {\n return null;\n }\n }\n\n const xApiKey = request.headers.get(\"X-API-Key\");\n if (xApiKey) {\n return xApiKey.trim();\n }\n\n return null;\n}\n\nexport function createApiKeyContext(\n result: ApiKeyValidationResult,\n): ApiKeyContext | null {\n if (!result.valid || !result.userId) {\n return null;\n }\n return {\n userId: result.userId,\n user: result.user || {},\n permissions: result.permissions || [],\n apiKeyId: result.apiKeyId || \"\",\n tenantId: result.tenantId,\n role: result.role,\n };\n}\n\nexport function hasApiKeyPermission(\n permissions: string[],\n required: string,\n): boolean {\n if (permissions.length === 0) return false;\n if (permissions.includes(\"*\")) return true;\n if (permissions.includes(required)) return true;\n\n const [resource, action] = required.split(\":\");\n if (permissions.includes(`${resource}:*`)) return true;\n\n return false;\n}\n\nexport function generateApiKey(): string {\n const chars = \"abcdefghijklmnopqrstuvwxyz0123456789\";\n let suffix = \"\";\n for (let i = 0; i < 32; i++) {\n suffix += chars[Math.floor(Math.random() * chars.length)];\n }\n return `kyro_${suffix}`;\n}\n\nexport function generateApiKeyPrefix(key: string): string {\n return key.substring(0, 8);\n}\n"]}
|