@kubun/server 0.4.1 → 0.4.3
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/lib/data/access-control.d.ts +46 -0
- package/lib/data/access-control.d.ts.map +1 -0
- package/lib/data/access-control.js +164 -0
- package/lib/data/graphql.d.ts +2 -0
- package/lib/data/graphql.d.ts.map +1 -1
- package/lib/data/graphql.js +36 -1
- package/lib/data/mutations.d.ts +37 -0
- package/lib/data/mutations.d.ts.map +1 -1
- package/lib/data/mutations.js +104 -0
- package/lib/handlers/graph.d.ts.map +1 -1
- package/lib/handlers/graph.js +31 -18
- package/lib/handlers/types.d.ts +2 -0
- package/lib/handlers/types.d.ts.map +1 -1
- package/lib/server.d.ts +8 -0
- package/lib/server.d.ts.map +1 -1
- package/lib/server.js +15 -3
- package/package.json +11 -9
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { KubunDB } from '@kubun/db';
|
|
2
|
+
import type { DocumentNode } from '@kubun/protocol';
|
|
3
|
+
export type AccessRule = {
|
|
4
|
+
level: string;
|
|
5
|
+
allowedDIDs: Array<string> | null;
|
|
6
|
+
};
|
|
7
|
+
export type AccessPermissions = {
|
|
8
|
+
read?: AccessRule;
|
|
9
|
+
write?: AccessRule;
|
|
10
|
+
};
|
|
11
|
+
export type ServerAccessConfig = {
|
|
12
|
+
defaultAccessLevel: {
|
|
13
|
+
read: 'only_owner' | 'anyone' | 'allowed_dids';
|
|
14
|
+
write: 'only_owner' | 'allowed_dids';
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
export type AccessChecker = (doc: DocumentNode, permissionType: 'read' | 'write') => Promise<boolean>;
|
|
18
|
+
/**
|
|
19
|
+
* Parse and validate access permissions from document data
|
|
20
|
+
*/
|
|
21
|
+
export declare function parseDocumentAccessPermissions(data: any): AccessPermissions | null;
|
|
22
|
+
/**
|
|
23
|
+
* Validate that DIDs have the correct format
|
|
24
|
+
*/
|
|
25
|
+
export declare function validateDIDs(dids: Array<string>): void;
|
|
26
|
+
/**
|
|
27
|
+
* Resolve the effective access rule for a document and permission type
|
|
28
|
+
* Order of precedence:
|
|
29
|
+
* 1. Document accessPermissions override
|
|
30
|
+
* 2. User's model default from database
|
|
31
|
+
* 3. Server configuration default
|
|
32
|
+
*/
|
|
33
|
+
export declare function resolveAccessRule(document: DocumentNode, modelId: string, ownerDID: string, permissionType: 'read' | 'write', db: KubunDB, serverConfig: ServerAccessConfig): Promise<AccessRule>;
|
|
34
|
+
/**
|
|
35
|
+
* Check if viewer has access to document through delegation tokens
|
|
36
|
+
*/
|
|
37
|
+
export declare function checkDelegation(viewerDID: string, grantor: string, document: DocumentNode, permissionType: 'read' | 'write', delegationTokens?: Array<string>): Promise<boolean>;
|
|
38
|
+
/**
|
|
39
|
+
* Check if viewer has access to a document for the specified permission type
|
|
40
|
+
*/
|
|
41
|
+
export declare function checkAccess(viewerDID: string | null, document: DocumentNode, permissionType: 'read' | 'write', db: KubunDB, serverConfig: ServerAccessConfig, delegationTokens?: Array<string>): Promise<boolean>;
|
|
42
|
+
/**
|
|
43
|
+
* Create an access checker function bound to specific viewer and delegation tokens
|
|
44
|
+
*/
|
|
45
|
+
export declare function createAccessChecker(viewerDID: string | null, delegationTokens: Array<string> | undefined, db: KubunDB, serverConfig: ServerAccessConfig): AccessChecker;
|
|
46
|
+
//# sourceMappingURL=access-control.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"access-control.d.ts","sourceRoot":"","sources":["../../src/data/access-control.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACxC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAEnD,MAAM,MAAM,UAAU,GAAG;IACvB,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,EAAE,KAAK,CAAC,MAAM,CAAC,GAAG,IAAI,CAAA;CAClC,CAAA;AAED,MAAM,MAAM,iBAAiB,GAAG;IAC9B,IAAI,CAAC,EAAE,UAAU,CAAA;IACjB,KAAK,CAAC,EAAE,UAAU,CAAA;CACnB,CAAA;AAED,MAAM,MAAM,kBAAkB,GAAG;IAC/B,kBAAkB,EAAE;QAClB,IAAI,EAAE,YAAY,GAAG,QAAQ,GAAG,cAAc,CAAA;QAC9C,KAAK,EAAE,YAAY,GAAG,cAAc,CAAA;KACrC,CAAA;CACF,CAAA;AAED,MAAM,MAAM,aAAa,GAAG,CAC1B,GAAG,EAAE,YAAY,EACjB,cAAc,EAAE,MAAM,GAAG,OAAO,KAC7B,OAAO,CAAC,OAAO,CAAC,CAAA;AAErB;;GAEG;AACH,wBAAgB,8BAA8B,CAAC,IAAI,EAAE,GAAG,GAAG,iBAAiB,GAAG,IAAI,CAiBlF;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,GAAG,IAAI,CAMtD;AAaD;;;;;;GAMG;AACH,wBAAsB,iBAAiB,CACrC,QAAQ,EAAE,YAAY,EACtB,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAChB,cAAc,EAAE,MAAM,GAAG,OAAO,EAChC,EAAE,EAAE,OAAO,EACX,YAAY,EAAE,kBAAkB,GAC/B,OAAO,CAAC,UAAU,CAAC,CA4BrB;AAED;;GAEG;AACH,wBAAsB,eAAe,CACnC,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,YAAY,EACtB,cAAc,EAAE,MAAM,GAAG,OAAO,EAChC,gBAAgB,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,GAC/B,OAAO,CAAC,OAAO,CAAC,CAoClB;AAED;;GAEG;AACH,wBAAsB,WAAW,CAC/B,SAAS,EAAE,MAAM,GAAG,IAAI,EACxB,QAAQ,EAAE,YAAY,EACtB,cAAc,EAAE,MAAM,GAAG,OAAO,EAChC,EAAE,EAAE,OAAO,EACX,YAAY,EAAE,kBAAkB,EAChC,gBAAgB,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,GAC/B,OAAO,CAAC,OAAO,CAAC,CA+DlB;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,MAAM,GAAG,IAAI,EACxB,gBAAgB,EAAE,KAAK,CAAC,MAAM,CAAC,GAAG,SAAS,EAC3C,EAAE,EAAE,OAAO,EACX,YAAY,EAAE,kBAAkB,GAC/B,aAAa,CAIf"}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { checkCapability } from '@enkaku/capability';
|
|
2
|
+
/**
|
|
3
|
+
* Parse and validate access permissions from document data
|
|
4
|
+
*/ export function parseDocumentAccessPermissions(data) {
|
|
5
|
+
try {
|
|
6
|
+
if (!data.accessPermissions) return null;
|
|
7
|
+
const perms = data.accessPermissions;
|
|
8
|
+
// Basic validation - check if the permission object has the expected structure
|
|
9
|
+
if (typeof perms !== 'object') {
|
|
10
|
+
console.warn('Invalid accessPermissions structure in document, ignoring');
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
return perms;
|
|
14
|
+
} catch (error) {
|
|
15
|
+
console.warn('Failed to parse document access permissions:', error);
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Validate that DIDs have the correct format
|
|
21
|
+
*/ export function validateDIDs(dids) {
|
|
22
|
+
for (const did of dids){
|
|
23
|
+
if (!did.startsWith('did:')) {
|
|
24
|
+
throw new Error(`Invalid DID format: ${did}`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Check if a level string is valid for the given permission type
|
|
30
|
+
*/ function isValidAccessLevel(level, permissionType) {
|
|
31
|
+
if (permissionType === 'read') {
|
|
32
|
+
return level === 'only_owner' || level === 'anyone' || level === 'allowed_dids';
|
|
33
|
+
}
|
|
34
|
+
// write permission
|
|
35
|
+
return level === 'only_owner' || level === 'allowed_dids';
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Resolve the effective access rule for a document and permission type
|
|
39
|
+
* Order of precedence:
|
|
40
|
+
* 1. Document accessPermissions override
|
|
41
|
+
* 2. User's model default from database
|
|
42
|
+
* 3. Server configuration default
|
|
43
|
+
*/ export async function resolveAccessRule(document, modelId, ownerDID, permissionType, db, serverConfig) {
|
|
44
|
+
// 1. Check document override
|
|
45
|
+
const docPerms = parseDocumentAccessPermissions(document.data);
|
|
46
|
+
if (docPerms?.[permissionType]) {
|
|
47
|
+
const rule = docPerms[permissionType];
|
|
48
|
+
if (rule && isValidAccessLevel(rule.level, permissionType)) {
|
|
49
|
+
return {
|
|
50
|
+
level: rule.level,
|
|
51
|
+
allowedDIDs: rule.allowedDIDs || null
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
// 2. Check user's model default
|
|
56
|
+
const modelDefault = await db.getUserModelAccessDefault(ownerDID, modelId, permissionType);
|
|
57
|
+
if (modelDefault) {
|
|
58
|
+
return {
|
|
59
|
+
level: modelDefault.level,
|
|
60
|
+
allowedDIDs: modelDefault.allowedDIDs
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
// 3. Fall back to server default
|
|
64
|
+
const level = serverConfig.defaultAccessLevel[permissionType];
|
|
65
|
+
return {
|
|
66
|
+
level,
|
|
67
|
+
allowedDIDs: null
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Check if viewer has access to document through delegation tokens
|
|
72
|
+
*/ export async function checkDelegation(viewerDID, grantor, document, permissionType, delegationTokens) {
|
|
73
|
+
if (!delegationTokens || delegationTokens.length === 0) {
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
// Build expected permission based on specificity
|
|
77
|
+
const resources = [
|
|
78
|
+
`urn:kubun:document:${document.id}`,
|
|
79
|
+
`urn:kubun:model:${document.model}`,
|
|
80
|
+
'*'
|
|
81
|
+
];
|
|
82
|
+
const action = `document/${permissionType}` // document/read or document/write
|
|
83
|
+
;
|
|
84
|
+
// First, try tokens as a delegation chain (for A→B→C scenarios)
|
|
85
|
+
for (const res of resources){
|
|
86
|
+
try {
|
|
87
|
+
await checkCapability({
|
|
88
|
+
act: action,
|
|
89
|
+
res
|
|
90
|
+
}, {
|
|
91
|
+
iss: viewerDID,
|
|
92
|
+
sub: grantor,
|
|
93
|
+
cap: delegationTokens
|
|
94
|
+
});
|
|
95
|
+
return true;
|
|
96
|
+
} catch {}
|
|
97
|
+
}
|
|
98
|
+
// If chain validation fails, try each token independently (for multiple independent grants)
|
|
99
|
+
for (const token of delegationTokens){
|
|
100
|
+
for (const res of resources){
|
|
101
|
+
try {
|
|
102
|
+
await checkCapability({
|
|
103
|
+
act: action,
|
|
104
|
+
res
|
|
105
|
+
}, {
|
|
106
|
+
iss: viewerDID,
|
|
107
|
+
sub: grantor,
|
|
108
|
+
cap: token
|
|
109
|
+
});
|
|
110
|
+
return true;
|
|
111
|
+
} catch {}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Check if viewer has access to a document for the specified permission type
|
|
118
|
+
*/ export async function checkAccess(viewerDID, document, permissionType, db, serverConfig, delegationTokens) {
|
|
119
|
+
// Validate document has owner
|
|
120
|
+
if (!document.owner) {
|
|
121
|
+
throw new Error('Document missing owner field');
|
|
122
|
+
}
|
|
123
|
+
// FAST PATH: Owner always has access
|
|
124
|
+
if (viewerDID === document.owner) {
|
|
125
|
+
return true;
|
|
126
|
+
}
|
|
127
|
+
// Resolve effective access rule
|
|
128
|
+
const rule = await resolveAccessRule(document, document.model, document.owner, permissionType, db, serverConfig);
|
|
129
|
+
// ANYONE: Always allow (read only)
|
|
130
|
+
if (rule.level === 'anyone') {
|
|
131
|
+
return true;
|
|
132
|
+
}
|
|
133
|
+
// No viewer: Deny
|
|
134
|
+
if (!viewerDID) {
|
|
135
|
+
return false;
|
|
136
|
+
}
|
|
137
|
+
// ONLY_OWNER: Check delegation from owner
|
|
138
|
+
if (rule.level === 'only_owner') {
|
|
139
|
+
return await checkDelegation(viewerDID, document.owner, document, permissionType, delegationTokens);
|
|
140
|
+
}
|
|
141
|
+
// ALLOWED_DIDS: Check if viewer is in list or has delegation
|
|
142
|
+
if (rule.level === 'allowed_dids') {
|
|
143
|
+
const allowedDIDs = rule.allowedDIDs || [];
|
|
144
|
+
if (allowedDIDs.includes(viewerDID)) {
|
|
145
|
+
return true;
|
|
146
|
+
}
|
|
147
|
+
// Check if any allowedDID has delegated to viewer
|
|
148
|
+
for (const allowedDID of allowedDIDs){
|
|
149
|
+
if (await checkDelegation(viewerDID, allowedDID, document, permissionType, delegationTokens)) {
|
|
150
|
+
return true;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
155
|
+
// Unknown access level
|
|
156
|
+
return false;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Create an access checker function bound to specific viewer and delegation tokens
|
|
160
|
+
*/ export function createAccessChecker(viewerDID, delegationTokens, db, serverConfig) {
|
|
161
|
+
return async (doc, permissionType)=>{
|
|
162
|
+
return await checkAccess(viewerDID, doc, permissionType, db, serverConfig, delegationTokens);
|
|
163
|
+
};
|
|
164
|
+
}
|
package/lib/data/graphql.d.ts
CHANGED
|
@@ -2,10 +2,12 @@ import type { KubunDB } from '@kubun/db';
|
|
|
2
2
|
import { type Context } from '@kubun/graphql';
|
|
3
3
|
import type { DocumentNode } from '@kubun/protocol';
|
|
4
4
|
import { type ExecutionArgs, type GraphQLSchema, type OperationTypeNode } from 'graphql';
|
|
5
|
+
import type { AccessChecker } from './access-control.js';
|
|
5
6
|
export type ExecutionContext = {
|
|
6
7
|
db: KubunDB;
|
|
7
8
|
viewerDID: string;
|
|
8
9
|
mutatedDocuments?: Record<string, DocumentNode>;
|
|
10
|
+
accessChecker?: AccessChecker;
|
|
9
11
|
};
|
|
10
12
|
export declare function createContext(ctx: ExecutionContext): Context;
|
|
11
13
|
export type ExecuteGraphQLParams = {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"graphql.d.ts","sourceRoot":"","sources":["../../src/data/graphql.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACxC,OAAO,EAAE,KAAK,OAAO,EAA+C,MAAM,gBAAgB,CAAA;AAC1F,OAAO,KAAK,EAAgB,YAAY,EAAE,MAAM,iBAAiB,CAAA;AACjE,OAAO,EACL,KAAK,aAAa,EAElB,KAAK,aAAa,EAElB,KAAK,iBAAiB,EAEvB,MAAM,SAAS,CAAA;AAEhB,MAAM,MAAM,gBAAgB,GAAG;IAC7B,EAAE,EAAE,OAAO,CAAA;IACX,SAAS,EAAE,MAAM,CAAA;IACjB,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAA;
|
|
1
|
+
{"version":3,"file":"graphql.d.ts","sourceRoot":"","sources":["../../src/data/graphql.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACxC,OAAO,EAAE,KAAK,OAAO,EAA+C,MAAM,gBAAgB,CAAA;AAC1F,OAAO,KAAK,EAAgB,YAAY,EAAE,MAAM,iBAAiB,CAAA;AACjE,OAAO,EACL,KAAK,aAAa,EAElB,KAAK,aAAa,EAElB,KAAK,iBAAiB,EAEvB,MAAM,SAAS,CAAA;AAEhB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AAQxD,MAAM,MAAM,gBAAgB,GAAG;IAC7B,EAAE,EAAE,OAAO,CAAA;IACX,SAAS,EAAE,MAAM,CAAA;IACjB,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAA;IAC/C,aAAa,CAAC,EAAE,aAAa,CAAA;CAC9B,CAAA;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,gBAAgB,GAAG,OAAO,CAgG5D;AAED,MAAM,MAAM,oBAAoB,GAAG;IACjC,OAAO,EAAE,gBAAgB,CAAA;IACzB,MAAM,EAAE,aAAa,CAAA;IACrB,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,iBAAiB,CAAA;IACvB,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CACnC,CAAA;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,oBAAoB,GAAG,aAAa,CAiB5E"}
|
package/lib/data/graphql.js
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import { createReadContext } from '@kubun/graphql';
|
|
2
2
|
import { Kind, parse } from 'graphql';
|
|
3
|
+
import { removeDocumentAccessOverride, removeModelAccessDefaults, setDocumentAccessOverride, setModelAccessDefaults } from './mutations.js';
|
|
3
4
|
export function createContext(ctx) {
|
|
4
|
-
const readContext = createReadContext(
|
|
5
|
+
const readContext = createReadContext({
|
|
6
|
+
db: ctx.db,
|
|
7
|
+
viewerDID: ctx.viewerDID,
|
|
8
|
+
accessChecker: ctx.accessChecker
|
|
9
|
+
});
|
|
5
10
|
function getMutationDocument(info) {
|
|
6
11
|
return ctx.mutatedDocuments?.[info.path.key];
|
|
7
12
|
}
|
|
@@ -18,6 +23,36 @@ export function createContext(ctx) {
|
|
|
18
23
|
},
|
|
19
24
|
async executeRemoveMutation (_id, _info) {
|
|
20
25
|
// no-op
|
|
26
|
+
},
|
|
27
|
+
async executeSetModelAccessDefaults (modelId, permissionType, accessLevel, allowedDIDs) {
|
|
28
|
+
await setModelAccessDefaults({
|
|
29
|
+
ownerDID: ctx.viewerDID,
|
|
30
|
+
modelID: modelId,
|
|
31
|
+
permissionType,
|
|
32
|
+
accessLevel,
|
|
33
|
+
allowedDIDs
|
|
34
|
+
}, ctx.db);
|
|
35
|
+
},
|
|
36
|
+
async executeRemoveModelAccessDefaults (modelId, permissionTypes) {
|
|
37
|
+
await removeModelAccessDefaults({
|
|
38
|
+
ownerDID: ctx.viewerDID,
|
|
39
|
+
modelID: modelId,
|
|
40
|
+
permissionTypes
|
|
41
|
+
}, ctx.db);
|
|
42
|
+
},
|
|
43
|
+
async executeSetDocumentAccessOverride (documentId, permissionType, accessLevel, allowedDIDs) {
|
|
44
|
+
await setDocumentAccessOverride({
|
|
45
|
+
documentID: documentId,
|
|
46
|
+
permissionType,
|
|
47
|
+
accessLevel,
|
|
48
|
+
allowedDIDs
|
|
49
|
+
}, ctx.db);
|
|
50
|
+
},
|
|
51
|
+
async executeRemoveDocumentAccessOverride (documentId, permissionTypes) {
|
|
52
|
+
await removeDocumentAccessOverride({
|
|
53
|
+
documentID: documentId,
|
|
54
|
+
permissionTypes
|
|
55
|
+
}, ctx.db);
|
|
21
56
|
}
|
|
22
57
|
};
|
|
23
58
|
}
|
package/lib/data/mutations.d.ts
CHANGED
|
@@ -1,4 +1,41 @@
|
|
|
1
|
+
import type { KubunDB } from '@kubun/db';
|
|
1
2
|
import { type MutationContext } from '@kubun/mutation';
|
|
2
3
|
import { type DocumentNode } from '@kubun/protocol';
|
|
4
|
+
import { type ServerAccessConfig } from './access-control.js';
|
|
3
5
|
export declare function applyMutation(ctx: MutationContext, token: string): Promise<DocumentNode>;
|
|
6
|
+
/**
|
|
7
|
+
* Set model-level access defaults for a user
|
|
8
|
+
*/
|
|
9
|
+
export declare function setModelAccessDefaults(params: {
|
|
10
|
+
ownerDID: string;
|
|
11
|
+
modelID: string;
|
|
12
|
+
permissionType: 'read' | 'write';
|
|
13
|
+
accessLevel: string;
|
|
14
|
+
allowedDIDs: Array<string> | null;
|
|
15
|
+
}, db: KubunDB): Promise<void>;
|
|
16
|
+
/**
|
|
17
|
+
* Remove model-level access defaults for a user
|
|
18
|
+
*/
|
|
19
|
+
export declare function removeModelAccessDefaults(params: {
|
|
20
|
+
ownerDID: string;
|
|
21
|
+
modelID: string;
|
|
22
|
+
permissionTypes: Array<'read' | 'write'>;
|
|
23
|
+
}, db: KubunDB): Promise<void>;
|
|
24
|
+
/**
|
|
25
|
+
* Set document-level access override
|
|
26
|
+
*/
|
|
27
|
+
export declare function setDocumentAccessOverride(params: {
|
|
28
|
+
documentID: string;
|
|
29
|
+
permissionType: 'read' | 'write';
|
|
30
|
+
accessLevel: string;
|
|
31
|
+
allowedDIDs: Array<string> | null;
|
|
32
|
+
}, db: KubunDB): Promise<void>;
|
|
33
|
+
/**
|
|
34
|
+
* Remove document-level access override
|
|
35
|
+
*/
|
|
36
|
+
export declare function removeDocumentAccessOverride(params: {
|
|
37
|
+
documentID: string;
|
|
38
|
+
permissionTypes: Array<'read' | 'write'>;
|
|
39
|
+
}, db: KubunDB): Promise<void>;
|
|
40
|
+
export type { ServerAccessConfig };
|
|
4
41
|
//# sourceMappingURL=mutations.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mutations.d.ts","sourceRoot":"","sources":["../../src/data/mutations.ts"],"names":[],"mappings":"AAEA,OAAO,EAA0B,KAAK,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAC9E,OAAO,EAAyB,KAAK,YAAY,EAAoB,MAAM,iBAAiB,CAAA;
|
|
1
|
+
{"version":3,"file":"mutations.d.ts","sourceRoot":"","sources":["../../src/data/mutations.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAExC,OAAO,EAA0B,KAAK,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAC9E,OAAO,EAAyB,KAAK,YAAY,EAAoB,MAAM,iBAAiB,CAAA;AAE5F,OAAO,EAAE,KAAK,kBAAkB,EAAgB,MAAM,qBAAqB,CAAA;AAI3E,wBAAsB,aAAa,CAAC,GAAG,EAAE,eAAe,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAI9F;AAED;;GAEG;AACH,wBAAsB,sBAAsB,CAC1C,MAAM,EAAE;IACN,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;IACf,cAAc,EAAE,MAAM,GAAG,OAAO,CAAA;IAChC,WAAW,EAAE,MAAM,CAAA;IACnB,WAAW,EAAE,KAAK,CAAC,MAAM,CAAC,GAAG,IAAI,CAAA;CAClC,EACD,EAAE,EAAE,OAAO,GACV,OAAO,CAAC,IAAI,CAAC,CAyBf;AAED;;GAEG;AACH,wBAAsB,yBAAyB,CAC7C,MAAM,EAAE;IACN,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;IACf,eAAe,EAAE,KAAK,CAAC,MAAM,GAAG,OAAO,CAAC,CAAA;CACzC,EACD,EAAE,EAAE,OAAO,GACV,OAAO,CAAC,IAAI,CAAC,CAGf;AAED;;GAEG;AACH,wBAAsB,yBAAyB,CAC7C,MAAM,EAAE;IACN,UAAU,EAAE,MAAM,CAAA;IAClB,cAAc,EAAE,MAAM,GAAG,OAAO,CAAA;IAChC,WAAW,EAAE,MAAM,CAAA;IACnB,WAAW,EAAE,KAAK,CAAC,MAAM,CAAC,GAAG,IAAI,CAAA;CAClC,EACD,EAAE,EAAE,OAAO,GACV,OAAO,CAAC,IAAI,CAAC,CAqCf;AAED;;GAEG;AACH,wBAAsB,4BAA4B,CAChD,MAAM,EAAE;IACN,UAAU,EAAE,MAAM,CAAA;IAClB,eAAe,EAAE,KAAK,CAAC,MAAM,GAAG,OAAO,CAAC,CAAA;CACzC,EACD,EAAE,EAAE,OAAO,GACV,OAAO,CAAC,IAAI,CAAC,CA6Bf;AAGD,YAAY,EAAE,kBAAkB,EAAE,CAAA"}
|
package/lib/data/mutations.js
CHANGED
|
@@ -1,10 +1,114 @@
|
|
|
1
1
|
import { asType, createValidator } from '@enkaku/schema';
|
|
2
2
|
import { verifyToken } from '@enkaku/token';
|
|
3
|
+
import { DocumentID } from '@kubun/id';
|
|
3
4
|
import { applyMutation as apply } from '@kubun/mutation';
|
|
4
5
|
import { documentMutation } from '@kubun/protocol';
|
|
6
|
+
import { validateDIDs } from './access-control.js';
|
|
5
7
|
const validateMutation = createValidator(documentMutation);
|
|
6
8
|
export async function applyMutation(ctx, token) {
|
|
7
9
|
const verified = await verifyToken(token);
|
|
8
10
|
const mutation = asType(validateMutation, verified.payload);
|
|
9
11
|
return await apply(ctx, mutation);
|
|
10
12
|
}
|
|
13
|
+
/**
|
|
14
|
+
* Set model-level access defaults for a user
|
|
15
|
+
*/ export async function setModelAccessDefaults(params, db) {
|
|
16
|
+
const { ownerDID, modelID, permissionType, accessLevel, allowedDIDs } = params;
|
|
17
|
+
// Validate DIDs if provided
|
|
18
|
+
if (allowedDIDs && allowedDIDs.length > 0) {
|
|
19
|
+
validateDIDs(allowedDIDs);
|
|
20
|
+
}
|
|
21
|
+
// Validate access level for permission type
|
|
22
|
+
const validLevels = {
|
|
23
|
+
read: [
|
|
24
|
+
'only_owner',
|
|
25
|
+
'anyone',
|
|
26
|
+
'allowed_dids'
|
|
27
|
+
],
|
|
28
|
+
write: [
|
|
29
|
+
'only_owner',
|
|
30
|
+
'allowed_dids'
|
|
31
|
+
]
|
|
32
|
+
};
|
|
33
|
+
if (!validLevels[permissionType].includes(accessLevel)) {
|
|
34
|
+
throw new Error(`Invalid access level "${accessLevel}" for permission type "${permissionType}"`);
|
|
35
|
+
}
|
|
36
|
+
await db.setUserModelAccessDefault({
|
|
37
|
+
ownerDID,
|
|
38
|
+
modelID,
|
|
39
|
+
permissionType,
|
|
40
|
+
accessLevel,
|
|
41
|
+
allowedDIDs
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Remove model-level access defaults for a user
|
|
46
|
+
*/ export async function removeModelAccessDefaults(params, db) {
|
|
47
|
+
const { ownerDID, modelID, permissionTypes } = params;
|
|
48
|
+
await db.removeUserModelAccessDefaults(ownerDID, modelID, permissionTypes);
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Set document-level access override
|
|
52
|
+
*/ export async function setDocumentAccessOverride(params, db) {
|
|
53
|
+
const { documentID, permissionType, accessLevel, allowedDIDs } = params;
|
|
54
|
+
// Validate DIDs if provided
|
|
55
|
+
if (allowedDIDs && allowedDIDs.length > 0) {
|
|
56
|
+
validateDIDs(allowedDIDs);
|
|
57
|
+
}
|
|
58
|
+
// Get the document
|
|
59
|
+
const docID = DocumentID.fromString(documentID);
|
|
60
|
+
const doc = await db.getDocument(docID);
|
|
61
|
+
if (!doc) {
|
|
62
|
+
throw new Error(`Document not found: ${documentID}`);
|
|
63
|
+
}
|
|
64
|
+
// Preserve existing permissions
|
|
65
|
+
const existingPerms = doc.data?.accessPermissions || {};
|
|
66
|
+
// Update the specific permission type
|
|
67
|
+
const updatedPerms = {
|
|
68
|
+
...existingPerms,
|
|
69
|
+
[permissionType]: {
|
|
70
|
+
level: accessLevel,
|
|
71
|
+
allowedDIDs
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
// Update the document with new access permissions
|
|
75
|
+
await db.saveDocument({
|
|
76
|
+
id: docID,
|
|
77
|
+
data: {
|
|
78
|
+
...doc.data,
|
|
79
|
+
accessPermissions: updatedPerms
|
|
80
|
+
},
|
|
81
|
+
state: null,
|
|
82
|
+
existing: doc
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Remove document-level access override
|
|
87
|
+
*/ export async function removeDocumentAccessOverride(params, db) {
|
|
88
|
+
const { documentID, permissionTypes } = params;
|
|
89
|
+
// Get the document
|
|
90
|
+
const docID = DocumentID.fromString(documentID);
|
|
91
|
+
const doc = await db.getDocument(docID);
|
|
92
|
+
if (!doc) {
|
|
93
|
+
// Silently return if document doesn't exist
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
// Preserve existing permissions, removing specified types
|
|
97
|
+
const existingPerms = doc.data?.accessPermissions || {};
|
|
98
|
+
const updatedPerms = {
|
|
99
|
+
...existingPerms
|
|
100
|
+
};
|
|
101
|
+
for (const permType of permissionTypes){
|
|
102
|
+
delete updatedPerms[permType];
|
|
103
|
+
}
|
|
104
|
+
// Update the document with updated access permissions
|
|
105
|
+
await db.saveDocument({
|
|
106
|
+
id: docID,
|
|
107
|
+
data: {
|
|
108
|
+
...doc.data,
|
|
109
|
+
accessPermissions: Object.keys(updatedPerms).length > 0 ? updatedPerms : undefined
|
|
110
|
+
},
|
|
111
|
+
state: null,
|
|
112
|
+
existing: doc
|
|
113
|
+
});
|
|
114
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"graph.d.ts","sourceRoot":"","sources":["../../src/handlers/graph.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAA;AAKvD,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"graph.d.ts","sourceRoot":"","sources":["../../src/handlers/graph.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAA;AAKvD,OAAO,KAAK,EAIV,aAAa,EACd,MAAM,iBAAiB,CAAA;AAexB,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAA;AAUtD,wBAAgB,cAAc,CAC5B,cAAc,EAAE,oBAAoB,GACnC,iBAAiB,CAAC,aAAa,CAAC,CAqKlC"}
|
package/lib/handlers/graph.js
CHANGED
|
@@ -5,6 +5,7 @@ import { AttachmentID } from '@kubun/id';
|
|
|
5
5
|
import { GraphModel } from '@kubun/protocol';
|
|
6
6
|
import { execute, OperationTypeNode, // printSchema,
|
|
7
7
|
subscribe } from 'graphql';
|
|
8
|
+
import { createAccessChecker } from '../data/access-control.js';
|
|
8
9
|
import { getExecutionArgs } from '../data/graphql.js';
|
|
9
10
|
import { applyMutation } from '../data/mutations.js';
|
|
10
11
|
function toGraphResult(result) {
|
|
@@ -15,7 +16,7 @@ function toGraphResult(result) {
|
|
|
15
16
|
};
|
|
16
17
|
}
|
|
17
18
|
export function createHandlers(handlersParams) {
|
|
18
|
-
const { db, logger } = handlersParams;
|
|
19
|
+
const { db, logger, serverAccessConfig } = handlersParams;
|
|
19
20
|
const graphModels = {};
|
|
20
21
|
async function getGraphModels(id) {
|
|
21
22
|
if (graphModels[id] == null) {
|
|
@@ -30,7 +31,10 @@ export function createHandlers(handlersParams) {
|
|
|
30
31
|
logger.debug('cached model for graph {id}', {
|
|
31
32
|
id
|
|
32
33
|
});
|
|
33
|
-
return
|
|
34
|
+
return {
|
|
35
|
+
record: graph.record,
|
|
36
|
+
aliases: graph.aliases
|
|
37
|
+
};
|
|
34
38
|
});
|
|
35
39
|
}
|
|
36
40
|
return await graphModels[id];
|
|
@@ -38,8 +42,8 @@ export function createHandlers(handlersParams) {
|
|
|
38
42
|
const schemas = {};
|
|
39
43
|
async function getGraphQLSchema(id) {
|
|
40
44
|
if (schemas[id] == null) {
|
|
41
|
-
schemas[id] = getGraphModels(id).then((
|
|
42
|
-
const schema = createSchema(
|
|
45
|
+
schemas[id] = getGraphModels(id).then((model)=>{
|
|
46
|
+
const schema = createSchema(model);
|
|
43
47
|
logger.debug('cached schema for graph {id}', {
|
|
44
48
|
id
|
|
45
49
|
});
|
|
@@ -71,7 +75,7 @@ export function createHandlers(handlersParams) {
|
|
|
71
75
|
});
|
|
72
76
|
return {
|
|
73
77
|
id,
|
|
74
|
-
|
|
78
|
+
...model.toJSON()
|
|
75
79
|
};
|
|
76
80
|
},
|
|
77
81
|
'graph/list': async ()=>{
|
|
@@ -84,9 +88,7 @@ export function createHandlers(handlersParams) {
|
|
|
84
88
|
};
|
|
85
89
|
},
|
|
86
90
|
'graph/load': async (ctx)=>{
|
|
87
|
-
return
|
|
88
|
-
models: await getGraphModels(ctx.param.id)
|
|
89
|
-
};
|
|
91
|
+
return await getGraphModels(ctx.param.id);
|
|
90
92
|
},
|
|
91
93
|
'graph/mutate': async (ctx)=>{
|
|
92
94
|
const attachments = Object.entries(ctx.param.attachments ?? {}).map(([key, value])=>{
|
|
@@ -109,9 +111,11 @@ export function createHandlers(handlersParams) {
|
|
|
109
111
|
}, mutation);
|
|
110
112
|
}));
|
|
111
113
|
const payload = ctx.message.payload;
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
114
|
+
const viewerDID = payload.sub || payload.iss;
|
|
115
|
+
const delegationTokens = payload.cap ? Array.isArray(payload.cap) ? payload.cap : [
|
|
116
|
+
payload.cap
|
|
117
|
+
] : undefined;
|
|
118
|
+
const accessChecker = createAccessChecker(viewerDID, delegationTokens, db, serverAccessConfig);
|
|
115
119
|
return await executeGraphQL({
|
|
116
120
|
schema: await getGraphQLSchema(ctx.param.id),
|
|
117
121
|
type: OperationTypeNode.MUTATION,
|
|
@@ -120,15 +124,18 @@ export function createHandlers(handlersParams) {
|
|
|
120
124
|
context: {
|
|
121
125
|
db,
|
|
122
126
|
mutatedDocuments,
|
|
123
|
-
viewerDID
|
|
127
|
+
viewerDID,
|
|
128
|
+
accessChecker
|
|
124
129
|
}
|
|
125
130
|
});
|
|
126
131
|
},
|
|
127
132
|
'graph/query': async (ctx)=>{
|
|
128
133
|
const payload = ctx.message.payload;
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
134
|
+
const viewerDID = payload.sub || payload.iss;
|
|
135
|
+
const delegationTokens = payload.cap ? Array.isArray(payload.cap) ? payload.cap : [
|
|
136
|
+
payload.cap
|
|
137
|
+
] : undefined;
|
|
138
|
+
const accessChecker = createAccessChecker(viewerDID, delegationTokens, db, serverAccessConfig);
|
|
132
139
|
return await executeGraphQL({
|
|
133
140
|
schema: await getGraphQLSchema(ctx.param.id),
|
|
134
141
|
type: OperationTypeNode.QUERY,
|
|
@@ -136,13 +143,18 @@ export function createHandlers(handlersParams) {
|
|
|
136
143
|
variables: ctx.param.variables ?? {},
|
|
137
144
|
context: {
|
|
138
145
|
db,
|
|
139
|
-
viewerDID
|
|
146
|
+
viewerDID,
|
|
147
|
+
accessChecker
|
|
140
148
|
}
|
|
141
149
|
});
|
|
142
150
|
},
|
|
143
151
|
'graph/subscribe': async (ctx)=>{
|
|
144
152
|
const payload = ctx.message.payload;
|
|
145
|
-
const viewerDID = payload.iss;
|
|
153
|
+
const viewerDID = payload.sub || payload.iss;
|
|
154
|
+
const delegationTokens = payload.cap ? Array.isArray(payload.cap) ? payload.cap : [
|
|
155
|
+
payload.cap
|
|
156
|
+
] : undefined;
|
|
157
|
+
const accessChecker = createAccessChecker(viewerDID, delegationTokens, db, serverAccessConfig);
|
|
146
158
|
const args = getExecutionArgs({
|
|
147
159
|
schema: await getGraphQLSchema(ctx.param.id),
|
|
148
160
|
type: OperationTypeNode.SUBSCRIPTION,
|
|
@@ -150,7 +162,8 @@ export function createHandlers(handlersParams) {
|
|
|
150
162
|
variables: ctx.param.variables ?? {},
|
|
151
163
|
context: {
|
|
152
164
|
db,
|
|
153
|
-
viewerDID
|
|
165
|
+
viewerDID,
|
|
166
|
+
accessChecker
|
|
154
167
|
}
|
|
155
168
|
});
|
|
156
169
|
const subscription = await subscribe(args);
|
package/lib/handlers/types.d.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import type { KubunDB } from '@kubun/db';
|
|
2
2
|
import type { Logger } from '@kubun/logger';
|
|
3
|
+
import type { ServerAccessConfig } from '../data/access-control.js';
|
|
3
4
|
export type CreateHandlersParams = {
|
|
4
5
|
db: KubunDB;
|
|
5
6
|
logger: Logger;
|
|
7
|
+
serverAccessConfig: ServerAccessConfig;
|
|
6
8
|
};
|
|
7
9
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/handlers/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACxC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAA;AAE3C,MAAM,MAAM,oBAAoB,GAAG;IACjC,EAAE,EAAE,OAAO,CAAA;IACX,MAAM,EAAE,MAAM,CAAA;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/handlers/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACxC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAA;AAE3C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAA;AAEnE,MAAM,MAAM,oBAAoB,GAAG;IACjC,EAAE,EAAE,OAAO,CAAA;IACX,MAAM,EAAE,MAAM,CAAA;IACd,kBAAkB,EAAE,kBAAkB,CAAA;CACvC,CAAA"}
|
package/lib/server.d.ts
CHANGED
|
@@ -8,6 +8,10 @@ export type ServerParams = {
|
|
|
8
8
|
db: KubunDB | DBParams;
|
|
9
9
|
id: string;
|
|
10
10
|
logger?: Logger;
|
|
11
|
+
defaultAccessLevel?: {
|
|
12
|
+
read?: 'only_owner' | 'anyone' | 'allowed_dids';
|
|
13
|
+
write?: 'only_owner' | 'allowed_dids';
|
|
14
|
+
};
|
|
11
15
|
};
|
|
12
16
|
export type CreateClientParams = Omit<ClientParams, 'serverID' | 'transport'> & {
|
|
13
17
|
signal?: AbortSignal;
|
|
@@ -16,6 +20,10 @@ export declare class KubunServer {
|
|
|
16
20
|
#private;
|
|
17
21
|
constructor(params: ServerParams);
|
|
18
22
|
get db(): KubunDB;
|
|
23
|
+
get defaultAccessLevel(): {
|
|
24
|
+
read: "only_owner" | "anyone" | "allowed_dids";
|
|
25
|
+
write: "only_owner" | "allowed_dids";
|
|
26
|
+
};
|
|
19
27
|
createClient(params: CreateClientParams): KubunClient;
|
|
20
28
|
serve(transport: ServerTransport, signal?: AbortSignal): Server<Protocol>;
|
|
21
29
|
}
|
package/lib/server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,MAAM,EAAS,MAAM,gBAAgB,CAAA;AAEnD,OAAO,EAAE,KAAK,YAAY,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAC9D,OAAO,EAAE,KAAK,QAAQ,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAClD,OAAO,EAAkB,KAAK,MAAM,EAAE,MAAM,eAAe,CAAA;AAC3D,OAAO,KAAK,EAAiB,QAAQ,EAAiB,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAI9F,MAAM,MAAM,YAAY,GAAG;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAA;IAChD,EAAE,EAAE,OAAO,GAAG,QAAQ,CAAA;IACtB,EAAE,EAAE,MAAM,CAAA;IACV,MAAM,CAAC,EAAE,MAAM,CAAA;
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,MAAM,EAAS,MAAM,gBAAgB,CAAA;AAEnD,OAAO,EAAE,KAAK,YAAY,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAC9D,OAAO,EAAE,KAAK,QAAQ,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAClD,OAAO,EAAkB,KAAK,MAAM,EAAE,MAAM,eAAe,CAAA;AAC3D,OAAO,KAAK,EAAiB,QAAQ,EAAiB,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAI9F,MAAM,MAAM,YAAY,GAAG;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAA;IAChD,EAAE,EAAE,OAAO,GAAG,QAAQ,CAAA;IACtB,EAAE,EAAE,MAAM,CAAA;IACV,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,kBAAkB,CAAC,EAAE;QACnB,IAAI,CAAC,EAAE,YAAY,GAAG,QAAQ,GAAG,cAAc,CAAA;QAC/C,KAAK,CAAC,EAAE,YAAY,GAAG,cAAc,CAAA;KACtC,CAAA;CACF,CAAA;AAED,MAAM,MAAM,kBAAkB,GAAG,IAAI,CAAC,YAAY,EAAE,UAAU,GAAG,WAAW,CAAC,GAAG;IAC9E,MAAM,CAAC,EAAE,WAAW,CAAA;CACrB,CAAA;AAED,qBAAa,WAAW;;gBAWV,MAAM,EAAE,YAAY;IAmBhC,IAAI,EAAE,IAAI,OAAO,CAEhB;IAED,IAAI,kBAAkB;cA3Bd,YAAY,GAAG,QAAQ,GAAG,cAAc;eACvC,YAAY,GAAG,cAAc;MA4BrC;IAED,YAAY,CAAC,MAAM,EAAE,kBAAkB,GAAG,WAAW;IAkBrD,KAAK,CAAC,SAAS,EAAE,eAAe,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,MAAM,CAAC,QAAQ,CAAC;CAU1E"}
|
package/lib/server.js
CHANGED
|
@@ -10,6 +10,7 @@ export class KubunServer {
|
|
|
10
10
|
#handlers;
|
|
11
11
|
#id;
|
|
12
12
|
#logger;
|
|
13
|
+
#defaultAccessLevel;
|
|
13
14
|
constructor(params){
|
|
14
15
|
const { access, db, id } = params;
|
|
15
16
|
const logger = params.logger ?? getKubunLogger('server', {
|
|
@@ -17,16 +18,27 @@ export class KubunServer {
|
|
|
17
18
|
});
|
|
18
19
|
this.#access = access ?? {};
|
|
19
20
|
this.#db = db instanceof KubunDB ? db : new KubunDB(db);
|
|
21
|
+
this.#id = id;
|
|
22
|
+
this.#logger = logger;
|
|
23
|
+
// Secure by default
|
|
24
|
+
this.#defaultAccessLevel = {
|
|
25
|
+
read: params.defaultAccessLevel?.read ?? 'only_owner',
|
|
26
|
+
write: params.defaultAccessLevel?.write ?? 'only_owner'
|
|
27
|
+
};
|
|
20
28
|
this.#handlers = createHandlers({
|
|
21
29
|
db: this.#db,
|
|
22
|
-
logger
|
|
30
|
+
logger,
|
|
31
|
+
serverAccessConfig: {
|
|
32
|
+
defaultAccessLevel: this.#defaultAccessLevel
|
|
33
|
+
}
|
|
23
34
|
});
|
|
24
|
-
this.#id = id;
|
|
25
|
-
this.#logger = logger;
|
|
26
35
|
}
|
|
27
36
|
get db() {
|
|
28
37
|
return this.#db;
|
|
29
38
|
}
|
|
39
|
+
get defaultAccessLevel() {
|
|
40
|
+
return this.#defaultAccessLevel;
|
|
41
|
+
}
|
|
30
42
|
createClient(params) {
|
|
31
43
|
const { signal, ...clientParams } = params;
|
|
32
44
|
const transports = new DirectTransports({
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kubun/server",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.3",
|
|
4
4
|
"license": "see LICENSE.md",
|
|
5
5
|
"keywords": [],
|
|
6
6
|
"type": "module",
|
|
@@ -16,26 +16,28 @@
|
|
|
16
16
|
"sideEffects": false,
|
|
17
17
|
"dependencies": {
|
|
18
18
|
"@enkaku/async": "^0.12.2",
|
|
19
|
+
"@enkaku/capability": "^0.12.1",
|
|
19
20
|
"@enkaku/codec": "^0.12.0",
|
|
20
21
|
"@enkaku/generator": "^0.12.1",
|
|
21
22
|
"@enkaku/schema": "^0.12.1",
|
|
22
|
-
"@enkaku/server": "^0.12.
|
|
23
|
+
"@enkaku/server": "^0.12.3",
|
|
23
24
|
"@enkaku/token": "0.12.3",
|
|
24
25
|
"@enkaku/transport": "0.12.0",
|
|
25
26
|
"graphql": "^16.12.0",
|
|
26
|
-
"@kubun/
|
|
27
|
-
"@kubun/id": "^0.4.0",
|
|
28
|
-
"@kubun/protocol": "^0.4.0",
|
|
29
|
-
"@kubun/client": "^0.4.0",
|
|
30
|
-
"@kubun/graphql": "^0.4.1",
|
|
27
|
+
"@kubun/client": "^0.4.1",
|
|
31
28
|
"@kubun/mutation": "^0.4.0",
|
|
32
|
-
"@kubun/
|
|
29
|
+
"@kubun/graphql": "^0.4.5",
|
|
30
|
+
"@kubun/logger": "^0.4.0",
|
|
31
|
+
"@kubun/protocol": "^0.4.1",
|
|
32
|
+
"@kubun/db": "^0.4.0",
|
|
33
|
+
"@kubun/id": "^0.4.0"
|
|
33
34
|
},
|
|
34
35
|
"devDependencies": {
|
|
35
36
|
"@databases/pg-test": "^3.1.2",
|
|
36
37
|
"@enkaku/stream": "^0.12.1",
|
|
37
|
-
"@kubun/scalars": "^0.4.0",
|
|
38
38
|
"@kubun/db-postgres": "^0.4.0",
|
|
39
|
+
"@kubun/db-sqlite": "^0.4.0",
|
|
40
|
+
"@kubun/scalars": "^0.4.0",
|
|
39
41
|
"@kubun/test-utils": "^0.4.0"
|
|
40
42
|
},
|
|
41
43
|
"scripts": {
|