@stratum-hq/lib 0.1.0
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/__tests__/crypto.test.d.ts +2 -0
- package/dist/__tests__/crypto.test.d.ts.map +1 -0
- package/dist/__tests__/crypto.test.js +41 -0
- package/dist/__tests__/crypto.test.js.map +1 -0
- package/dist/crypto.d.ts +7 -0
- package/dist/crypto.d.ts.map +1 -0
- package/dist/crypto.js +74 -0
- package/dist/crypto.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/pool-helpers.d.ts +4 -0
- package/dist/pool-helpers.d.ts.map +1 -0
- package/dist/pool-helpers.js +31 -0
- package/dist/pool-helpers.js.map +1 -0
- package/dist/services/__tests__/audit-service.test.d.ts +2 -0
- package/dist/services/__tests__/audit-service.test.d.ts.map +1 -0
- package/dist/services/__tests__/audit-service.test.js +220 -0
- package/dist/services/__tests__/audit-service.test.js.map +1 -0
- package/dist/services/__tests__/config-service.test.d.ts +2 -0
- package/dist/services/__tests__/config-service.test.d.ts.map +1 -0
- package/dist/services/__tests__/config-service.test.js +555 -0
- package/dist/services/__tests__/config-service.test.js.map +1 -0
- package/dist/services/__tests__/consent-service.test.d.ts +2 -0
- package/dist/services/__tests__/consent-service.test.d.ts.map +1 -0
- package/dist/services/__tests__/consent-service.test.js +159 -0
- package/dist/services/__tests__/consent-service.test.js.map +1 -0
- package/dist/services/__tests__/event-service.test.d.ts +2 -0
- package/dist/services/__tests__/event-service.test.d.ts.map +1 -0
- package/dist/services/__tests__/event-service.test.js +151 -0
- package/dist/services/__tests__/event-service.test.js.map +1 -0
- package/dist/services/__tests__/permission-service.test.d.ts +2 -0
- package/dist/services/__tests__/permission-service.test.d.ts.map +1 -0
- package/dist/services/__tests__/permission-service.test.js +470 -0
- package/dist/services/__tests__/permission-service.test.js.map +1 -0
- package/dist/services/__tests__/retention-service.test.d.ts +2 -0
- package/dist/services/__tests__/retention-service.test.d.ts.map +1 -0
- package/dist/services/__tests__/retention-service.test.js +126 -0
- package/dist/services/__tests__/retention-service.test.js.map +1 -0
- package/dist/services/__tests__/tenant-service.test.d.ts +2 -0
- package/dist/services/__tests__/tenant-service.test.d.ts.map +1 -0
- package/dist/services/__tests__/tenant-service.test.js +451 -0
- package/dist/services/__tests__/tenant-service.test.js.map +1 -0
- package/dist/services/__tests__/test-helpers.d.ts +9 -0
- package/dist/services/__tests__/test-helpers.d.ts.map +1 -0
- package/dist/services/__tests__/test-helpers.js +20 -0
- package/dist/services/__tests__/test-helpers.js.map +1 -0
- package/dist/services/__tests__/webhook-service.test.d.ts +2 -0
- package/dist/services/__tests__/webhook-service.test.d.ts.map +1 -0
- package/dist/services/__tests__/webhook-service.test.js +174 -0
- package/dist/services/__tests__/webhook-service.test.js.map +1 -0
- package/dist/services/api-key-service.d.ts +61 -0
- package/dist/services/api-key-service.d.ts.map +1 -0
- package/dist/services/api-key-service.js +164 -0
- package/dist/services/api-key-service.js.map +1 -0
- package/dist/services/audit-service.d.ts +6 -0
- package/dist/services/audit-service.d.ts.map +1 -0
- package/dist/services/audit-service.js +85 -0
- package/dist/services/audit-service.js.map +1 -0
- package/dist/services/config-service.d.ts +33 -0
- package/dist/services/config-service.d.ts.map +1 -0
- package/dist/services/config-service.js +271 -0
- package/dist/services/config-service.js.map +1 -0
- package/dist/services/consent-service.d.ts +7 -0
- package/dist/services/consent-service.d.ts.map +1 -0
- package/dist/services/consent-service.js +71 -0
- package/dist/services/consent-service.js.map +1 -0
- package/dist/services/event-service.d.ts +39 -0
- package/dist/services/event-service.d.ts.map +1 -0
- package/dist/services/event-service.js +331 -0
- package/dist/services/event-service.js.map +1 -0
- package/dist/services/key-rotation-service.d.ts +15 -0
- package/dist/services/key-rotation-service.d.ts.map +1 -0
- package/dist/services/key-rotation-service.js +41 -0
- package/dist/services/key-rotation-service.js.map +1 -0
- package/dist/services/permission-service.d.ts +21 -0
- package/dist/services/permission-service.d.ts.map +1 -0
- package/dist/services/permission-service.js +213 -0
- package/dist/services/permission-service.js.map +1 -0
- package/dist/services/region-service.d.ts +9 -0
- package/dist/services/region-service.d.ts.map +1 -0
- package/dist/services/region-service.js +105 -0
- package/dist/services/region-service.js.map +1 -0
- package/dist/services/retention-service.d.ts +19 -0
- package/dist/services/retention-service.d.ts.map +1 -0
- package/dist/services/retention-service.js +88 -0
- package/dist/services/retention-service.js.map +1 -0
- package/dist/services/role-service.d.ts +35 -0
- package/dist/services/role-service.d.ts.map +1 -0
- package/dist/services/role-service.js +101 -0
- package/dist/services/role-service.js.map +1 -0
- package/dist/services/tenant-service.d.ts +25 -0
- package/dist/services/tenant-service.d.ts.map +1 -0
- package/dist/services/tenant-service.js +335 -0
- package/dist/services/tenant-service.js.map +1 -0
- package/dist/services/webhook-service.d.ts +12 -0
- package/dist/services/webhook-service.d.ts.map +1 -0
- package/dist/services/webhook-service.js +129 -0
- package/dist/services/webhook-service.js.map +1 -0
- package/dist/stratum.d.ts +107 -0
- package/dist/stratum.d.ts.map +1 -0
- package/dist/stratum.js +531 -0
- package/dist/stratum.js.map +1 -0
- package/dist/telemetry.d.ts +27 -0
- package/dist/telemetry.d.ts.map +1 -0
- package/dist/telemetry.js +94 -0
- package/dist/telemetry.js.map +1 -0
- package/package.json +46 -0
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
const vitest_1 = require("vitest");
|
|
27
|
+
// Mock pool-helpers before importing webhook-service
|
|
28
|
+
vitest_1.vi.mock("../../pool-helpers.js", () => ({
|
|
29
|
+
withClient: vitest_1.vi.fn(),
|
|
30
|
+
withTransaction: vitest_1.vi.fn(),
|
|
31
|
+
}));
|
|
32
|
+
const poolHelpers = __importStar(require("../../pool-helpers.js"));
|
|
33
|
+
const webhookService = __importStar(require("../webhook-service.js"));
|
|
34
|
+
const core_1 = require("@stratum-hq/core");
|
|
35
|
+
function makeMockPool() {
|
|
36
|
+
return {};
|
|
37
|
+
}
|
|
38
|
+
const mockWebhook = {
|
|
39
|
+
id: "550e8400-e29b-41d4-a716-446655440000",
|
|
40
|
+
tenant_id: null,
|
|
41
|
+
url: "https://example.com/webhook",
|
|
42
|
+
secret_hash: "mock-encrypted-secret",
|
|
43
|
+
events: ["tenant.created"],
|
|
44
|
+
active: true,
|
|
45
|
+
created_at: "2024-01-01T00:00:00.000Z",
|
|
46
|
+
updated_at: "2024-01-01T00:00:00.000Z",
|
|
47
|
+
};
|
|
48
|
+
(0, vitest_1.beforeEach)(() => {
|
|
49
|
+
vitest_1.vi.clearAllMocks();
|
|
50
|
+
});
|
|
51
|
+
(0, vitest_1.describe)("secret encryption", () => {
|
|
52
|
+
(0, vitest_1.it)("encrypts and decrypts a secret round-trip", () => {
|
|
53
|
+
// We test the exported decryptSecret by creating an encrypted value
|
|
54
|
+
// through the createWebhook flow (which calls encryptSecret internally).
|
|
55
|
+
// For a direct test, we check that decryptSecret can recover the original.
|
|
56
|
+
(0, vitest_1.expect)(typeof webhookService.decryptSecret).toBe("function");
|
|
57
|
+
});
|
|
58
|
+
(0, vitest_1.it)("decryptSecret throws on invalid format", () => {
|
|
59
|
+
(0, vitest_1.expect)(() => webhookService.decryptSecret("invalid")).toThrow("Invalid encrypted value format");
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
(0, vitest_1.describe)("createWebhook", () => {
|
|
63
|
+
(0, vitest_1.it)("inserts webhook and returns it", async () => {
|
|
64
|
+
const pool = makeMockPool();
|
|
65
|
+
vitest_1.vi.mocked(poolHelpers.withClient).mockImplementation(async (_pool, fn) => {
|
|
66
|
+
const client = {
|
|
67
|
+
query: vitest_1.vi.fn().mockResolvedValue({ rows: [mockWebhook] }),
|
|
68
|
+
};
|
|
69
|
+
return fn(client);
|
|
70
|
+
});
|
|
71
|
+
const result = await webhookService.createWebhook(pool, {
|
|
72
|
+
tenant_id: null,
|
|
73
|
+
url: "https://example.com/webhook",
|
|
74
|
+
secret: "mysecretatleast16chars",
|
|
75
|
+
events: ["tenant.created"],
|
|
76
|
+
});
|
|
77
|
+
(0, vitest_1.expect)(result).toEqual(mockWebhook);
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
(0, vitest_1.describe)("getWebhook", () => {
|
|
81
|
+
(0, vitest_1.it)("returns webhook when found", async () => {
|
|
82
|
+
const pool = makeMockPool();
|
|
83
|
+
vitest_1.vi.mocked(poolHelpers.withClient).mockImplementation(async (_pool, fn) => {
|
|
84
|
+
const client = {
|
|
85
|
+
query: vitest_1.vi.fn().mockResolvedValue({ rows: [mockWebhook] }),
|
|
86
|
+
};
|
|
87
|
+
return fn(client);
|
|
88
|
+
});
|
|
89
|
+
const result = await webhookService.getWebhook(pool, mockWebhook.id);
|
|
90
|
+
(0, vitest_1.expect)(result).toEqual(mockWebhook);
|
|
91
|
+
});
|
|
92
|
+
(0, vitest_1.it)("throws WebhookNotFoundError when not found", async () => {
|
|
93
|
+
const pool = makeMockPool();
|
|
94
|
+
vitest_1.vi.mocked(poolHelpers.withClient).mockImplementation(async (_pool, fn) => {
|
|
95
|
+
const client = {
|
|
96
|
+
query: vitest_1.vi.fn().mockResolvedValue({ rows: [] }),
|
|
97
|
+
};
|
|
98
|
+
return fn(client);
|
|
99
|
+
});
|
|
100
|
+
await (0, vitest_1.expect)(webhookService.getWebhook(pool, "nonexistent-id")).rejects.toThrow(core_1.WebhookNotFoundError);
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
(0, vitest_1.describe)("listWebhooks", () => {
|
|
104
|
+
(0, vitest_1.it)("returns all webhooks when no tenantId", async () => {
|
|
105
|
+
const pool = makeMockPool();
|
|
106
|
+
vitest_1.vi.mocked(poolHelpers.withClient).mockImplementation(async (_pool, fn) => {
|
|
107
|
+
const client = {
|
|
108
|
+
query: vitest_1.vi.fn().mockResolvedValue({ rows: [mockWebhook] }),
|
|
109
|
+
};
|
|
110
|
+
return fn(client);
|
|
111
|
+
});
|
|
112
|
+
const result = await webhookService.listWebhooks(pool);
|
|
113
|
+
(0, vitest_1.expect)(result).toEqual([mockWebhook]);
|
|
114
|
+
});
|
|
115
|
+
(0, vitest_1.it)("filters by tenantId when provided", async () => {
|
|
116
|
+
const pool = makeMockPool();
|
|
117
|
+
const mockQuery = vitest_1.vi.fn().mockResolvedValue({ rows: [mockWebhook] });
|
|
118
|
+
vitest_1.vi.mocked(poolHelpers.withClient).mockImplementation(async (_pool, fn) => {
|
|
119
|
+
const client = {
|
|
120
|
+
query: mockQuery,
|
|
121
|
+
};
|
|
122
|
+
return fn(client);
|
|
123
|
+
});
|
|
124
|
+
await webhookService.listWebhooks(pool, "tenant-123");
|
|
125
|
+
// Should have used a parameterized query with tenant_id
|
|
126
|
+
(0, vitest_1.expect)(mockQuery).toHaveBeenCalledWith(vitest_1.expect.stringContaining("tenant_id"), ["tenant-123"]);
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
(0, vitest_1.describe)("updateWebhook", () => {
|
|
130
|
+
(0, vitest_1.it)("returns existing webhook when no fields to update", async () => {
|
|
131
|
+
const pool = makeMockPool();
|
|
132
|
+
vitest_1.vi.mocked(poolHelpers.withTransaction).mockImplementation(async (_pool, fn) => {
|
|
133
|
+
const client = {
|
|
134
|
+
query: vitest_1.vi.fn().mockResolvedValue({ rows: [mockWebhook] }),
|
|
135
|
+
};
|
|
136
|
+
return fn(client);
|
|
137
|
+
});
|
|
138
|
+
const result = await webhookService.updateWebhook(pool, mockWebhook.id, {});
|
|
139
|
+
(0, vitest_1.expect)(result).toEqual(mockWebhook);
|
|
140
|
+
});
|
|
141
|
+
(0, vitest_1.it)("throws WebhookNotFoundError when webhook not found", async () => {
|
|
142
|
+
const pool = makeMockPool();
|
|
143
|
+
vitest_1.vi.mocked(poolHelpers.withTransaction).mockImplementation(async (_pool, fn) => {
|
|
144
|
+
const client = {
|
|
145
|
+
query: vitest_1.vi.fn().mockResolvedValue({ rows: [] }),
|
|
146
|
+
};
|
|
147
|
+
return fn(client);
|
|
148
|
+
});
|
|
149
|
+
await (0, vitest_1.expect)(webhookService.updateWebhook(pool, "nonexistent", { active: false })).rejects.toThrow(core_1.WebhookNotFoundError);
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
(0, vitest_1.describe)("deleteWebhook", () => {
|
|
153
|
+
(0, vitest_1.it)("resolves when webhook exists", async () => {
|
|
154
|
+
const pool = makeMockPool();
|
|
155
|
+
vitest_1.vi.mocked(poolHelpers.withTransaction).mockImplementation(async (_pool, fn) => {
|
|
156
|
+
const client = {
|
|
157
|
+
query: vitest_1.vi.fn().mockResolvedValue({ rows: [{ id: mockWebhook.id }] }),
|
|
158
|
+
};
|
|
159
|
+
return fn(client);
|
|
160
|
+
});
|
|
161
|
+
await (0, vitest_1.expect)(webhookService.deleteWebhook(pool, mockWebhook.id)).resolves.toBeUndefined();
|
|
162
|
+
});
|
|
163
|
+
(0, vitest_1.it)("throws WebhookNotFoundError when webhook not found", async () => {
|
|
164
|
+
const pool = makeMockPool();
|
|
165
|
+
vitest_1.vi.mocked(poolHelpers.withTransaction).mockImplementation(async (_pool, fn) => {
|
|
166
|
+
const client = {
|
|
167
|
+
query: vitest_1.vi.fn().mockResolvedValue({ rows: [] }),
|
|
168
|
+
};
|
|
169
|
+
return fn(client);
|
|
170
|
+
});
|
|
171
|
+
await (0, vitest_1.expect)(webhookService.deleteWebhook(pool, "nonexistent-id")).rejects.toThrow(core_1.WebhookNotFoundError);
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
//# sourceMappingURL=webhook-service.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webhook-service.test.js","sourceRoot":"","sources":["../../../src/services/__tests__/webhook-service.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import pg from "pg";
|
|
2
|
+
export interface ApiKeyRecord {
|
|
3
|
+
id: string;
|
|
4
|
+
tenant_id: string | null;
|
|
5
|
+
key_hash: string;
|
|
6
|
+
key_prefix: string | null;
|
|
7
|
+
name: string | null;
|
|
8
|
+
created_at: Date;
|
|
9
|
+
last_used_at: Date | null;
|
|
10
|
+
revoked_at: Date | null;
|
|
11
|
+
expires_at: Date | null;
|
|
12
|
+
hash_version: number;
|
|
13
|
+
}
|
|
14
|
+
export interface CreatedApiKey {
|
|
15
|
+
id: string;
|
|
16
|
+
tenant_id: string | null;
|
|
17
|
+
key_prefix: string | null;
|
|
18
|
+
name: string | null;
|
|
19
|
+
created_at: Date;
|
|
20
|
+
/** Plaintext key — only returned on creation, never stored */
|
|
21
|
+
plaintext_key: string;
|
|
22
|
+
}
|
|
23
|
+
export declare function generateKey(keyPrefix: string): {
|
|
24
|
+
plaintextKey: string;
|
|
25
|
+
keyHash: string;
|
|
26
|
+
hashVersion: number;
|
|
27
|
+
};
|
|
28
|
+
export interface CreateApiKeyOptions {
|
|
29
|
+
name?: string;
|
|
30
|
+
expiresAt?: Date;
|
|
31
|
+
rateLimitMax?: number;
|
|
32
|
+
rateLimitWindow?: string;
|
|
33
|
+
}
|
|
34
|
+
export declare function createApiKey(pool: pg.Pool, keyPrefix: string, tenantId: string, nameOrOptions?: string | CreateApiKeyOptions, expiresAt?: Date): Promise<CreatedApiKey>;
|
|
35
|
+
export interface ValidatedApiKey {
|
|
36
|
+
tenant_id: string | null;
|
|
37
|
+
key_id: string;
|
|
38
|
+
scopes: string[];
|
|
39
|
+
rate_limit_max: number | null;
|
|
40
|
+
rate_limit_window: string | null;
|
|
41
|
+
}
|
|
42
|
+
export declare function validateApiKey(pool: pg.Pool, key: string): Promise<ValidatedApiKey | null>;
|
|
43
|
+
export declare function revokeApiKey(pool: pg.Pool, keyId: string): Promise<boolean>;
|
|
44
|
+
export declare function rotateApiKey(pool: pg.Pool, keyPrefix: string, oldKeyId: string, newName?: string): Promise<CreatedApiKey>;
|
|
45
|
+
export declare function listApiKeys(pool: pg.Pool, tenantId?: string): Promise<Array<{
|
|
46
|
+
id: string;
|
|
47
|
+
tenant_id: string | null;
|
|
48
|
+
name: string | null;
|
|
49
|
+
created_at: Date;
|
|
50
|
+
last_used_at: Date | null;
|
|
51
|
+
revoked_at: Date | null;
|
|
52
|
+
expires_at: Date | null;
|
|
53
|
+
}>>;
|
|
54
|
+
export declare function listDormantKeys(pool: pg.Pool, dormantDays?: number): Promise<Array<{
|
|
55
|
+
id: string;
|
|
56
|
+
tenant_id: string | null;
|
|
57
|
+
name: string | null;
|
|
58
|
+
last_used_at: Date | null;
|
|
59
|
+
created_at: Date;
|
|
60
|
+
}>>;
|
|
61
|
+
//# sourceMappingURL=api-key-service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-key-service.d.ts","sourceRoot":"","sources":["../../src/services/api-key-service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.listDormantKeys = exports.listApiKeys = exports.rotateApiKey = exports.revokeApiKey = exports.validateApiKey = exports.createApiKey = exports.generateKey = void 0;
|
|
7
|
+
const node_crypto_1 = __importDefault(require("node:crypto"));
|
|
8
|
+
const pool_helpers_js_1 = require("../pool-helpers.js");
|
|
9
|
+
// --- Hashing ---
|
|
10
|
+
const HASH_V1_SHA256 = 1;
|
|
11
|
+
const HASH_V2_HMAC = 2;
|
|
12
|
+
function getHmacSecret() {
|
|
13
|
+
return process.env.STRATUM_API_KEY_HMAC_SECRET;
|
|
14
|
+
}
|
|
15
|
+
/** Legacy SHA-256 hash (v1). */
|
|
16
|
+
function sha256Hash(key) {
|
|
17
|
+
return node_crypto_1.default.createHash("sha256").update(key).digest("hex");
|
|
18
|
+
}
|
|
19
|
+
/** HMAC-SHA256 hash (v2). Requires STRATUM_API_KEY_HMAC_SECRET. */
|
|
20
|
+
function hmacHash(key, secret) {
|
|
21
|
+
return node_crypto_1.default.createHmac("sha256", secret).update(key).digest("hex");
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Hash a key using the best available method.
|
|
25
|
+
* Returns HMAC-SHA256 if STRATUM_API_KEY_HMAC_SECRET is set, otherwise SHA-256.
|
|
26
|
+
*/
|
|
27
|
+
function hashKey(key) {
|
|
28
|
+
const secret = getHmacSecret();
|
|
29
|
+
if (secret) {
|
|
30
|
+
return { keyHash: hmacHash(key, secret), hashVersion: HASH_V2_HMAC };
|
|
31
|
+
}
|
|
32
|
+
return { keyHash: sha256Hash(key), hashVersion: HASH_V1_SHA256 };
|
|
33
|
+
}
|
|
34
|
+
function generateKey(keyPrefix) {
|
|
35
|
+
const random = node_crypto_1.default.randomBytes(32).toString("base64url");
|
|
36
|
+
const plaintextKey = `${keyPrefix}${random}`;
|
|
37
|
+
const { keyHash, hashVersion } = hashKey(plaintextKey);
|
|
38
|
+
return { plaintextKey, keyHash, hashVersion };
|
|
39
|
+
}
|
|
40
|
+
exports.generateKey = generateKey;
|
|
41
|
+
async function createApiKey(pool, keyPrefix, tenantId, nameOrOptions, expiresAt) {
|
|
42
|
+
// Support both old signature (name, expiresAt) and new options object
|
|
43
|
+
const opts = typeof nameOrOptions === "object" && nameOrOptions !== null
|
|
44
|
+
? nameOrOptions
|
|
45
|
+
: { name: nameOrOptions, expiresAt };
|
|
46
|
+
const { plaintextKey, keyHash, hashVersion } = generateKey(keyPrefix);
|
|
47
|
+
return (0, pool_helpers_js_1.withClient)(pool, async (client) => {
|
|
48
|
+
const res = await client.query(`INSERT INTO api_keys (tenant_id, key_hash, key_prefix, name, expires_at, rate_limit_max, rate_limit_window, hash_version)
|
|
49
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
|
|
50
|
+
RETURNING *`, [tenantId, keyHash, keyPrefix, opts.name ?? null, opts.expiresAt ?? null, opts.rateLimitMax ?? null, opts.rateLimitWindow ?? null, hashVersion]);
|
|
51
|
+
const row = res.rows[0];
|
|
52
|
+
return {
|
|
53
|
+
id: row.id,
|
|
54
|
+
tenant_id: row.tenant_id,
|
|
55
|
+
key_prefix: row.key_prefix,
|
|
56
|
+
name: row.name,
|
|
57
|
+
created_at: row.created_at,
|
|
58
|
+
plaintext_key: plaintextKey,
|
|
59
|
+
};
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
exports.createApiKey = createApiKey;
|
|
63
|
+
async function validateApiKey(pool, key) {
|
|
64
|
+
const hmacSecret = getHmacSecret();
|
|
65
|
+
// Build candidate hashes: try HMAC first (if secret is set), then SHA-256 fallback
|
|
66
|
+
const candidates = [];
|
|
67
|
+
if (hmacSecret) {
|
|
68
|
+
candidates.push({ hash: hmacHash(key, hmacSecret), version: HASH_V2_HMAC });
|
|
69
|
+
}
|
|
70
|
+
candidates.push({ hash: sha256Hash(key), version: HASH_V1_SHA256 });
|
|
71
|
+
return (0, pool_helpers_js_1.withClient)(pool, async (client) => {
|
|
72
|
+
for (const candidate of candidates) {
|
|
73
|
+
const res = await client.query(`SELECT id, tenant_id, key_hash, key_prefix, name, created_at, last_used_at, revoked_at, expires_at, scopes, rate_limit_max, rate_limit_window, hash_version
|
|
74
|
+
FROM api_keys WHERE key_hash = $1 AND revoked_at IS NULL AND (expires_at IS NULL OR expires_at > now())`, [candidate.hash]);
|
|
75
|
+
if (res.rows.length === 0)
|
|
76
|
+
continue;
|
|
77
|
+
const row = res.rows[0];
|
|
78
|
+
// Transparent upgrade: if we matched via legacy SHA-256 but HMAC secret is available,
|
|
79
|
+
// re-hash with HMAC and update the stored hash in-place
|
|
80
|
+
if (row.hash_version === HASH_V1_SHA256 && hmacSecret) {
|
|
81
|
+
const upgradedHash = hmacHash(key, hmacSecret);
|
|
82
|
+
pool
|
|
83
|
+
.query(`UPDATE api_keys SET key_hash = $1, hash_version = $2, last_used_at = now() WHERE id = $3`, [upgradedHash, HASH_V2_HMAC, row.id])
|
|
84
|
+
.catch(() => {
|
|
85
|
+
// Non-critical: upgrade will happen on next request
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
// Update last_used_at — fire-and-forget
|
|
90
|
+
pool
|
|
91
|
+
.query(`UPDATE api_keys SET last_used_at = now() WHERE id = $1`, [row.id])
|
|
92
|
+
.catch(() => { });
|
|
93
|
+
}
|
|
94
|
+
return {
|
|
95
|
+
tenant_id: row.tenant_id,
|
|
96
|
+
key_id: row.id,
|
|
97
|
+
scopes: row.scopes ?? ["read"],
|
|
98
|
+
rate_limit_max: row.rate_limit_max,
|
|
99
|
+
rate_limit_window: row.rate_limit_window,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
return null;
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
exports.validateApiKey = validateApiKey;
|
|
106
|
+
async function revokeApiKey(pool, keyId) {
|
|
107
|
+
return (0, pool_helpers_js_1.withTransaction)(pool, async (client) => {
|
|
108
|
+
const res = await client.query(`UPDATE api_keys SET revoked_at = now() WHERE id = $1 AND revoked_at IS NULL RETURNING id`, [keyId]);
|
|
109
|
+
return res.rows.length > 0;
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
exports.revokeApiKey = revokeApiKey;
|
|
113
|
+
async function rotateApiKey(pool, keyPrefix, oldKeyId, newName) {
|
|
114
|
+
return (0, pool_helpers_js_1.withTransaction)(pool, async (client) => {
|
|
115
|
+
// Verify old key exists and is not revoked
|
|
116
|
+
const oldRes = await client.query(`SELECT id, tenant_id, name, key_prefix FROM api_keys WHERE id = $1 AND revoked_at IS NULL`, [oldKeyId]);
|
|
117
|
+
if (oldRes.rows.length === 0) {
|
|
118
|
+
throw new Error(`API key not found or already revoked: ${oldKeyId}`);
|
|
119
|
+
}
|
|
120
|
+
const old = oldRes.rows[0];
|
|
121
|
+
// Create new key for same tenant
|
|
122
|
+
const { plaintextKey, keyHash, hashVersion } = generateKey(keyPrefix);
|
|
123
|
+
const res = await client.query(`INSERT INTO api_keys (tenant_id, key_hash, key_prefix, name, hash_version)
|
|
124
|
+
VALUES ($1, $2, $3, $4, $5)
|
|
125
|
+
RETURNING id, tenant_id, name, key_prefix, created_at`, [old.tenant_id, keyHash, keyPrefix, newName ?? `${old.name ?? "key"} (rotated)`, hashVersion]);
|
|
126
|
+
// Revoke old key
|
|
127
|
+
await client.query(`UPDATE api_keys SET revoked_at = now() WHERE id = $1`, [oldKeyId]);
|
|
128
|
+
const row = res.rows[0];
|
|
129
|
+
return {
|
|
130
|
+
id: row.id,
|
|
131
|
+
tenant_id: row.tenant_id,
|
|
132
|
+
key_prefix: row.key_prefix,
|
|
133
|
+
name: row.name,
|
|
134
|
+
created_at: row.created_at,
|
|
135
|
+
plaintext_key: plaintextKey,
|
|
136
|
+
};
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
exports.rotateApiKey = rotateApiKey;
|
|
140
|
+
async function listApiKeys(pool, tenantId) {
|
|
141
|
+
return (0, pool_helpers_js_1.withClient)(pool, async (client) => {
|
|
142
|
+
if (tenantId) {
|
|
143
|
+
const res = await client.query(`SELECT id, tenant_id, name, created_at, last_used_at, revoked_at, expires_at
|
|
144
|
+
FROM api_keys WHERE tenant_id = $1 ORDER BY created_at DESC`, [tenantId]);
|
|
145
|
+
return res.rows;
|
|
146
|
+
}
|
|
147
|
+
const res = await client.query(`SELECT id, tenant_id, name, created_at, last_used_at, revoked_at, expires_at
|
|
148
|
+
FROM api_keys ORDER BY created_at DESC`);
|
|
149
|
+
return res.rows;
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
exports.listApiKeys = listApiKeys;
|
|
153
|
+
async function listDormantKeys(pool, dormantDays = 90) {
|
|
154
|
+
return (0, pool_helpers_js_1.withClient)(pool, async (client) => {
|
|
155
|
+
const res = await client.query(`SELECT id, tenant_id, name, last_used_at, created_at
|
|
156
|
+
FROM api_keys
|
|
157
|
+
WHERE revoked_at IS NULL
|
|
158
|
+
AND (last_used_at IS NULL OR last_used_at < now() - ($1 || ' days')::interval)
|
|
159
|
+
ORDER BY last_used_at ASC NULLS FIRST`, [dormantDays]);
|
|
160
|
+
return res.rows;
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
exports.listDormantKeys = listDormantKeys;
|
|
164
|
+
//# sourceMappingURL=api-key-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-key-service.js","sourceRoot":"","sources":["../../src/services/api-key-service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import pg from "pg";
|
|
2
|
+
import type { AuditContext, AuditEntry, AuditLogQuery } from "@stratum-hq/core";
|
|
3
|
+
export declare function createAuditEntry(pool: pg.Pool, context: AuditContext, action: string, resourceType: string, resourceId: string | null, tenantId: string | null, beforeState?: Record<string, unknown> | null, afterState?: Record<string, unknown> | null, metadata?: Record<string, unknown>): Promise<AuditEntry>;
|
|
4
|
+
export declare function queryAuditLogs(pool: pg.Pool, query: AuditLogQuery): Promise<AuditEntry[]>;
|
|
5
|
+
export declare function getAuditEntry(pool: pg.Pool, id: string): Promise<AuditEntry | null>;
|
|
6
|
+
//# sourceMappingURL=audit-service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit-service.d.ts","sourceRoot":"","sources":["../../src/services/audit-service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AAEpB,OAAO,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEhF,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,EAAE,CAAC,IAAI,EACb,OAAO,EAAE,YAAY,EACrB,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,MAAM,GAAG,IAAI,EACzB,QAAQ,EAAE,MAAM,GAAG,IAAI,EACvB,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,EAC5C,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,EAC3C,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACjC,OAAO,CAAC,UAAU,CAAC,CAwBrB;AAED,wBAAsB,cAAc,CAClC,IAAI,EAAE,EAAE,CAAC,IAAI,EACb,KAAK,EAAE,aAAa,GACnB,OAAO,CAAC,UAAU,EAAE,CAAC,CAkDvB;AAED,wBAAsB,aAAa,CACjC,IAAI,EAAE,EAAE,CAAC,IAAI,EACb,EAAE,EAAE,MAAM,GACT,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAY5B"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getAuditEntry = exports.queryAuditLogs = exports.createAuditEntry = void 0;
|
|
4
|
+
const pool_helpers_js_1 = require("../pool-helpers.js");
|
|
5
|
+
async function createAuditEntry(pool, context, action, resourceType, resourceId, tenantId, beforeState, afterState, metadata) {
|
|
6
|
+
return (0, pool_helpers_js_1.withClient)(pool, async (client) => {
|
|
7
|
+
const res = await client.query(`INSERT INTO audit_logs (actor_id, actor_type, action, resource_type, resource_id, tenant_id, source_ip, request_id, before_state, after_state, metadata)
|
|
8
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)
|
|
9
|
+
RETURNING id, actor_id, actor_type, action, resource_type, resource_id, tenant_id,
|
|
10
|
+
source_ip::text as source_ip, request_id, before_state, after_state, metadata,
|
|
11
|
+
created_at::text as created_at`, [
|
|
12
|
+
context.actor_id,
|
|
13
|
+
context.actor_type,
|
|
14
|
+
action,
|
|
15
|
+
resourceType,
|
|
16
|
+
resourceId,
|
|
17
|
+
tenantId,
|
|
18
|
+
context.source_ip ?? null,
|
|
19
|
+
context.request_id ?? null,
|
|
20
|
+
beforeState ? JSON.stringify(beforeState) : null,
|
|
21
|
+
afterState ? JSON.stringify(afterState) : null,
|
|
22
|
+
JSON.stringify(metadata ?? {}),
|
|
23
|
+
]);
|
|
24
|
+
return res.rows[0];
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
exports.createAuditEntry = createAuditEntry;
|
|
28
|
+
async function queryAuditLogs(pool, query) {
|
|
29
|
+
return (0, pool_helpers_js_1.withClient)(pool, async (client) => {
|
|
30
|
+
const conditions = [];
|
|
31
|
+
const values = [];
|
|
32
|
+
let idx = 1;
|
|
33
|
+
if (query.tenant_id !== undefined) {
|
|
34
|
+
conditions.push(`tenant_id = $${idx++}`);
|
|
35
|
+
values.push(query.tenant_id);
|
|
36
|
+
}
|
|
37
|
+
if (query.action !== undefined) {
|
|
38
|
+
conditions.push(`action = $${idx++}`);
|
|
39
|
+
values.push(query.action);
|
|
40
|
+
}
|
|
41
|
+
if (query.resource_type !== undefined) {
|
|
42
|
+
conditions.push(`resource_type = $${idx++}`);
|
|
43
|
+
values.push(query.resource_type);
|
|
44
|
+
}
|
|
45
|
+
if (query.actor_id !== undefined) {
|
|
46
|
+
conditions.push(`actor_id = $${idx++}`);
|
|
47
|
+
values.push(query.actor_id);
|
|
48
|
+
}
|
|
49
|
+
if (query.from !== undefined) {
|
|
50
|
+
conditions.push(`created_at >= $${idx++}`);
|
|
51
|
+
values.push(query.from);
|
|
52
|
+
}
|
|
53
|
+
if (query.to !== undefined) {
|
|
54
|
+
conditions.push(`created_at <= $${idx++}`);
|
|
55
|
+
values.push(query.to);
|
|
56
|
+
}
|
|
57
|
+
if (query.cursor !== undefined) {
|
|
58
|
+
conditions.push(`id < $${idx++}`);
|
|
59
|
+
values.push(query.cursor);
|
|
60
|
+
}
|
|
61
|
+
const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
62
|
+
values.push(query.limit ?? 100);
|
|
63
|
+
const res = await client.query(`SELECT id, actor_id, actor_type, action, resource_type, resource_id, tenant_id,
|
|
64
|
+
source_ip::text as source_ip, request_id, before_state, after_state, metadata,
|
|
65
|
+
created_at::text as created_at
|
|
66
|
+
FROM audit_logs
|
|
67
|
+
${where}
|
|
68
|
+
ORDER BY created_at DESC, id DESC
|
|
69
|
+
LIMIT $${idx}`, values);
|
|
70
|
+
return res.rows;
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
exports.queryAuditLogs = queryAuditLogs;
|
|
74
|
+
async function getAuditEntry(pool, id) {
|
|
75
|
+
return (0, pool_helpers_js_1.withClient)(pool, async (client) => {
|
|
76
|
+
const res = await client.query(`SELECT id, actor_id, actor_type, action, resource_type, resource_id, tenant_id,
|
|
77
|
+
source_ip::text as source_ip, request_id, before_state, after_state, metadata,
|
|
78
|
+
created_at::text as created_at
|
|
79
|
+
FROM audit_logs
|
|
80
|
+
WHERE id = $1`, [id]);
|
|
81
|
+
return res.rows[0] ?? null;
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
exports.getAuditEntry = getAuditEntry;
|
|
85
|
+
//# sourceMappingURL=audit-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit-service.js","sourceRoot":"","sources":["../../src/services/audit-service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import pg from "pg";
|
|
2
|
+
import { type ConfigEntry, type SetConfigInput, type ResolvedConfig, type BatchSetConfigResult } from "@stratum-hq/core";
|
|
3
|
+
/**
|
|
4
|
+
* Resolve the effective config for a tenant by batch-loading ancestor configs
|
|
5
|
+
* in a single query and walking root→leaf.
|
|
6
|
+
*/
|
|
7
|
+
export declare function resolveConfig(pool: pg.Pool, tenantId: string): Promise<ResolvedConfig>;
|
|
8
|
+
/**
|
|
9
|
+
* Set (upsert) a config key for a tenant.
|
|
10
|
+
* Rejects if the key is locked by an ancestor.
|
|
11
|
+
*/
|
|
12
|
+
export declare function setConfig(pool: pg.Pool, tenantId: string, key: string, input: SetConfigInput): Promise<ConfigEntry>;
|
|
13
|
+
/**
|
|
14
|
+
* Set multiple config keys for a tenant in a single transaction.
|
|
15
|
+
* Partial success: locked keys are skipped and reported as errors,
|
|
16
|
+
* while unlocked keys are set successfully within the same transaction.
|
|
17
|
+
*/
|
|
18
|
+
export declare function batchSetConfig(pool: pg.Pool, tenantId: string, entries: Array<{
|
|
19
|
+
key: string;
|
|
20
|
+
value: unknown;
|
|
21
|
+
locked?: boolean;
|
|
22
|
+
sensitive?: boolean;
|
|
23
|
+
}>): Promise<BatchSetConfigResult>;
|
|
24
|
+
/**
|
|
25
|
+
* Delete a config override for a tenant, revealing the inherited parent value.
|
|
26
|
+
*/
|
|
27
|
+
export declare function deleteConfig(pool: pg.Pool, tenantId: string, key: string): Promise<void>;
|
|
28
|
+
/**
|
|
29
|
+
* Return all config entries for a tenant showing inheritance status:
|
|
30
|
+
* inherited (from ancestor), overridden (tenant has own value), or locked.
|
|
31
|
+
*/
|
|
32
|
+
export declare function getConfigWithInheritance(pool: pg.Pool, tenantId: string): Promise<ResolvedConfig>;
|
|
33
|
+
//# sourceMappingURL=config-service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-service.d.ts","sourceRoot":"","sources":["../../src/services/config-service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AAEpB,OAAO,EACL,KAAK,WAAW,EAChB,KAAK,cAAc,EAEnB,KAAK,cAAc,EACnB,KAAK,oBAAoB,EAO1B,MAAM,kBAAkB,CAAC;AAG1B;;;GAGG;AACH,wBAAsB,aAAa,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAyE5F;AAED;;;GAGG;AACH,wBAAsB,SAAS,CAC7B,IAAI,EAAE,EAAE,CAAC,IAAI,EACb,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,cAAc,GACpB,OAAO,CAAC,WAAW,CAAC,CA8CtB;AAED;;;;GAIG;AACH,wBAAsB,cAAc,CAClC,IAAI,EAAE,EAAE,CAAC,IAAI,EACb,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,KAAK,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAAC,SAAS,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC,GACrF,OAAO,CAAC,oBAAoB,CAAC,CAuE/B;AAED;;GAEG;AACH,wBAAsB,YAAY,CAChC,IAAI,EAAE,EAAE,CAAC,IAAI,EACb,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,IAAI,CAAC,CAUf;AAED;;;GAGG;AACH,wBAAsB,wBAAwB,CAC5C,IAAI,EAAE,EAAE,CAAC,IAAI,EACb,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,cAAc,CAAC,CA0GzB"}
|