@objectstack/core 4.0.4 → 4.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +95 -10
- package/dist/index.cjs +169 -507
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +24 -223
- package/dist/index.d.ts +24 -223
- package/dist/index.js +175 -505
- package/dist/index.js.map +1 -1
- package/dist/logger.cjs +177 -0
- package/dist/logger.cjs.map +1 -0
- package/dist/logger.d.cts +26 -0
- package/dist/logger.d.ts +26 -0
- package/dist/logger.js +158 -0
- package/dist/logger.js.map +1 -0
- package/package.json +36 -15
- package/.turbo/turbo-build.log +0 -22
- package/ADVANCED_FEATURES.md +0 -380
- package/API_REGISTRY.md +0 -392
- package/CHANGELOG.md +0 -472
- package/PHASE2_IMPLEMENTATION.md +0 -388
- package/REFACTORING_SUMMARY.md +0 -40
- package/examples/api-registry-example.ts +0 -559
- package/examples/kernel-features-example.ts +0 -311
- package/examples/phase2-integration.ts +0 -357
- package/src/api-registry-plugin.test.ts +0 -393
- package/src/api-registry-plugin.ts +0 -89
- package/src/api-registry.test.ts +0 -1089
- package/src/api-registry.ts +0 -739
- package/src/contracts/data-engine.ts +0 -57
- package/src/contracts/http-server.ts +0 -151
- package/src/contracts/logger.ts +0 -72
- package/src/dependency-resolver.test.ts +0 -287
- package/src/dependency-resolver.ts +0 -390
- package/src/fallbacks/fallbacks.test.ts +0 -281
- package/src/fallbacks/index.ts +0 -26
- package/src/fallbacks/memory-cache.ts +0 -34
- package/src/fallbacks/memory-i18n.ts +0 -112
- package/src/fallbacks/memory-job.ts +0 -23
- package/src/fallbacks/memory-metadata.ts +0 -50
- package/src/fallbacks/memory-queue.ts +0 -28
- package/src/health-monitor.test.ts +0 -81
- package/src/health-monitor.ts +0 -318
- package/src/hot-reload.ts +0 -382
- package/src/index.ts +0 -50
- package/src/kernel-base.ts +0 -273
- package/src/kernel.test.ts +0 -624
- package/src/kernel.ts +0 -631
- package/src/lite-kernel.test.ts +0 -248
- package/src/lite-kernel.ts +0 -137
- package/src/logger.test.ts +0 -116
- package/src/logger.ts +0 -355
- package/src/namespace-resolver.test.ts +0 -130
- package/src/namespace-resolver.ts +0 -188
- package/src/package-manager.test.ts +0 -225
- package/src/package-manager.ts +0 -428
- package/src/plugin-loader.test.ts +0 -421
- package/src/plugin-loader.ts +0 -484
- package/src/qa/adapter.ts +0 -16
- package/src/qa/http-adapter.ts +0 -116
- package/src/qa/index.ts +0 -5
- package/src/qa/runner.ts +0 -189
- package/src/security/index.ts +0 -50
- package/src/security/permission-manager.test.ts +0 -256
- package/src/security/permission-manager.ts +0 -338
- package/src/security/plugin-config-validator.test.ts +0 -276
- package/src/security/plugin-config-validator.ts +0 -193
- package/src/security/plugin-permission-enforcer.test.ts +0 -251
- package/src/security/plugin-permission-enforcer.ts +0 -436
- package/src/security/plugin-signature-verifier.ts +0 -403
- package/src/security/sandbox-runtime.ts +0 -462
- package/src/security/security-scanner.ts +0 -367
- package/src/types.ts +0 -120
- package/src/utils/env.test.ts +0 -62
- package/src/utils/env.ts +0 -53
- package/tsconfig.json +0 -10
- package/vitest.config.ts +0 -10
package/src/qa/runner.ts
DELETED
|
@@ -1,189 +0,0 @@
|
|
|
1
|
-
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
|
|
2
|
-
|
|
3
|
-
import { QA } from '@objectstack/spec';
|
|
4
|
-
import { TestExecutionAdapter } from './adapter.js';
|
|
5
|
-
|
|
6
|
-
export interface TestResult {
|
|
7
|
-
scenarioId: string;
|
|
8
|
-
passed: boolean;
|
|
9
|
-
steps: StepResult[];
|
|
10
|
-
error?: unknown;
|
|
11
|
-
duration: number;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export interface StepResult {
|
|
15
|
-
stepName: string;
|
|
16
|
-
passed: boolean;
|
|
17
|
-
error?: unknown;
|
|
18
|
-
output?: unknown;
|
|
19
|
-
duration: number;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export class TestRunner {
|
|
23
|
-
constructor(private adapter: TestExecutionAdapter) {}
|
|
24
|
-
|
|
25
|
-
async runSuite(suite: QA.TestSuite): Promise<TestResult[]> {
|
|
26
|
-
const results: TestResult[] = [];
|
|
27
|
-
for (const scenario of suite.scenarios) {
|
|
28
|
-
results.push(await this.runScenario(scenario));
|
|
29
|
-
}
|
|
30
|
-
return results;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
async runScenario(scenario: QA.TestScenario): Promise<TestResult> {
|
|
34
|
-
const startTime = Date.now();
|
|
35
|
-
const context: Record<string, unknown> = {}; // Variable context
|
|
36
|
-
|
|
37
|
-
// Initialize context from initial payload if needed? Currently schema doesn't have initial context prop on Scenario
|
|
38
|
-
// But we defined TestContextSchema separately.
|
|
39
|
-
|
|
40
|
-
// Setup
|
|
41
|
-
if (scenario.setup) {
|
|
42
|
-
for (const step of scenario.setup) {
|
|
43
|
-
try {
|
|
44
|
-
await this.runStep(step, context);
|
|
45
|
-
} catch (e) {
|
|
46
|
-
return {
|
|
47
|
-
scenarioId: scenario.id,
|
|
48
|
-
passed: false,
|
|
49
|
-
steps: [],
|
|
50
|
-
error: `Setup failed: ${e instanceof Error ? e.message : String(e)}`,
|
|
51
|
-
duration: Date.now() - startTime
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
const stepResults: StepResult[] = [];
|
|
58
|
-
let scenarioPassed = true;
|
|
59
|
-
let scenarioError: unknown = undefined;
|
|
60
|
-
|
|
61
|
-
// Main Steps
|
|
62
|
-
for (const step of scenario.steps) {
|
|
63
|
-
const stepStartTime = Date.now();
|
|
64
|
-
try {
|
|
65
|
-
const output = await this.runStep(step, context);
|
|
66
|
-
stepResults.push({
|
|
67
|
-
stepName: step.name,
|
|
68
|
-
passed: true,
|
|
69
|
-
output,
|
|
70
|
-
duration: Date.now() - stepStartTime
|
|
71
|
-
});
|
|
72
|
-
} catch (e) {
|
|
73
|
-
scenarioPassed = false;
|
|
74
|
-
scenarioError = e;
|
|
75
|
-
stepResults.push({
|
|
76
|
-
stepName: step.name,
|
|
77
|
-
passed: false,
|
|
78
|
-
error: e,
|
|
79
|
-
duration: Date.now() - stepStartTime
|
|
80
|
-
});
|
|
81
|
-
break; // Stop on first failure
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// Teardown (run even if failed)
|
|
86
|
-
if (scenario.teardown) {
|
|
87
|
-
for (const step of scenario.teardown) {
|
|
88
|
-
try {
|
|
89
|
-
await this.runStep(step, context);
|
|
90
|
-
} catch (e) {
|
|
91
|
-
// Log teardown failure but don't override main failure if it exists
|
|
92
|
-
if (scenarioPassed) {
|
|
93
|
-
scenarioPassed = false;
|
|
94
|
-
scenarioError = `Teardown failed: ${e instanceof Error ? e.message : String(e)}`;
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
return {
|
|
101
|
-
scenarioId: scenario.id,
|
|
102
|
-
passed: scenarioPassed,
|
|
103
|
-
steps: stepResults,
|
|
104
|
-
error: scenarioError,
|
|
105
|
-
duration: Date.now() - startTime
|
|
106
|
-
};
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
private async runStep(step: QA.TestStep, context: Record<string, unknown>): Promise<unknown> {
|
|
110
|
-
// 1. Resolve Variables with Context (Simple interpolation or just pass context?)
|
|
111
|
-
// For now, assume adpater handles context resolution or we do basic replacement
|
|
112
|
-
const resolvedAction = this.resolveVariables(step.action, context);
|
|
113
|
-
|
|
114
|
-
// 2. Execute Action
|
|
115
|
-
const result = await this.adapter.execute(resolvedAction, context);
|
|
116
|
-
|
|
117
|
-
// 3. Capture Outputs
|
|
118
|
-
if (step.capture) {
|
|
119
|
-
for (const [varName, path] of Object.entries(step.capture)) {
|
|
120
|
-
context[varName] = this.getValueByPath(result, path);
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
// 4. Run Assertions
|
|
125
|
-
if (step.assertions) {
|
|
126
|
-
for (const assertion of step.assertions) {
|
|
127
|
-
this.assert(result, assertion, context);
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
return result;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
private resolveVariables(action: QA.TestAction, context: Record<string, unknown>): QA.TestAction {
|
|
135
|
-
const actionStr = JSON.stringify(action);
|
|
136
|
-
const resolved = actionStr.replace(/\{\{([^}]+)\}\}/g, (_match, varPath: string) => {
|
|
137
|
-
const value = this.getValueByPath(context, varPath.trim());
|
|
138
|
-
if (value === undefined) return _match; // Keep unresolved
|
|
139
|
-
return typeof value === 'string' ? value : JSON.stringify(value);
|
|
140
|
-
});
|
|
141
|
-
try {
|
|
142
|
-
return JSON.parse(resolved) as QA.TestAction;
|
|
143
|
-
} catch {
|
|
144
|
-
return action; // Fallback to original if parse fails
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
private getValueByPath(obj: unknown, path: string): unknown {
|
|
149
|
-
if (!path) return obj;
|
|
150
|
-
const parts = path.split('.');
|
|
151
|
-
let current: any = obj;
|
|
152
|
-
for (const part of parts) {
|
|
153
|
-
if (current === null || current === undefined) return undefined;
|
|
154
|
-
current = current[part];
|
|
155
|
-
}
|
|
156
|
-
return current;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
private assert(result: unknown, assertion: QA.TestAssertion, _context: Record<string, unknown>) {
|
|
160
|
-
const actual = this.getValueByPath(result, assertion.field);
|
|
161
|
-
// Resolve expected value if it's a variable ref?
|
|
162
|
-
const expected = assertion.expectedValue; // Simplify for now
|
|
163
|
-
|
|
164
|
-
switch (assertion.operator) {
|
|
165
|
-
case 'equals':
|
|
166
|
-
if (actual !== expected) throw new Error(`Assertion failed: ${assertion.field} expected ${expected}, got ${actual}`);
|
|
167
|
-
break;
|
|
168
|
-
case 'not_equals':
|
|
169
|
-
if (actual === expected) throw new Error(`Assertion failed: ${assertion.field} expected not ${expected}, got ${actual}`);
|
|
170
|
-
break;
|
|
171
|
-
case 'contains':
|
|
172
|
-
if (Array.isArray(actual)) {
|
|
173
|
-
if (!actual.includes(expected)) throw new Error(`Assertion failed: ${assertion.field} array does not contain ${expected}`);
|
|
174
|
-
} else if (typeof actual === 'string') {
|
|
175
|
-
if (!actual.includes(String(expected))) throw new Error(`Assertion failed: ${assertion.field} string does not contain ${expected}`);
|
|
176
|
-
}
|
|
177
|
-
break;
|
|
178
|
-
case 'not_null':
|
|
179
|
-
if (actual === null || actual === undefined) throw new Error(`Assertion failed: ${assertion.field} is null`);
|
|
180
|
-
break;
|
|
181
|
-
case 'is_null':
|
|
182
|
-
if (actual !== null && actual !== undefined) throw new Error(`Assertion failed: ${assertion.field} is not null`);
|
|
183
|
-
break;
|
|
184
|
-
// ... Add other operators
|
|
185
|
-
default:
|
|
186
|
-
throw new Error(`Unknown assertion operator: ${assertion.operator}`);
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
}
|
package/src/security/index.ts
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Security Module
|
|
5
|
-
*
|
|
6
|
-
* Provides security features for the ObjectStack microkernel:
|
|
7
|
-
* - Plugin signature verification
|
|
8
|
-
* - Plugin configuration validation
|
|
9
|
-
* - Permission and capability enforcement
|
|
10
|
-
*
|
|
11
|
-
* @module @objectstack/core/security
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
export {
|
|
15
|
-
PluginSignatureVerifier,
|
|
16
|
-
type PluginSignatureConfig,
|
|
17
|
-
type SignatureVerificationResult,
|
|
18
|
-
} from './plugin-signature-verifier.js';
|
|
19
|
-
|
|
20
|
-
export {
|
|
21
|
-
PluginConfigValidator,
|
|
22
|
-
createPluginConfigValidator,
|
|
23
|
-
} from './plugin-config-validator.js';
|
|
24
|
-
|
|
25
|
-
export {
|
|
26
|
-
PluginPermissionEnforcer,
|
|
27
|
-
SecurePluginContext,
|
|
28
|
-
createPluginPermissionEnforcer,
|
|
29
|
-
type PluginPermissions,
|
|
30
|
-
type PermissionCheckResult,
|
|
31
|
-
} from './plugin-permission-enforcer.js';
|
|
32
|
-
|
|
33
|
-
// Advanced security components (Phase 2)
|
|
34
|
-
export {
|
|
35
|
-
PluginPermissionManager,
|
|
36
|
-
type PermissionGrant,
|
|
37
|
-
type PermissionCheckResult as PluginPermissionCheckResult,
|
|
38
|
-
} from './permission-manager.js';
|
|
39
|
-
|
|
40
|
-
export {
|
|
41
|
-
PluginSandboxRuntime,
|
|
42
|
-
type SandboxContext,
|
|
43
|
-
type ResourceUsage,
|
|
44
|
-
} from './sandbox-runtime.js';
|
|
45
|
-
|
|
46
|
-
export {
|
|
47
|
-
PluginSecurityScanner,
|
|
48
|
-
type ScanTarget,
|
|
49
|
-
type SecurityIssue,
|
|
50
|
-
} from './security-scanner.js';
|
|
@@ -1,256 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach } from 'vitest';
|
|
2
|
-
import { PluginPermissionManager } from './permission-manager.js';
|
|
3
|
-
import { createLogger } from '../logger.js';
|
|
4
|
-
import type { PermissionSet } from '@objectstack/spec/kernel';
|
|
5
|
-
|
|
6
|
-
describe('PluginPermissionManager', () => {
|
|
7
|
-
let manager: PluginPermissionManager;
|
|
8
|
-
let logger: ReturnType<typeof createLogger>;
|
|
9
|
-
|
|
10
|
-
beforeEach(() => {
|
|
11
|
-
logger = createLogger({ level: 'silent' });
|
|
12
|
-
manager = new PluginPermissionManager(logger);
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
describe('registerPermissions', () => {
|
|
16
|
-
it('should register permissions for a plugin', () => {
|
|
17
|
-
const permissionSet: PermissionSet = {
|
|
18
|
-
permissions: [
|
|
19
|
-
{
|
|
20
|
-
id: 'read-data',
|
|
21
|
-
resource: 'data.object',
|
|
22
|
-
actions: ['read'],
|
|
23
|
-
scope: 'plugin',
|
|
24
|
-
description: 'Read object data',
|
|
25
|
-
required: true,
|
|
26
|
-
},
|
|
27
|
-
],
|
|
28
|
-
defaultGrant: 'prompt',
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
manager.registerPermissions('test-plugin', permissionSet);
|
|
32
|
-
|
|
33
|
-
const permissions = manager.getPluginPermissions('test-plugin');
|
|
34
|
-
expect(permissions).toHaveLength(1);
|
|
35
|
-
expect(permissions[0].id).toBe('read-data');
|
|
36
|
-
});
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
describe('grantPermission', () => {
|
|
40
|
-
it('should grant a permission to a plugin', () => {
|
|
41
|
-
const permissionSet: PermissionSet = {
|
|
42
|
-
permissions: [
|
|
43
|
-
{
|
|
44
|
-
id: 'read-data',
|
|
45
|
-
resource: 'data.object',
|
|
46
|
-
actions: ['read'],
|
|
47
|
-
scope: 'plugin',
|
|
48
|
-
description: 'Read object data',
|
|
49
|
-
required: true,
|
|
50
|
-
},
|
|
51
|
-
],
|
|
52
|
-
defaultGrant: 'prompt',
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
manager.registerPermissions('test-plugin', permissionSet);
|
|
56
|
-
manager.grantPermission('test-plugin', 'read-data');
|
|
57
|
-
|
|
58
|
-
expect(manager.hasPermission('test-plugin', 'read-data')).toBe(true);
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
it('should throw error for non-existent permission', () => {
|
|
62
|
-
const permissionSet: PermissionSet = {
|
|
63
|
-
permissions: [],
|
|
64
|
-
defaultGrant: 'prompt',
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
manager.registerPermissions('test-plugin', permissionSet);
|
|
68
|
-
|
|
69
|
-
expect(() => {
|
|
70
|
-
manager.grantPermission('test-plugin', 'invalid-permission');
|
|
71
|
-
}).toThrow();
|
|
72
|
-
});
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
describe('revokePermission', () => {
|
|
76
|
-
it('should revoke a permission from a plugin', () => {
|
|
77
|
-
const permissionSet: PermissionSet = {
|
|
78
|
-
permissions: [
|
|
79
|
-
{
|
|
80
|
-
id: 'read-data',
|
|
81
|
-
resource: 'data.object',
|
|
82
|
-
actions: ['read'],
|
|
83
|
-
scope: 'plugin',
|
|
84
|
-
description: 'Read object data',
|
|
85
|
-
required: true,
|
|
86
|
-
},
|
|
87
|
-
],
|
|
88
|
-
defaultGrant: 'prompt',
|
|
89
|
-
};
|
|
90
|
-
|
|
91
|
-
manager.registerPermissions('test-plugin', permissionSet);
|
|
92
|
-
manager.grantPermission('test-plugin', 'read-data');
|
|
93
|
-
manager.revokePermission('test-plugin', 'read-data');
|
|
94
|
-
|
|
95
|
-
expect(manager.hasPermission('test-plugin', 'read-data')).toBe(false);
|
|
96
|
-
});
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
describe('checkAccess', () => {
|
|
100
|
-
it('should allow access when permission is granted', () => {
|
|
101
|
-
const permissionSet: PermissionSet = {
|
|
102
|
-
permissions: [
|
|
103
|
-
{
|
|
104
|
-
id: 'read-data',
|
|
105
|
-
resource: 'data.object',
|
|
106
|
-
actions: ['read'],
|
|
107
|
-
scope: 'plugin',
|
|
108
|
-
description: 'Read object data',
|
|
109
|
-
required: true,
|
|
110
|
-
},
|
|
111
|
-
],
|
|
112
|
-
defaultGrant: 'prompt',
|
|
113
|
-
};
|
|
114
|
-
|
|
115
|
-
manager.registerPermissions('test-plugin', permissionSet);
|
|
116
|
-
manager.grantPermission('test-plugin', 'read-data');
|
|
117
|
-
|
|
118
|
-
const result = manager.checkAccess('test-plugin', 'data.object', 'read');
|
|
119
|
-
expect(result.allowed).toBe(true);
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
it('should deny access when permission is not granted', () => {
|
|
123
|
-
const permissionSet: PermissionSet = {
|
|
124
|
-
permissions: [
|
|
125
|
-
{
|
|
126
|
-
id: 'read-data',
|
|
127
|
-
resource: 'data.object',
|
|
128
|
-
actions: ['read'],
|
|
129
|
-
scope: 'plugin',
|
|
130
|
-
description: 'Read object data',
|
|
131
|
-
required: true,
|
|
132
|
-
},
|
|
133
|
-
],
|
|
134
|
-
defaultGrant: 'prompt',
|
|
135
|
-
};
|
|
136
|
-
|
|
137
|
-
manager.registerPermissions('test-plugin', permissionSet);
|
|
138
|
-
|
|
139
|
-
const result = manager.checkAccess('test-plugin', 'data.object', 'read');
|
|
140
|
-
expect(result.allowed).toBe(false);
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
it('should deny access when permission does not exist', () => {
|
|
144
|
-
const permissionSet: PermissionSet = {
|
|
145
|
-
permissions: [],
|
|
146
|
-
defaultGrant: 'prompt',
|
|
147
|
-
};
|
|
148
|
-
|
|
149
|
-
manager.registerPermissions('test-plugin', permissionSet);
|
|
150
|
-
|
|
151
|
-
const result = manager.checkAccess('test-plugin', 'data.object', 'read');
|
|
152
|
-
expect(result.allowed).toBe(false);
|
|
153
|
-
});
|
|
154
|
-
});
|
|
155
|
-
|
|
156
|
-
describe('getMissingPermissions', () => {
|
|
157
|
-
it('should return missing required permissions', () => {
|
|
158
|
-
const permissionSet: PermissionSet = {
|
|
159
|
-
permissions: [
|
|
160
|
-
{
|
|
161
|
-
id: 'read-data',
|
|
162
|
-
resource: 'data.object',
|
|
163
|
-
actions: ['read'],
|
|
164
|
-
scope: 'plugin',
|
|
165
|
-
description: 'Read object data',
|
|
166
|
-
required: true,
|
|
167
|
-
},
|
|
168
|
-
{
|
|
169
|
-
id: 'write-data',
|
|
170
|
-
resource: 'data.object',
|
|
171
|
-
actions: ['create', 'update'],
|
|
172
|
-
scope: 'plugin',
|
|
173
|
-
description: 'Write object data',
|
|
174
|
-
required: true,
|
|
175
|
-
},
|
|
176
|
-
],
|
|
177
|
-
defaultGrant: 'prompt',
|
|
178
|
-
};
|
|
179
|
-
|
|
180
|
-
manager.registerPermissions('test-plugin', permissionSet);
|
|
181
|
-
manager.grantPermission('test-plugin', 'read-data');
|
|
182
|
-
|
|
183
|
-
const missing = manager.getMissingPermissions('test-plugin');
|
|
184
|
-
expect(missing).toHaveLength(1);
|
|
185
|
-
expect(missing[0].id).toBe('write-data');
|
|
186
|
-
});
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
describe('hasAllRequiredPermissions', () => {
|
|
190
|
-
it('should return true when all required permissions are granted', () => {
|
|
191
|
-
const permissionSet: PermissionSet = {
|
|
192
|
-
permissions: [
|
|
193
|
-
{
|
|
194
|
-
id: 'read-data',
|
|
195
|
-
resource: 'data.object',
|
|
196
|
-
actions: ['read'],
|
|
197
|
-
scope: 'plugin',
|
|
198
|
-
description: 'Read object data',
|
|
199
|
-
required: true,
|
|
200
|
-
},
|
|
201
|
-
],
|
|
202
|
-
defaultGrant: 'prompt',
|
|
203
|
-
};
|
|
204
|
-
|
|
205
|
-
manager.registerPermissions('test-plugin', permissionSet);
|
|
206
|
-
manager.grantPermission('test-plugin', 'read-data');
|
|
207
|
-
|
|
208
|
-
expect(manager.hasAllRequiredPermissions('test-plugin')).toBe(true);
|
|
209
|
-
});
|
|
210
|
-
|
|
211
|
-
it('should return false when required permissions are missing', () => {
|
|
212
|
-
const permissionSet: PermissionSet = {
|
|
213
|
-
permissions: [
|
|
214
|
-
{
|
|
215
|
-
id: 'read-data',
|
|
216
|
-
resource: 'data.object',
|
|
217
|
-
actions: ['read'],
|
|
218
|
-
scope: 'plugin',
|
|
219
|
-
description: 'Read object data',
|
|
220
|
-
required: true,
|
|
221
|
-
},
|
|
222
|
-
],
|
|
223
|
-
defaultGrant: 'prompt',
|
|
224
|
-
};
|
|
225
|
-
|
|
226
|
-
manager.registerPermissions('test-plugin', permissionSet);
|
|
227
|
-
|
|
228
|
-
expect(manager.hasAllRequiredPermissions('test-plugin')).toBe(false);
|
|
229
|
-
});
|
|
230
|
-
});
|
|
231
|
-
|
|
232
|
-
describe('clearPluginPermissions', () => {
|
|
233
|
-
it('should clear all permissions for a plugin', () => {
|
|
234
|
-
const permissionSet: PermissionSet = {
|
|
235
|
-
permissions: [
|
|
236
|
-
{
|
|
237
|
-
id: 'read-data',
|
|
238
|
-
resource: 'data.object',
|
|
239
|
-
actions: ['read'],
|
|
240
|
-
scope: 'plugin',
|
|
241
|
-
description: 'Read object data',
|
|
242
|
-
required: true,
|
|
243
|
-
},
|
|
244
|
-
],
|
|
245
|
-
defaultGrant: 'prompt',
|
|
246
|
-
};
|
|
247
|
-
|
|
248
|
-
manager.registerPermissions('test-plugin', permissionSet);
|
|
249
|
-
manager.grantPermission('test-plugin', 'read-data');
|
|
250
|
-
manager.clearPluginPermissions('test-plugin');
|
|
251
|
-
|
|
252
|
-
expect(manager.getPluginPermissions('test-plugin')).toHaveLength(0);
|
|
253
|
-
expect(manager.getGrantedPermissions('test-plugin')).toHaveLength(0);
|
|
254
|
-
});
|
|
255
|
-
});
|
|
256
|
-
});
|