@pikku/kysely 0.12.12 → 0.12.14
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/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,28 @@
|
|
|
1
|
+
## 0.12.14
|
|
2
|
+
|
|
3
|
+
### Patch Changes
|
|
4
|
+
|
|
5
|
+
- 4b5c75b: feat(auth-js): wire OIDC config (issuer/tenantId) as variables, expand provider registry
|
|
6
|
+
- Move `issuer` and `tenantId` out of the secret blob for OIDC providers (auth0, okta, azure-ad, keycloak, cognito, microsoft-entra-id) — they are public config URLs, not secrets. Now registered via `wireVariable` and loaded at runtime via `services.variables.get()`.
|
|
7
|
+
- Expand provider registry from 13 to 31 providers: reddit, notion, instagram, zoom, figma, tiktok, threads, patreon, dropbox, bitbucket, hubspot, salesforce, atlassian, strava, keycloak, cognito, microsoft-entra-id added.
|
|
8
|
+
- `serialize-auth-gen` emits `wireVariable({...})` declarations and `services.variables.get()` calls in the generated factory for OIDC providers.
|
|
9
|
+
- Integration verifier exercises real `/auth/providers` endpoint with `LocalSecretService` + `LocalVariablesService`, including a spy test proving `services.variables.get('AUTH0_ISSUER')` is called at request time.
|
|
10
|
+
|
|
11
|
+
- Updated dependencies [4b5c75b]
|
|
12
|
+
- Updated dependencies [4b5c75b]
|
|
13
|
+
- @pikku/core@0.12.27
|
|
14
|
+
|
|
15
|
+
## 0.12.13
|
|
16
|
+
|
|
17
|
+
### Patch Changes
|
|
18
|
+
|
|
19
|
+
- 909eb25: Add audit logging support for function invocations and database queries.
|
|
20
|
+
|
|
21
|
+
Introduces `AuditService` and `createAuditedKysely` — configurable audit capture with best-effort and transactional durability modes. Audit logs capture session metadata (user, org), RPC call details, and Kysely query operations (type, tables, changes). Audit context is scoped per-invocation so nested RPC calls are correctly attributed.
|
|
22
|
+
|
|
23
|
+
- Updated dependencies [909eb25]
|
|
24
|
+
- @pikku/core@0.12.26
|
|
25
|
+
|
|
1
26
|
## 0.12.12
|
|
2
27
|
|
|
3
28
|
### Patch Changes
|
|
@@ -20,10 +20,10 @@ export declare class KyselySecretService implements SecretService {
|
|
|
20
20
|
init(): Promise<void>;
|
|
21
21
|
private logAudit;
|
|
22
22
|
private getKEK;
|
|
23
|
-
getSecret(key: string): Promise<
|
|
24
|
-
getSecretJSON<R = {}>(key: string): Promise<R>;
|
|
23
|
+
getSecret<T = string>(key: string): Promise<T>;
|
|
25
24
|
hasSecret(key: string): Promise<boolean>;
|
|
26
|
-
|
|
25
|
+
setSecret(key: string, value: unknown): Promise<void>;
|
|
27
26
|
deleteSecret(key: string): Promise<void>;
|
|
27
|
+
getSecrets(keys: string[]): Promise<Record<string, unknown>>;
|
|
28
28
|
rotateKEK(): Promise<number>;
|
|
29
29
|
}
|
|
@@ -76,10 +76,6 @@ export class KyselySecretService {
|
|
|
76
76
|
await this.logAudit(key, 'read');
|
|
77
77
|
return result;
|
|
78
78
|
}
|
|
79
|
-
async getSecretJSON(key) {
|
|
80
|
-
const raw = await this.getSecret(key);
|
|
81
|
-
return JSON.parse(raw);
|
|
82
|
-
}
|
|
83
79
|
async hasSecret(key) {
|
|
84
80
|
const row = await this.db
|
|
85
81
|
.selectFrom('secrets')
|
|
@@ -88,9 +84,8 @@ export class KyselySecretService {
|
|
|
88
84
|
.executeTakeFirst();
|
|
89
85
|
return !!row;
|
|
90
86
|
}
|
|
91
|
-
async
|
|
92
|
-
const
|
|
93
|
-
const { ciphertext, wrappedDEK } = await envelopeEncrypt(this.key, plaintext);
|
|
87
|
+
async setSecret(key, value) {
|
|
88
|
+
const { ciphertext, wrappedDEK } = await envelopeEncrypt(this.key, value);
|
|
94
89
|
const now = new Date().toISOString();
|
|
95
90
|
await this.db
|
|
96
91
|
.insertInto('secrets')
|
|
@@ -115,6 +110,24 @@ export class KyselySecretService {
|
|
|
115
110
|
await this.db.deleteFrom('secrets').where('key', '=', key).execute();
|
|
116
111
|
await this.logAudit(key, 'delete');
|
|
117
112
|
}
|
|
113
|
+
async getSecrets(keys) {
|
|
114
|
+
const rows = await this.db
|
|
115
|
+
.selectFrom('secrets')
|
|
116
|
+
.select(['key', 'ciphertext', 'wrappedDek', 'keyVersion'])
|
|
117
|
+
.where('key', 'in', keys)
|
|
118
|
+
.execute();
|
|
119
|
+
const out = {};
|
|
120
|
+
for (const row of rows) {
|
|
121
|
+
try {
|
|
122
|
+
const kek = this.getKEK(row.keyVersion);
|
|
123
|
+
out[row.key] = await envelopeDecrypt(kek, row.ciphertext, row.wrappedDek);
|
|
124
|
+
}
|
|
125
|
+
catch {
|
|
126
|
+
// skip secrets that fail to decrypt
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return out;
|
|
130
|
+
}
|
|
118
131
|
async rotateKEK() {
|
|
119
132
|
if (!this.previousKey) {
|
|
120
133
|
throw new Error('No previousKey configured — nothing to rotate from');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pikku/kysely",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.14",
|
|
4
4
|
"author": "yasser.fadl@gmail.com",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"module": "dist/src/index.js",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"prepublishOnly": "yarn build"
|
|
21
21
|
},
|
|
22
22
|
"peerDependencies": {
|
|
23
|
-
"@pikku/core": "^0.12.
|
|
23
|
+
"@pikku/core": "^0.12.27"
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
26
|
"kysely": "^0.28.12"
|
|
@@ -33,5 +33,8 @@
|
|
|
33
33
|
"kysely-postgres-js": "^3.0.0",
|
|
34
34
|
"postgres": "^3.4.8",
|
|
35
35
|
"typescript": "^5.9"
|
|
36
|
+
},
|
|
37
|
+
"exports": {
|
|
38
|
+
".": "./dist/src/index.js"
|
|
36
39
|
}
|
|
37
40
|
}
|
|
@@ -93,7 +93,7 @@ export class KyselySecretService implements SecretService {
|
|
|
93
93
|
throw new Error(`No KEK available for key_version ${version}`)
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
-
async getSecret(key: string): Promise<
|
|
96
|
+
async getSecret<T = string>(key: string): Promise<T> {
|
|
97
97
|
const row = await this.db
|
|
98
98
|
.selectFrom('secrets')
|
|
99
99
|
.select(['ciphertext', 'wrappedDek', 'keyVersion'])
|
|
@@ -103,20 +103,11 @@ export class KyselySecretService implements SecretService {
|
|
|
103
103
|
if (!row) throw new Error('Requested secret not found')
|
|
104
104
|
|
|
105
105
|
const kek = this.getKEK(row.keyVersion)
|
|
106
|
-
const result = await envelopeDecrypt<
|
|
107
|
-
kek,
|
|
108
|
-
row.ciphertext,
|
|
109
|
-
row.wrappedDek
|
|
110
|
-
)
|
|
106
|
+
const result = await envelopeDecrypt<T>(kek, row.ciphertext, row.wrappedDek)
|
|
111
107
|
await this.logAudit(key, 'read')
|
|
112
108
|
return result
|
|
113
109
|
}
|
|
114
110
|
|
|
115
|
-
async getSecretJSON<R = {}>(key: string): Promise<R> {
|
|
116
|
-
const raw = await this.getSecret(key)
|
|
117
|
-
return JSON.parse(raw) as R
|
|
118
|
-
}
|
|
119
|
-
|
|
120
111
|
async hasSecret(key: string): Promise<boolean> {
|
|
121
112
|
const row = await this.db
|
|
122
113
|
.selectFrom('secrets')
|
|
@@ -126,12 +117,8 @@ export class KyselySecretService implements SecretService {
|
|
|
126
117
|
return !!row
|
|
127
118
|
}
|
|
128
119
|
|
|
129
|
-
async
|
|
130
|
-
const
|
|
131
|
-
const { ciphertext, wrappedDEK } = await envelopeEncrypt(
|
|
132
|
-
this.key,
|
|
133
|
-
plaintext
|
|
134
|
-
)
|
|
120
|
+
async setSecret(key: string, value: unknown): Promise<void> {
|
|
121
|
+
const { ciphertext, wrappedDEK } = await envelopeEncrypt(this.key, value)
|
|
135
122
|
const now = new Date().toISOString()
|
|
136
123
|
|
|
137
124
|
await this.db
|
|
@@ -162,6 +149,29 @@ export class KyselySecretService implements SecretService {
|
|
|
162
149
|
await this.logAudit(key, 'delete')
|
|
163
150
|
}
|
|
164
151
|
|
|
152
|
+
async getSecrets(keys: string[]): Promise<Record<string, unknown>> {
|
|
153
|
+
const rows = await this.db
|
|
154
|
+
.selectFrom('secrets')
|
|
155
|
+
.select(['key', 'ciphertext', 'wrappedDek', 'keyVersion'])
|
|
156
|
+
.where('key', 'in', keys)
|
|
157
|
+
.execute()
|
|
158
|
+
|
|
159
|
+
const out: Record<string, unknown> = {}
|
|
160
|
+
for (const row of rows) {
|
|
161
|
+
try {
|
|
162
|
+
const kek = this.getKEK(row.keyVersion)
|
|
163
|
+
out[row.key] = await envelopeDecrypt(
|
|
164
|
+
kek,
|
|
165
|
+
row.ciphertext,
|
|
166
|
+
row.wrappedDek
|
|
167
|
+
)
|
|
168
|
+
} catch {
|
|
169
|
+
// skip secrets that fail to decrypt
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return out
|
|
173
|
+
}
|
|
174
|
+
|
|
165
175
|
async rotateKEK(): Promise<number> {
|
|
166
176
|
if (!this.previousKey) {
|
|
167
177
|
throw new Error('No previousKey configured — nothing to rotate from')
|
|
@@ -128,7 +128,7 @@ function registerTests(
|
|
|
128
128
|
auditReads: true,
|
|
129
129
|
})
|
|
130
130
|
await service.init()
|
|
131
|
-
await service.
|
|
131
|
+
await service.setSecret('audit-test', 'value')
|
|
132
132
|
await service.getSecret('audit-test')
|
|
133
133
|
await service.deleteSecret('audit-test')
|
|
134
134
|
|
|
@@ -152,7 +152,7 @@ function registerTests(
|
|
|
152
152
|
auditReads: false,
|
|
153
153
|
})
|
|
154
154
|
await service.init()
|
|
155
|
-
await service.
|
|
155
|
+
await service.setSecret('no-read-audit', 'value')
|
|
156
156
|
await service.getSecret('no-read-audit')
|
|
157
157
|
|
|
158
158
|
const logs = await getDb()
|