@tumbaland/backend-core 1.37.0 → 1.39.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/apiKeys/ApiKey.d.ts +23 -3
- package/dist/apiKeys/ApiKey.d.ts.map +1 -1
- package/dist/apiKeys/ApiKey.js +19 -2
- package/dist/apiKeys/ApiKey.js.map +1 -1
- package/dist/apiKeys/index.d.ts +2 -2
- package/dist/apiKeys/index.d.ts.map +1 -1
- package/dist/apiKeys/index.js +5 -1
- package/dist/apiKeys/index.js.map +1 -1
- package/dist/apiKeys/middleware.d.ts +17 -3
- package/dist/apiKeys/middleware.d.ts.map +1 -1
- package/dist/apiKeys/middleware.js +71 -34
- package/dist/apiKeys/middleware.js.map +1 -1
- package/dist/apiKeys/service.d.ts +5 -3
- package/dist/apiKeys/service.d.ts.map +1 -1
- package/dist/apiKeys/service.js +11 -3
- package/dist/apiKeys/service.js.map +1 -1
- package/dist/apiKeys/types.d.ts +43 -8
- package/dist/apiKeys/types.d.ts.map +1 -1
- package/dist/apiKeys/types.js +35 -1
- package/dist/apiKeys/types.js.map +1 -1
- package/dist/audit/AuditEvent.d.ts +51 -0
- package/dist/audit/AuditEvent.d.ts.map +1 -0
- package/dist/audit/AuditEvent.js +69 -0
- package/dist/audit/AuditEvent.js.map +1 -0
- package/dist/audit/actor.d.ts +41 -0
- package/dist/audit/actor.d.ts.map +1 -0
- package/dist/audit/actor.js +39 -0
- package/dist/audit/actor.js.map +1 -0
- package/dist/audit/context.d.ts +40 -0
- package/dist/audit/context.d.ts.map +1 -0
- package/dist/audit/context.js +60 -0
- package/dist/audit/context.js.map +1 -0
- package/dist/audit/index.d.ts +12 -0
- package/dist/audit/index.d.ts.map +1 -0
- package/dist/audit/index.js +22 -0
- package/dist/audit/index.js.map +1 -0
- package/dist/audit/plugin.d.ts +23 -0
- package/dist/audit/plugin.d.ts.map +1 -0
- package/dist/audit/plugin.js +226 -0
- package/dist/audit/plugin.js.map +1 -0
- package/dist/audit/reads.d.ts +47 -0
- package/dist/audit/reads.d.ts.map +1 -0
- package/dist/audit/reads.js +94 -0
- package/dist/audit/reads.js.map +1 -0
- package/dist/audit/service.d.ts +49 -0
- package/dist/audit/service.d.ts.map +1 -0
- package/dist/audit/service.js +65 -0
- package/dist/audit/service.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/oauth/models.d.ts +13 -2
- package/dist/oauth/models.d.ts.map +1 -1
- package/dist/oauth/models.js +19 -0
- package/dist/oauth/models.js.map +1 -1
- package/dist/oauth/service.d.ts +15 -8
- package/dist/oauth/service.d.ts.map +1 -1
- package/dist/oauth/service.js +10 -3
- package/dist/oauth/service.js.map +1 -1
- package/dist/oauth/tokens.d.ts +37 -5
- package/dist/oauth/tokens.d.ts.map +1 -1
- package/dist/oauth/tokens.js +9 -2
- package/dist/oauth/tokens.js.map +1 -1
- package/package.json +1 -1
- package/src/apiKeys/ApiKey.test.ts +25 -1
- package/src/apiKeys/ApiKey.ts +31 -4
- package/src/apiKeys/index.ts +16 -2
- package/src/apiKeys/middleware.test.ts +126 -20
- package/src/apiKeys/middleware.ts +99 -39
- package/src/apiKeys/service.test.ts +58 -9
- package/src/apiKeys/service.ts +25 -6
- package/src/apiKeys/types.ts +63 -8
- package/src/audit/AuditEvent.ts +79 -0
- package/src/audit/actor.test.ts +95 -0
- package/src/audit/actor.ts +68 -0
- package/src/audit/context.test.ts +91 -0
- package/src/audit/context.ts +83 -0
- package/src/audit/index.ts +11 -0
- package/src/audit/plugin.test.ts +258 -0
- package/src/audit/plugin.ts +254 -0
- package/src/audit/reads.test.ts +164 -0
- package/src/audit/reads.ts +88 -0
- package/src/audit/service.test.ts +115 -0
- package/src/audit/service.ts +92 -0
- package/src/index.ts +1 -0
- package/src/middleware/authMiddleware.test.ts +6 -1
- package/src/oauth/models.ts +32 -2
- package/src/oauth/service.test.ts +58 -6
- package/src/oauth/service.ts +25 -7
- package/src/oauth/tokens.test.ts +22 -5
- package/src/oauth/tokens.ts +52 -7
package/dist/apiKeys/ApiKey.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import mongoose, { Document } from 'mongoose';
|
|
2
|
-
import type
|
|
2
|
+
import { type ApiKeyScope, type Tenant } from './types';
|
|
3
3
|
/**
|
|
4
4
|
* One API key. Lives in backend-core rather than auth-service because both
|
|
5
5
|
* halves need it: auth-service issues and revokes keys, and every other service
|
|
@@ -30,8 +30,17 @@ export interface IApiKey extends Document {
|
|
|
30
30
|
sealedIv: string;
|
|
31
31
|
sealedTag: string;
|
|
32
32
|
scopes: ApiKeyScope[];
|
|
33
|
-
/**
|
|
34
|
-
|
|
33
|
+
/** every tenant this key may act in; see `Tenant` */
|
|
34
|
+
tenants: Tenant[];
|
|
35
|
+
/** the one it acts in when a request names none; always a member of `tenants` */
|
|
36
|
+
defaultTenant: Tenant;
|
|
37
|
+
/**
|
|
38
|
+
* The single tenant this key was pinned to, before tenants were a set.
|
|
39
|
+
*
|
|
40
|
+
* Kept so keys issued under the old model keep working; `readTenants` falls
|
|
41
|
+
* back to it. Nothing writes it any more.
|
|
42
|
+
*/
|
|
43
|
+
groupId?: string | null;
|
|
35
44
|
lastUsedAt?: Date;
|
|
36
45
|
expiresAt?: Date;
|
|
37
46
|
revokedAt?: Date;
|
|
@@ -41,6 +50,17 @@ export interface IApiKey extends Document {
|
|
|
41
50
|
createdAt: Date;
|
|
42
51
|
updatedAt: Date;
|
|
43
52
|
}
|
|
53
|
+
/**
|
|
54
|
+
* Issuing and revoking a key is worth a record — arguably more than anything a
|
|
55
|
+
* key goes on to do, since a credential nobody remembers creating is the one
|
|
56
|
+
* that matters.
|
|
57
|
+
*
|
|
58
|
+
* Every secret-bearing field is redacted. The trail is readable by the account
|
|
59
|
+
* owner and kept for a year; a snapshot carrying `hash` or the sealed
|
|
60
|
+
* ciphertext would put credential material into a second collection with a
|
|
61
|
+
* different lifetime, which is precisely the thing the encryption is for.
|
|
62
|
+
*/
|
|
63
|
+
export declare const API_KEY_SECRET_FIELDS: string[];
|
|
44
64
|
export declare const ApiKey: mongoose.Model<IApiKey, {}, {}, {}, mongoose.Document<unknown, {}, IApiKey, {}, mongoose.DefaultSchemaOptions> & IApiKey & Required<{
|
|
45
65
|
_id: mongoose.Types.ObjectId;
|
|
46
66
|
}> & {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ApiKey.d.ts","sourceRoot":"","sources":["../../src/apiKeys/ApiKey.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,EAAE,EAAE,QAAQ,EAAU,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"ApiKey.d.ts","sourceRoot":"","sources":["../../src/apiKeys/ApiKey.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,EAAE,EAAE,QAAQ,EAAU,MAAM,UAAU,CAAC;AAEtD,OAAO,EAAmB,KAAK,WAAW,EAAE,KAAK,MAAM,EAAE,MAAM,SAAS,CAAC;AAEzE;;;;;GAKG;AACH,MAAM,WAAW,OAAQ,SAAQ,QAAQ;IACvC,MAAM,EAAE,MAAM,CAAC;IACf;;;;;;;;OAQG;IACH,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,gEAAgE;IAChE,IAAI,EAAE,MAAM,CAAC;IACb,qFAAqF;IACrF,KAAK,EAAE,MAAM,CAAC;IACd,+EAA+E;IAC/E,IAAI,EAAE,MAAM,CAAC;IACb,sEAAsE;IACtE,gBAAgB,EAAE,MAAM,CAAC;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,qDAAqD;IACrD,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,iFAAiF;IACjF,aAAa,EAAE,MAAM,CAAC;IACtB;;;;;OAKG;IACH,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,UAAU,CAAC,EAAE,IAAI,CAAC;IAClB,SAAS,CAAC,EAAE,IAAI,CAAC;IACjB,SAAS,CAAC,EAAE,IAAI,CAAC;IACjB,uFAAuF;IACvF,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,IAAI,CAAC;IACtB,SAAS,EAAE,IAAI,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;CACjB;AAgCD;;;;;;;;;GASG;AACH,eAAO,MAAM,qBAAqB,UAAwD,CAAC;AAI3F,eAAO,MAAM,MAAM;;;;;;gBAEgC,CAAC"}
|
package/dist/apiKeys/ApiKey.js
CHANGED
|
@@ -33,8 +33,10 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.ApiKey = void 0;
|
|
36
|
+
exports.ApiKey = exports.API_KEY_SECRET_FIELDS = void 0;
|
|
37
37
|
const mongoose_1 = __importStar(require("mongoose"));
|
|
38
|
+
const plugin_1 = require("../audit/plugin");
|
|
39
|
+
const types_1 = require("./types");
|
|
38
40
|
const ApiKeySchema = new mongoose_1.Schema({
|
|
39
41
|
userId: { type: String, required: true, index: true },
|
|
40
42
|
userEmail: { type: String, required: true },
|
|
@@ -46,8 +48,11 @@ const ApiKeySchema = new mongoose_1.Schema({
|
|
|
46
48
|
sealedIv: { type: String, required: true },
|
|
47
49
|
sealedTag: { type: String, required: true },
|
|
48
50
|
scopes: { type: [String], default: [] },
|
|
49
|
-
//
|
|
51
|
+
// Defaulted rather than optional: which data a key reaches is a decision the
|
|
50
52
|
// creator made, and it must not be indistinguishable from a field nobody set.
|
|
53
|
+
tenants: { type: [String], default: () => [types_1.PERSONAL_TENANT] },
|
|
54
|
+
defaultTenant: { type: String, default: types_1.PERSONAL_TENANT },
|
|
55
|
+
// Written by the single-tenant model this replaced; read-only now.
|
|
51
56
|
groupId: { type: String, default: null },
|
|
52
57
|
lastUsedAt: { type: Date },
|
|
53
58
|
expiresAt: { type: Date },
|
|
@@ -57,6 +62,18 @@ const ApiKeySchema = new mongoose_1.Schema({
|
|
|
57
62
|
}, { timestamps: true, collection: 'api_keys' });
|
|
58
63
|
// Every listing is one user's keys, newest first.
|
|
59
64
|
ApiKeySchema.index({ userId: 1, createdAt: -1 });
|
|
65
|
+
/**
|
|
66
|
+
* Issuing and revoking a key is worth a record — arguably more than anything a
|
|
67
|
+
* key goes on to do, since a credential nobody remembers creating is the one
|
|
68
|
+
* that matters.
|
|
69
|
+
*
|
|
70
|
+
* Every secret-bearing field is redacted. The trail is readable by the account
|
|
71
|
+
* owner and kept for a year; a snapshot carrying `hash` or the sealed
|
|
72
|
+
* ciphertext would put credential material into a second collection with a
|
|
73
|
+
* different lifetime, which is precisely the thing the encryption is for.
|
|
74
|
+
*/
|
|
75
|
+
exports.API_KEY_SECRET_FIELDS = ['hash', 'sealedCiphertext', 'sealedIv', 'sealedTag'];
|
|
76
|
+
ApiKeySchema.plugin(plugin_1.auditPlugin, { resource: 'api_key', redact: exports.API_KEY_SECRET_FIELDS });
|
|
60
77
|
exports.ApiKey = mongoose_1.default.models.ApiKey
|
|
61
78
|
? mongoose_1.default.models.ApiKey
|
|
62
79
|
: mongoose_1.default.model('ApiKey', ApiKeySchema);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ApiKey.js","sourceRoot":"","sources":["../../src/apiKeys/ApiKey.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,qDAAsD;
|
|
1
|
+
{"version":3,"file":"ApiKey.js","sourceRoot":"","sources":["../../src/apiKeys/ApiKey.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,qDAAsD;AACtD,4CAA8C;AAC9C,mCAAyE;AAqDzE,MAAM,YAAY,GAAG,IAAI,iBAAM,CAC7B;IACE,MAAM,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE;IACrD,SAAS,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE;IAC3C,QAAQ,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE;IACvD,IAAI,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE;IACjE,KAAK,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE;IAClE,IAAI,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE;IACtC,gBAAgB,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE;IAClD,QAAQ,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE;IAC1C,SAAS,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE;IAC3C,MAAM,EAAE,EAAE,IAAI,EAAE,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE;IACvC,6EAA6E;IAC7E,8EAA8E;IAC9E,OAAO,EAAE,EAAE,IAAI,EAAE,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,CAAC,uBAAe,CAAC,EAAE;IAC7D,aAAa,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,uBAAe,EAAE;IACzD,mEAAmE;IACnE,OAAO,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE;IACxC,UAAU,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE;IAC1B,SAAS,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE;IACzB,SAAS,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE;IACzB,WAAW,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,EAAE;IACzC,cAAc,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE;CAC/B,EACD,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,EAAE,UAAU,EAAE,CAC7C,CAAC;AAEF,kDAAkD;AAClD,YAAY,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;AAEjD;;;;;;;;;GASG;AACU,QAAA,qBAAqB,GAAG,CAAC,MAAM,EAAE,kBAAkB,EAAE,UAAU,EAAE,WAAW,CAAC,CAAC;AAE3F,YAAY,CAAC,MAAM,CAAC,oBAAW,EAAE,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,6BAAqB,EAAE,CAAC,CAAC;AAE5E,QAAA,MAAM,GAAG,kBAAQ,CAAC,MAAM,CAAC,MAAM;IAC1C,CAAC,CAAE,kBAAQ,CAAC,MAAM,CAAC,MAAkC;IACrD,CAAC,CAAC,kBAAQ,CAAC,KAAK,CAAU,QAAQ,EAAE,YAAY,CAAC,CAAC"}
|
package/dist/apiKeys/index.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ export { createApiKey, listApiKeys, revealApiKey, revokeApiKey, deleteApiKey, ve
|
|
|
4
4
|
export type { CreateApiKeyInput, CreatedApiKey } from './service';
|
|
5
5
|
export { authenticateAgent, requireScope, denyApiKeys } from './middleware';
|
|
6
6
|
export type { ApiKeyContext } from './middleware';
|
|
7
|
-
export { API_KEY_SCOPES, isApiKeyScope } from './types';
|
|
8
|
-
export type { ApiKeyScope, ApiKeySummary, ApiKeyVerification, ApiKeyRejection } from './types';
|
|
7
|
+
export { API_KEY_SCOPES, isApiKeyScope, PERSONAL_TENANT, groupIdOf, groupIdsOf, readTenants } from './types';
|
|
8
|
+
export type { ApiKeyScope, ApiKeySummary, ApiKeyVerification, ApiKeyRejection, ApiKeyTenant, Tenant } from './types';
|
|
9
9
|
export { looksLikeApiKey, displayPrefix, isEncryptionConfigured, resetEncryptionKeyCache } from './crypto';
|
|
10
10
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/apiKeys/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,YAAY,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AACxC,OAAO,EACL,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,YAAY,EACZ,YAAY,EACZ,YAAY,EACb,MAAM,WAAW,CAAC;AACnB,YAAY,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAClE,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC5E,YAAY,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/apiKeys/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,YAAY,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AACxC,OAAO,EACL,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,YAAY,EACZ,YAAY,EACZ,YAAY,EACb,MAAM,WAAW,CAAC;AACnB,YAAY,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAClE,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC5E,YAAY,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,EACL,cAAc,EACd,aAAa,EACb,eAAe,EACf,SAAS,EACT,UAAU,EACV,WAAW,EACZ,MAAM,SAAS,CAAC;AACjB,YAAY,EACV,WAAW,EACX,aAAa,EACb,kBAAkB,EAClB,eAAe,EACf,YAAY,EACZ,MAAM,EACP,MAAM,SAAS,CAAC;AACjB,OAAO,EACL,eAAe,EACf,aAAa,EACb,sBAAsB,EACtB,uBAAuB,EACxB,MAAM,UAAU,CAAC"}
|
package/dist/apiKeys/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.resetEncryptionKeyCache = exports.isEncryptionConfigured = exports.displayPrefix = exports.looksLikeApiKey = exports.isApiKeyScope = exports.API_KEY_SCOPES = exports.denyApiKeys = exports.requireScope = exports.authenticateAgent = exports.verifyApiKey = exports.deleteApiKey = exports.revokeApiKey = exports.revealApiKey = exports.listApiKeys = exports.createApiKey = exports.ApiKey = void 0;
|
|
3
|
+
exports.resetEncryptionKeyCache = exports.isEncryptionConfigured = exports.displayPrefix = exports.looksLikeApiKey = exports.readTenants = exports.groupIdsOf = exports.groupIdOf = exports.PERSONAL_TENANT = exports.isApiKeyScope = exports.API_KEY_SCOPES = exports.denyApiKeys = exports.requireScope = exports.authenticateAgent = exports.verifyApiKey = exports.deleteApiKey = exports.revokeApiKey = exports.revealApiKey = exports.listApiKeys = exports.createApiKey = exports.ApiKey = void 0;
|
|
4
4
|
var ApiKey_1 = require("./ApiKey");
|
|
5
5
|
Object.defineProperty(exports, "ApiKey", { enumerable: true, get: function () { return ApiKey_1.ApiKey; } });
|
|
6
6
|
var service_1 = require("./service");
|
|
@@ -17,6 +17,10 @@ Object.defineProperty(exports, "denyApiKeys", { enumerable: true, get: function
|
|
|
17
17
|
var types_1 = require("./types");
|
|
18
18
|
Object.defineProperty(exports, "API_KEY_SCOPES", { enumerable: true, get: function () { return types_1.API_KEY_SCOPES; } });
|
|
19
19
|
Object.defineProperty(exports, "isApiKeyScope", { enumerable: true, get: function () { return types_1.isApiKeyScope; } });
|
|
20
|
+
Object.defineProperty(exports, "PERSONAL_TENANT", { enumerable: true, get: function () { return types_1.PERSONAL_TENANT; } });
|
|
21
|
+
Object.defineProperty(exports, "groupIdOf", { enumerable: true, get: function () { return types_1.groupIdOf; } });
|
|
22
|
+
Object.defineProperty(exports, "groupIdsOf", { enumerable: true, get: function () { return types_1.groupIdsOf; } });
|
|
23
|
+
Object.defineProperty(exports, "readTenants", { enumerable: true, get: function () { return types_1.readTenants; } });
|
|
20
24
|
var crypto_1 = require("./crypto");
|
|
21
25
|
Object.defineProperty(exports, "looksLikeApiKey", { enumerable: true, get: function () { return crypto_1.looksLikeApiKey; } });
|
|
22
26
|
Object.defineProperty(exports, "displayPrefix", { enumerable: true, get: function () { return crypto_1.displayPrefix; } });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/apiKeys/index.ts"],"names":[],"mappings":";;;AAAA,mCAAkC;AAAzB,gGAAA,MAAM,OAAA;AAEf,qCAOmB;AANjB,uGAAA,YAAY,OAAA;AACZ,sGAAA,WAAW,OAAA;AACX,uGAAA,YAAY,OAAA;AACZ,uGAAA,YAAY,OAAA;AACZ,uGAAA,YAAY,OAAA;AACZ,uGAAA,YAAY,OAAA;AAGd,2CAA4E;AAAnE,+GAAA,iBAAiB,OAAA;AAAE,0GAAA,YAAY,OAAA;AAAE,yGAAA,WAAW,OAAA;AAErD,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/apiKeys/index.ts"],"names":[],"mappings":";;;AAAA,mCAAkC;AAAzB,gGAAA,MAAM,OAAA;AAEf,qCAOmB;AANjB,uGAAA,YAAY,OAAA;AACZ,sGAAA,WAAW,OAAA;AACX,uGAAA,YAAY,OAAA;AACZ,uGAAA,YAAY,OAAA;AACZ,uGAAA,YAAY,OAAA;AACZ,uGAAA,YAAY,OAAA;AAGd,2CAA4E;AAAnE,+GAAA,iBAAiB,OAAA;AAAE,0GAAA,YAAY,OAAA;AAAE,yGAAA,WAAW,OAAA;AAErD,iCAOiB;AANf,uGAAA,cAAc,OAAA;AACd,sGAAA,aAAa,OAAA;AACb,wGAAA,eAAe,OAAA;AACf,kGAAA,SAAS,OAAA;AACT,mGAAA,UAAU,OAAA;AACV,oGAAA,WAAW,OAAA;AAUb,mCAKkB;AAJhB,yGAAA,eAAe,OAAA;AACf,uGAAA,aAAa,OAAA;AACb,gHAAA,sBAAsB,OAAA;AACtB,iHAAA,uBAAuB,OAAA"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { RequestHandler } from 'express';
|
|
2
|
-
import { type ApiKeyScope } from './types';
|
|
2
|
+
import { type ApiKeyScope, type ApiKeyTenant, type Tenant } from './types';
|
|
3
3
|
/**
|
|
4
4
|
* Request-scoped facts about the key a request arrived on. Absent on ordinary
|
|
5
5
|
* session requests, which is itself the signal a handler needs: `req.apiKey`
|
|
@@ -8,8 +8,22 @@ import { type ApiKeyScope } from './types';
|
|
|
8
8
|
export interface ApiKeyContext {
|
|
9
9
|
keyId: string;
|
|
10
10
|
scopes: ApiKeyScope[];
|
|
11
|
-
/**
|
|
12
|
-
|
|
11
|
+
/** every tenant this credential may act in, and the one it acts in by default */
|
|
12
|
+
tenants: ApiKeyTenant;
|
|
13
|
+
/** the tenant this particular request resolved to */
|
|
14
|
+
actingAs: Tenant;
|
|
15
|
+
/** which credential this is; they are revoked and audited differently */
|
|
16
|
+
kind: 'api_key' | 'oauth';
|
|
17
|
+
/** what the owner called it — a key's name, or the connected app's */
|
|
18
|
+
label: string;
|
|
19
|
+
/**
|
|
20
|
+
* The stable identity of the credential.
|
|
21
|
+
*
|
|
22
|
+
* A key's document id, or an OAuth client id — never the access token's
|
|
23
|
+
* `jti`, which rotates hourly and would make one connection look like a new
|
|
24
|
+
* actor every time it refreshed.
|
|
25
|
+
*/
|
|
26
|
+
credentialId: string;
|
|
13
27
|
}
|
|
14
28
|
declare global {
|
|
15
29
|
namespace Express {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../../src/apiKeys/middleware.ts"],"names":[],"mappings":"AAAA,OAAO,EAAyB,cAAc,EAAY,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../../src/apiKeys/middleware.ts"],"names":[],"mappings":"AAAA,OAAO,EAAyB,cAAc,EAAY,MAAM,SAAS,CAAC;AAS1E,OAAO,EAKL,KAAK,WAAW,EAChB,KAAK,YAAY,EACjB,KAAK,MAAM,EACZ,MAAM,SAAS,CAAC;AAEjB;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,iFAAiF;IACjF,OAAO,EAAE,YAAY,CAAC;IACtB,qDAAqD;IACrD,QAAQ,EAAE,MAAM,CAAC;IACjB,yEAAyE;IACzE,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC;IAC1B,sEAAsE;IACtE,KAAK,EAAE,MAAM,CAAC;IACd;;;;;;OAMG;IACH,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,OAAO,CAAC;QAChB,UAAU,OAAO;YACf,MAAM,CAAC,EAAE,aAAa,CAAC;SACxB;KACF;CACF;AAgJD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,iBAAiB,GAAI,GAAG,gBAAgB,WAAW,EAAE,KAAG,cAoElE,CAAC;AAEJ;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,YAAY,GAAI,GAAG,gBAAgB,WAAW,EAAE,KAAG,cAiB7D,CAAC;AAEJ;;;;;;GAMG;AACH,eAAO,MAAM,WAAW,EAAE,cASzB,CAAC"}
|
|
@@ -7,6 +7,7 @@ exports.denyApiKeys = exports.requireScope = exports.authenticateAgent = void 0;
|
|
|
7
7
|
const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
|
|
8
8
|
const env_1 = require("../config/env");
|
|
9
9
|
const logger_1 = __importDefault(require("../logging/logger"));
|
|
10
|
+
const context_1 = require("../audit/context");
|
|
10
11
|
const tokens_1 = require("../oauth/tokens");
|
|
11
12
|
const crypto_1 = require("./crypto");
|
|
12
13
|
const service_1 = require("./service");
|
|
@@ -20,10 +21,12 @@ const unauthorized = (res) => {
|
|
|
20
21
|
* The tenant a request is asking to act in, wherever it named one.
|
|
21
22
|
*
|
|
22
23
|
* Handlers read `groupId` from either the query string or the body depending on
|
|
23
|
-
* the verb, so both are checked — a
|
|
24
|
-
* no
|
|
24
|
+
* the verb, so both are checked — a rule that only covered one of them would be
|
|
25
|
+
* no rule at all. A caller names the personal tenant with the literal
|
|
26
|
+
* `personal`, since "no group" is what the resolved default gets written into
|
|
27
|
+
* and would otherwise be indistinguishable from not having asked.
|
|
25
28
|
*/
|
|
26
|
-
const
|
|
29
|
+
const requestedTenant = (req) => {
|
|
27
30
|
const fromQuery = req.query?.groupId;
|
|
28
31
|
if (typeof fromQuery === 'string' && fromQuery.length > 0)
|
|
29
32
|
return fromQuery;
|
|
@@ -32,33 +35,50 @@ const requestedGroupId = (req) => {
|
|
|
32
35
|
return fromBody;
|
|
33
36
|
return undefined;
|
|
34
37
|
};
|
|
38
|
+
/** Express 5 makes `req.query` a getter, so it is redefined rather than assigned. */
|
|
39
|
+
const setQueryGroupId = (req, groupId) => {
|
|
40
|
+
const query = { ...req.query };
|
|
41
|
+
if (groupId === undefined)
|
|
42
|
+
delete query.groupId;
|
|
43
|
+
else
|
|
44
|
+
query.groupId = groupId;
|
|
45
|
+
Object.defineProperty(req, 'query', {
|
|
46
|
+
value: query,
|
|
47
|
+
writable: true,
|
|
48
|
+
configurable: true,
|
|
49
|
+
enumerable: true
|
|
50
|
+
});
|
|
51
|
+
};
|
|
35
52
|
/**
|
|
36
|
-
* Hold a
|
|
53
|
+
* Hold a credential to the tenants it was granted, and settle which one this
|
|
54
|
+
* request is acting in.
|
|
55
|
+
*
|
|
56
|
+
* A request naming a tenant outside the grant is refused outright — that
|
|
57
|
+
* boundary is the whole reason a credential is safe to hand to an agent, and it
|
|
58
|
+
* is unchanged by the grant being a set rather than one. What the set adds is a
|
|
59
|
+
* choice *inside* it, which is what lets one connection reach a shared journal
|
|
60
|
+
* and a private photo library without reconnecting.
|
|
37
61
|
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
* none has the pinned tenant written in for it, so the agent never has to know a
|
|
42
|
-
* group id exists, and a handler's `if (groupId) ... else personal` branch lands
|
|
43
|
-
* where the key's owner intended.
|
|
62
|
+
* A request that names nothing gets the default written in for it, so an agent
|
|
63
|
+
* granted a single tenant never has to know a group id exists and a handler's
|
|
64
|
+
* `if (groupId) … else personal` branch lands where the owner intended.
|
|
44
65
|
*/
|
|
45
|
-
const
|
|
46
|
-
const requested =
|
|
47
|
-
if (requested
|
|
48
|
-
return
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
66
|
+
const applyTenantGrant = (req, tenants) => {
|
|
67
|
+
const requested = requestedTenant(req) ?? tenants.default;
|
|
68
|
+
if (!tenants.allowed.includes(requested))
|
|
69
|
+
return null;
|
|
70
|
+
// Resolved either way, including when the caller asked for exactly what the
|
|
71
|
+
// default already was: handlers read the personal tenant as an absent
|
|
72
|
+
// `groupId`, so `personal` has to be erased rather than passed through.
|
|
73
|
+
const groupId = requested === types_1.PERSONAL_TENANT ? undefined : requested;
|
|
74
|
+
setQueryGroupId(req, groupId);
|
|
75
|
+
if (req.body && typeof req.body === 'object') {
|
|
76
|
+
if (groupId === undefined)
|
|
77
|
+
delete req.body.groupId;
|
|
78
|
+
else
|
|
58
79
|
req.body.groupId = groupId;
|
|
59
|
-
}
|
|
60
80
|
}
|
|
61
|
-
return
|
|
81
|
+
return requested;
|
|
62
82
|
};
|
|
63
83
|
/**
|
|
64
84
|
* Admit software acting for a user, on the terms its credential carries.
|
|
@@ -78,7 +98,8 @@ function admitAgent(req, res, next, requiredScopes, credential) {
|
|
|
78
98
|
});
|
|
79
99
|
return;
|
|
80
100
|
}
|
|
81
|
-
|
|
101
|
+
const actingAs = applyTenantGrant(req, credential.tenants);
|
|
102
|
+
if (actingAs === null) {
|
|
82
103
|
res.status(403).json({
|
|
83
104
|
success: false,
|
|
84
105
|
message: 'Credential is not permitted to act in the requested group'
|
|
@@ -86,12 +107,23 @@ function admitAgent(req, res, next, requiredScopes, credential) {
|
|
|
86
107
|
return;
|
|
87
108
|
}
|
|
88
109
|
req.user = { id: credential.userId, email: credential.email, name: credential.name };
|
|
89
|
-
// Only the
|
|
110
|
+
// Only the granted groups, never the owner's full membership: this is what
|
|
90
111
|
// stops a credential reaching a group it was not issued for through any
|
|
91
112
|
// handler that consults `userGroups` instead of the `groupId` parameter.
|
|
92
|
-
req.userGroups =
|
|
93
|
-
req.apiKey = {
|
|
94
|
-
|
|
113
|
+
req.userGroups = (0, types_1.groupIdsOf)(credential.tenants.allowed);
|
|
114
|
+
req.apiKey = {
|
|
115
|
+
keyId: credential.credentialId,
|
|
116
|
+
scopes: credential.scopes,
|
|
117
|
+
tenants: credential.tenants,
|
|
118
|
+
actingAs,
|
|
119
|
+
kind: credential.kind,
|
|
120
|
+
label: credential.label,
|
|
121
|
+
credentialId: credential.credentialId
|
|
122
|
+
};
|
|
123
|
+
// Attribution comes from authentication, so it is established here rather
|
|
124
|
+
// than mounted separately — a router that authenticates per route would
|
|
125
|
+
// otherwise have no actor in scope when a router-level middleware ran.
|
|
126
|
+
(0, context_1.beginAudit)(req, res, next);
|
|
95
127
|
}
|
|
96
128
|
/**
|
|
97
129
|
* Authenticate a request that software may legitimately be making.
|
|
@@ -124,7 +156,7 @@ const authenticateAgent = (...requiredScopes) => async function authenticateAgen
|
|
|
124
156
|
const user = claims;
|
|
125
157
|
req.user = user;
|
|
126
158
|
req.userGroups = user.groups ?? [];
|
|
127
|
-
|
|
159
|
+
(0, context_1.beginAudit)(req, res, next);
|
|
128
160
|
return;
|
|
129
161
|
}
|
|
130
162
|
// An OAuth access token: signed with the same secret as a session and
|
|
@@ -138,12 +170,15 @@ const authenticateAgent = (...requiredScopes) => async function authenticateAgen
|
|
|
138
170
|
// whose scope collapsed to nothing fell through to "no user context" and
|
|
139
171
|
// answered from the whole collection.
|
|
140
172
|
admitAgent(req, res, next, requiredScopes, {
|
|
141
|
-
|
|
173
|
+
// The client, not the token: `jti` is a new value every hour.
|
|
174
|
+
credentialId: claims.clientId ?? 'unknown-client',
|
|
175
|
+
kind: 'oauth',
|
|
176
|
+
label: claims.clientName || 'A connected app',
|
|
142
177
|
userId: claims.sub,
|
|
143
178
|
email: claims.email ?? '',
|
|
144
179
|
name: claims.name ?? '',
|
|
145
180
|
scopes: (claims.scope ?? '').split(' ').filter(types_1.isApiKeyScope),
|
|
146
|
-
|
|
181
|
+
tenants: (0, types_1.readTenants)(claims)
|
|
147
182
|
});
|
|
148
183
|
return;
|
|
149
184
|
}
|
|
@@ -157,11 +192,13 @@ const authenticateAgent = (...requiredScopes) => async function authenticateAgen
|
|
|
157
192
|
}
|
|
158
193
|
admitAgent(req, res, next, requiredScopes, {
|
|
159
194
|
credentialId: result.keyId,
|
|
195
|
+
kind: 'api_key',
|
|
196
|
+
label: result.label || 'An API key',
|
|
160
197
|
userId: result.userId,
|
|
161
198
|
email: result.userEmail ?? '',
|
|
162
199
|
name: result.userName ?? '',
|
|
163
200
|
scopes: result.scopes ?? [],
|
|
164
|
-
|
|
201
|
+
tenants: result.tenants ?? (0, types_1.readTenants)({})
|
|
165
202
|
});
|
|
166
203
|
};
|
|
167
204
|
exports.authenticateAgent = authenticateAgent;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"middleware.js","sourceRoot":"","sources":["../../src/apiKeys/middleware.ts"],"names":[],"mappings":";;;;;;AACA,gEAA+B;AAC/B,uCAA2C;AAC3C,+DAAuC;AACvC,4CAA8E;AAE9E,qCAA2C;AAC3C,uCAAyC;AACzC,
|
|
1
|
+
{"version":3,"file":"middleware.js","sourceRoot":"","sources":["../../src/apiKeys/middleware.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,12 +1,14 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type ApiKeyScope, type ApiKeySummary, type ApiKeyVerification, type Tenant } from './types';
|
|
2
2
|
export interface CreateApiKeyInput {
|
|
3
3
|
userId: string;
|
|
4
4
|
userEmail: string;
|
|
5
5
|
userName: string;
|
|
6
6
|
name: string;
|
|
7
7
|
scopes: ApiKeyScope[];
|
|
8
|
-
/**
|
|
9
|
-
|
|
8
|
+
/** every tenant the key may act in; defaults to the user's own data alone */
|
|
9
|
+
tenants?: Tenant[];
|
|
10
|
+
/** which of them it acts in when a request names none */
|
|
11
|
+
defaultTenant?: Tenant;
|
|
10
12
|
expiresAt?: Date | null;
|
|
11
13
|
}
|
|
12
14
|
export interface CreatedApiKey {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../src/apiKeys/service.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../src/apiKeys/service.ts"],"names":[],"mappings":"AASA,OAAO,EAGL,KAAK,WAAW,EAChB,KAAK,aAAa,EAClB,KAAK,kBAAkB,EACvB,KAAK,MAAM,EACZ,MAAM,SAAS,CAAC;AAEjB,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,6EAA6E;IAC7E,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,yDAAyD;IACzD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;CACzB;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,aAAa,CAAC;IACvB,+EAA+E;IAC/E,KAAK,EAAE,MAAM,CAAC;CACf;AAgBD,eAAO,MAAM,YAAY,GAAU,OAAO,iBAAiB,KAAG,OAAO,CAAC,aAAa,CA2BlF,CAAC;AAEF,eAAO,MAAM,WAAW,GAAU,QAAQ,MAAM,KAAG,OAAO,CAAC,aAAa,EAAE,CAGzE,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,YAAY,GAAU,QAAQ,MAAM,EAAE,IAAI,MAAM,KAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAepF,CAAC;AAEF,+EAA+E;AAC/E,eAAO,MAAM,YAAY,GAAU,QAAQ,MAAM,EAAE,IAAI,MAAM,KAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAU3F,CAAC;AAEF,eAAO,MAAM,YAAY,GAAU,QAAQ,MAAM,EAAE,IAAI,MAAM,KAAG,OAAO,CAAC,OAAO,CAG9E,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,YAAY,GAAU,OAAO,OAAO,KAAG,OAAO,CAAC,kBAAkB,CA4B7E,CAAC"}
|
package/dist/apiKeys/service.js
CHANGED
|
@@ -3,18 +3,22 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.verifyApiKey = exports.deleteApiKey = exports.revokeApiKey = exports.revealApiKey = exports.listApiKeys = exports.createApiKey = void 0;
|
|
4
4
|
const ApiKey_1 = require("./ApiKey");
|
|
5
5
|
const crypto_1 = require("./crypto");
|
|
6
|
+
const types_1 = require("./types");
|
|
6
7
|
const toSummary = (key) => ({
|
|
7
8
|
id: String(key._id),
|
|
8
9
|
name: key.name,
|
|
9
10
|
prefix: (0, crypto_1.displayPrefix)(key.keyId),
|
|
10
11
|
scopes: key.scopes,
|
|
11
|
-
|
|
12
|
+
...(({ allowed, default: fallback }) => ({ tenants: allowed, defaultTenant: fallback }))((0, types_1.readTenants)(key)),
|
|
12
13
|
createdAt: key.createdAt.toISOString(),
|
|
13
14
|
lastUsedAt: key.lastUsedAt?.toISOString() ?? null,
|
|
14
15
|
expiresAt: key.expiresAt?.toISOString() ?? null,
|
|
15
16
|
revokedAt: key.revokedAt?.toISOString() ?? null
|
|
16
17
|
});
|
|
17
18
|
const createApiKey = async (input) => {
|
|
19
|
+
// A key that reaches nowhere would authenticate and then fail every call, so
|
|
20
|
+
// an empty grant is read as the safest non-empty one rather than stored.
|
|
21
|
+
const tenants = input.tenants?.length ? input.tenants : [types_1.PERSONAL_TENANT];
|
|
18
22
|
const { token, id, hash } = (0, crypto_1.generateKey)();
|
|
19
23
|
const sealed = (0, crypto_1.encryptSecret)(token);
|
|
20
24
|
const key = await ApiKey_1.ApiKey.create({
|
|
@@ -28,7 +32,10 @@ const createApiKey = async (input) => {
|
|
|
28
32
|
sealedIv: sealed.iv,
|
|
29
33
|
sealedTag: sealed.tag,
|
|
30
34
|
scopes: input.scopes,
|
|
31
|
-
|
|
35
|
+
tenants,
|
|
36
|
+
defaultTenant: tenants.includes(input.defaultTenant ?? '')
|
|
37
|
+
? input.defaultTenant
|
|
38
|
+
: tenants[0],
|
|
32
39
|
expiresAt: input.expiresAt ?? undefined
|
|
33
40
|
});
|
|
34
41
|
return { summary: toSummary(key), token };
|
|
@@ -114,8 +121,9 @@ const verifyApiKey = async (token) => {
|
|
|
114
121
|
userEmail: key.userEmail,
|
|
115
122
|
userName: key.userName,
|
|
116
123
|
keyId: String(key._id),
|
|
124
|
+
label: key.name,
|
|
117
125
|
scopes: key.scopes,
|
|
118
|
-
|
|
126
|
+
tenants: (0, types_1.readTenants)(key)
|
|
119
127
|
};
|
|
120
128
|
};
|
|
121
129
|
exports.verifyApiKey = verifyApiKey;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.js","sourceRoot":"","sources":["../../src/apiKeys/service.ts"],"names":[],"mappings":";;;AAAA,qCAA2C;AAC3C,qCAOkB;
|
|
1
|
+
{"version":3,"file":"service.js","sourceRoot":"","sources":["../../src/apiKeys/service.ts"],"names":[],"mappings":"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"}
|
package/dist/apiKeys/types.d.ts
CHANGED
|
@@ -12,16 +12,48 @@ export declare const API_KEY_SCOPES: readonly ["relationship:read", "relationshi
|
|
|
12
12
|
export type ApiKeyScope = (typeof API_KEY_SCOPES)[number];
|
|
13
13
|
export declare const isApiKeyScope: (value: unknown) => value is ApiKeyScope;
|
|
14
14
|
/**
|
|
15
|
-
*
|
|
15
|
+
* A tenant a credential may act in: a group id, or the user's own data.
|
|
16
16
|
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
17
|
+
* Personal is a named sentinel rather than `null` so that a tenant is always a
|
|
18
|
+
* plain string — storable in an array, sendable in a JWT claim, comparable
|
|
19
|
+
* without a special case at every layer. It cannot collide with a real tenant:
|
|
20
|
+
* group ids are 24-character hex.
|
|
21
|
+
*/
|
|
22
|
+
export declare const PERSONAL_TENANT = "personal";
|
|
23
|
+
export type Tenant = string;
|
|
24
|
+
/** The `groupId` a tenant corresponds to, as handlers have always read it. */
|
|
25
|
+
export declare const groupIdOf: (tenant: Tenant) => string | null;
|
|
26
|
+
/**
|
|
27
|
+
* Which tenants a credential may act in, and which one it acts in by default.
|
|
28
|
+
*
|
|
29
|
+
* The web app picks a tenant per request from a selector; an agent has no such
|
|
30
|
+
* UI. So the *set* is chosen once, by a person, and the credential cannot escape
|
|
31
|
+
* it — but within that set the caller may name one per request, which is what
|
|
32
|
+
* lets a single connection reach both a shared journal and a private photo
|
|
33
|
+
* library without reconnecting.
|
|
34
|
+
*
|
|
35
|
+
* `allowed` is never empty and always contains `default`. A credential granted
|
|
36
|
+
* exactly one tenant behaves precisely as a pinned one did: nothing to name,
|
|
37
|
+
* nothing to get wrong.
|
|
21
38
|
*/
|
|
22
39
|
export interface ApiKeyTenant {
|
|
23
|
-
|
|
40
|
+
allowed: Tenant[];
|
|
41
|
+
default: Tenant;
|
|
24
42
|
}
|
|
43
|
+
/** Just the groups, for the `req.userGroups` every handler already reads. */
|
|
44
|
+
export declare const groupIdsOf: (tenants: Tenant[]) => string[];
|
|
45
|
+
/**
|
|
46
|
+
* Read a stored tenant grant, tolerating one written before tenants were a set.
|
|
47
|
+
*
|
|
48
|
+
* Keys and grants issued under the old single-tenant model carry `groupId`
|
|
49
|
+
* alone. Falling back to it here means they keep working across the deploy
|
|
50
|
+
* rather than every connected assistant breaking at once.
|
|
51
|
+
*/
|
|
52
|
+
export declare const readTenants: (stored: {
|
|
53
|
+
tenants?: Tenant[] | null;
|
|
54
|
+
defaultTenant?: Tenant | null;
|
|
55
|
+
groupId?: string | null;
|
|
56
|
+
}) => ApiKeyTenant;
|
|
25
57
|
/** A key as the API hands it back — never including the secret. */
|
|
26
58
|
export interface ApiKeySummary {
|
|
27
59
|
id: string;
|
|
@@ -29,7 +61,8 @@ export interface ApiKeySummary {
|
|
|
29
61
|
/** the public half, shown in listings so a key is identifiable at a glance */
|
|
30
62
|
prefix: string;
|
|
31
63
|
scopes: ApiKeyScope[];
|
|
32
|
-
|
|
64
|
+
tenants: Tenant[];
|
|
65
|
+
defaultTenant: Tenant;
|
|
33
66
|
createdAt: string;
|
|
34
67
|
lastUsedAt: string | null;
|
|
35
68
|
expiresAt: string | null;
|
|
@@ -44,7 +77,9 @@ export interface ApiKeyVerification {
|
|
|
44
77
|
userEmail?: string;
|
|
45
78
|
userName?: string;
|
|
46
79
|
keyId?: string;
|
|
80
|
+
/** what the owner called this key, for an audit trail a person can read */
|
|
81
|
+
label?: string;
|
|
47
82
|
scopes?: ApiKeyScope[];
|
|
48
|
-
|
|
83
|
+
tenants?: ApiKeyTenant;
|
|
49
84
|
}
|
|
50
85
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/apiKeys/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,+EAA+E;AAC/E,eAAO,MAAM,cAAc,oHAOjB,CAAC;AAEX,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,CAAC,CAAC;AAE1D,eAAO,MAAM,aAAa,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,WAC4B,CAAC;AAErF;;;;;;;GAOG;AACH,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/apiKeys/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,+EAA+E;AAC/E,eAAO,MAAM,cAAc,oHAOjB,CAAC;AAEX,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,CAAC,CAAC;AAE1D,eAAO,MAAM,aAAa,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,WAC4B,CAAC;AAErF;;;;;;;GAOG;AACH,eAAO,MAAM,eAAe,aAAa,CAAC;AAE1C,MAAM,MAAM,MAAM,GAAG,MAAM,CAAC;AAE5B,8EAA8E;AAC9E,eAAO,MAAM,SAAS,GAAI,QAAQ,MAAM,KAAG,MAAM,GAAG,IACR,CAAC;AAE7C;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,6EAA6E;AAC7E,eAAO,MAAM,UAAU,GAAI,SAAS,MAAM,EAAE,KAAG,MAAM,EACG,CAAC;AAEzD;;;;;;GAMG;AACH,eAAO,MAAM,WAAW,GAAI,QAAQ;IAClC,OAAO,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IAC1B,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB,KAAG,YAaH,CAAC;AAEF,mEAAmE;AACnE,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,8EAA8E;IAC9E,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,2FAA2F;AAC3F,MAAM,MAAM,eAAe,GACvB,WAAW,GACX,SAAS,GACT,SAAS,GACT,SAAS,GACT,YAAY,CAAC;AAEjB,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,OAAO,CAAC;IACZ,SAAS,CAAC,EAAE,eAAe,CAAC;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2EAA2E;IAC3E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,WAAW,EAAE,CAAC;IACvB,OAAO,CAAC,EAAE,YAAY,CAAC;CACxB"}
|